Dis-entangle cfx_char.h from fx_bidi.h.

The one function in fx_bidi.h that references cfx_chars becomes a
static method in cfx_char.h. Then all of the other XFA-only code
moves to cfx_char.cpp, which is appropriate since it is twiddling
vectors of CFX_Char anyways. Some ifdef XFAs go away in the process.

Change-Id: I70c21be4865ceaa35852df77017f47cf1c58f440
Reviewed-on: https://pdfium-review.googlesource.com/c/48230
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcrt/cfx_char.cpp b/core/fxcrt/cfx_char.cpp
index 15fe47d..02d051f 100644
--- a/core/fxcrt/cfx_char.cpp
+++ b/core/fxcrt/cfx_char.cpp
@@ -6,6 +6,547 @@
 
 #include "core/fxcrt/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,
diff --git a/core/fxcrt/cfx_char.h b/core/fxcrt/cfx_char.h
index 5a5268f..e6c6978 100644
--- a/core/fxcrt/cfx_char.h
+++ b/core/fxcrt/cfx_char.h
@@ -9,6 +9,8 @@
 
 #include <stdint.h>
 
+#include <vector>
+
 #include "core/fxcrt/fx_linebreak.h"
 #include "core/fxcrt/fx_unicode.h"
 #include "core/fxcrt/retain_ptr.h"
@@ -17,6 +19,8 @@
 
 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,
diff --git a/core/fxcrt/fx_bidi.cpp b/core/fxcrt/fx_bidi.cpp
index 9fee05d..c5fb5c6 100644
--- a/core/fxcrt/fx_bidi.cpp
+++ b/core/fxcrt/fx_bidi.cpp
@@ -11,549 +11,6 @@
 #include "core/fxcrt/fx_unicode.h"
 #include "third_party/base/stl_util.h"
 
-#ifdef PDF_ENABLE_XFA
-#include "core/fxcrt/fx_extension.h"
-#endif  // PDF_ENABLE_XFA
-
-namespace {
-
-#ifdef PDF_ENABLE_XFA
-
-#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;
-  }
-}
-
-void 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);
-}
-#endif  // PDF_ENABLE_XFA
-
-}  // namespace
-
 CFX_BidiChar::CFX_BidiChar()
     : m_CurrentSegment({0, 0, NEUTRAL}), m_LastSegment({0, 0, NEUTRAL}) {}
 
@@ -630,9 +87,3 @@
     m_eOverallDirection = CFX_BidiChar::RIGHT;
   }
 }
-
-#ifdef PDF_ENABLE_XFA
-void FX_BidiLine(std::vector<CFX_Char>* chars, size_t iCount) {
-  BidiLine(chars, iCount);
-}
-#endif  // PDF_ENABLE_XFA
diff --git a/core/fxcrt/fx_bidi.h b/core/fxcrt/fx_bidi.h
index f64fc67..ade9a2a 100644
--- a/core/fxcrt/fx_bidi.h
+++ b/core/fxcrt/fx_bidi.h
@@ -12,10 +12,6 @@
 #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:
@@ -71,8 +67,4 @@
   CFX_BidiChar::Direction m_eOverallDirection = CFX_BidiChar::LEFT;
 };
 
-#ifdef 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/testing/fuzzers/pdf_bidi_fuzzer.cc b/testing/fuzzers/pdf_bidi_fuzzer.cc
index fb3209c..276e3df 100644
--- a/testing/fuzzers/pdf_bidi_fuzzer.cc
+++ b/testing/fuzzers/pdf_bidi_fuzzer.cc
@@ -4,7 +4,7 @@
 
 #include <cstdint>
 
-#include "core/fxcrt/fx_bidi.h"
+#include "core/fxcrt/cfx_char.h"
 #include "core/fxcrt/widestring.h"
 #include "core/fxge/cfx_font.h"
 #include "third_party/base/ptr_util.h"
@@ -31,6 +31,6 @@
     rtf_break.AppendChar(ch);
 
   auto chars = rtf_break.GetCurrentLineForTesting()->m_LineChars;
-  FX_BidiLine(&chars, chars.size());
+  CFX_Char::BidiLine(&chars, chars.size());
   return 0;
 }
diff --git a/xfa/fgas/layout/cfx_rtfbreak.cpp b/xfa/fgas/layout/cfx_rtfbreak.cpp
index 61fb9d9..40f73fb 100644
--- a/xfa/fgas/layout/cfx_rtfbreak.cpp
+++ b/xfa/fgas/layout/cfx_rtfbreak.cpp
@@ -8,8 +8,8 @@
 
 #include <algorithm>
 
+#include "core/fxcrt/cfx_char.h"
 #include "core/fxcrt/fx_arabic.h"
-#include "core/fxcrt/fx_bidi.h"
 #include "core/fxcrt/fx_linebreak.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "third_party/base/numerics/safe_math.h"
@@ -427,7 +427,7 @@
       if (i == 0)
         pTC->m_iBidiLevel = 1;
     }
-    FX_BidiLine(&chars, iBidiNum + 1);
+    CFX_Char::BidiLine(&chars, iBidiNum + 1);
   } else {
     for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
       pTC = &chars[i];
diff --git a/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp b/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp
index e7146c0..307fc8c 100644
--- a/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp
+++ b/xfa/fgas/layout/cfx_rtfbreak_unittest.cpp
@@ -9,7 +9,7 @@
 #include <memory>
 #include <utility>
 
-#include "core/fxcrt/fx_bidi.h"
+#include "core/fxcrt/cfx_char.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -86,6 +86,6 @@
     rtf_break->AppendChar(ch);
 
   auto chars = rtf_break->GetCurrentLineForTesting()->m_LineChars;
-  FX_BidiLine(&chars, chars.size());
+  CFX_Char::BidiLine(&chars, chars.size());
   EXPECT_EQ(3u, chars.size());
 }
diff --git a/xfa/fgas/layout/cfx_txtbreak.cpp b/xfa/fgas/layout/cfx_txtbreak.cpp
index d5a6cb4..f2a3f67 100644
--- a/xfa/fgas/layout/cfx_txtbreak.cpp
+++ b/xfa/fgas/layout/cfx_txtbreak.cpp
@@ -8,8 +8,8 @@
 
 #include <algorithm>
 
+#include "core/fxcrt/cfx_char.h"
 #include "core/fxcrt/fx_arabic.h"
-#include "core/fxcrt/fx_bidi.h"
 #include "core/fxcrt/fx_linebreak.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fde/cfde_texteditengine.h"
@@ -323,7 +323,7 @@
       if (i == 0)
         pTC->m_iBidiLevel = 1;
     }
-    FX_BidiLine(&chars, iBidiNum + 1);
+    CFX_Char::BidiLine(&chars, iBidiNum + 1);
   }
 
   if (bDone) {
diff --git a/xfa/fgas/layout/cfx_txtbreak_unittest.cpp b/xfa/fgas/layout/cfx_txtbreak_unittest.cpp
index fcfc1d9..4b71df3 100644
--- a/xfa/fgas/layout/cfx_txtbreak_unittest.cpp
+++ b/xfa/fgas/layout/cfx_txtbreak_unittest.cpp
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "core/fxcrt/fx_bidi.h"
+#include "core/fxcrt/cfx_char.h"
 #include "core/fxge/cfx_font.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
@@ -43,6 +43,6 @@
     txt_break->AppendChar(ch);
 
   auto chars = txt_break->GetCurrentLineForTesting()->m_LineChars;
-  FX_BidiLine(&chars, chars.size());
+  CFX_Char::BidiLine(&chars, chars.size());
   EXPECT_EQ(3u, chars.size());
 }