Move bidi code to fx_bidi

This CL moves the BIDI code from fx_arabic to fx_bidi and conditionally
compiles based on XFA.

Change-Id: Iaba60486f03e48f0816d60e365a58a8622bc8254
Reviewed-on: https://pdfium-review.googlesource.com/12713
Commit-Queue: dsinclair <dsinclair@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/fx_arabic.cpp b/core/fxcrt/fx_arabic.cpp
index a4a65e2..d9804ba 100644
--- a/core/fxcrt/fx_arabic.cpp
+++ b/core/fxcrt/fx_arabic.cpp
@@ -9,12 +9,10 @@
 #include <algorithm>
 #include <vector>
 
-#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_ucd.h"
 #include "third_party/base/stl_util.h"
 
-#define FX_BIDIMAXLEVEL 61
-
 namespace {
 
 struct FX_ARBFORMTABLE {
@@ -34,72 +32,6 @@
   uint16_t wIsolated;
 };
 
-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 FX_ARBFORMTABLE g_FX_ArabicFormTables[] = {
     {0xFE81, 0xFE82, 0xFE81, 0xFE82}, {0xFE83, 0xFE84, 0xFE83, 0xFE84},
     {0xFE85, 0xFE86, 0xFE85, 0xFE86}, {0xFE87, 0xFE88, 0xFE87, 0xFE88},
@@ -205,122 +137,6 @@
     {0x064F, 0xFC61}, {0x0650, 0xFC62},
 };
 
-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},
-};
-
 const FX_ARBFORMTABLE* GetArabicFormTable(wchar_t unicode) {
   if (unicode < 0x622 || unicode > 0x6d5)
     return nullptr;
@@ -355,314 +171,6 @@
   return alef;
 }
 
-class CFX_BidiLine {
- public:
-  void BidiLine(std::vector<CFX_Char>* chars, int32_t iCount) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    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,
-                     int32_t iStart,
-                     int32_t iCount) {
-    ASSERT(pdfium::IndexInBounds(*chars, iStart));
-    ASSERT(iCount >= 0 &&
-           iStart + iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    std::reverse(chars->begin() + iStart, chars->begin() + iStart + iCount);
-  }
-
-  void SetDeferredRun(std::vector<CFX_Char>* chars,
-                      bool bClass,
-                      int32_t iStart,
-                      int32_t iCount,
-                      int32_t iValue) {
-    ASSERT(iStart >= 0 && iStart <= pdfium::CollectionSize<int32_t>(*chars));
-    ASSERT(iStart - iCount > -1);
-    int32_t iLast = iStart - iCount;
-    if (bClass) {
-      for (int32_t i = iStart - 1; i >= iLast; i--)
-        (*chars)[i].m_iBidiClass = static_cast<int16_t>(iValue);
-      return;
-    }
-
-    for (int32_t i = iStart - 1; i >= iLast; i--)
-      (*chars)[i].m_iBidiLevel = static_cast<int16_t>(iValue);
-  }
-
-  void Classify(std::vector<CFX_Char>* chars, int32_t iCount, bool bWS) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    if (bWS) {
-      for (int32_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 (int32_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, int32_t iCount) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    for (int32_t i = 0; i < iCount; i++)
-      (*chars)[i].m_iBidiLevel = 0;
-  }
-
-  void ResolveWeak(std::vector<CFX_Char>* chars, int32_t iCount) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    iCount--;
-    if (iCount < 1)
-      return;
-
-    int32_t iLevelCur = 0;
-    int32_t iState = FX_BWSxl;
-    int32_t i = 0;
-    int32_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) {
-      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, int32_t iCount) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    iCount--;
-    if (iCount < 1)
-      return;
-
-    CFX_Char* pTC;
-    int32_t iLevel = 0;
-    int32_t iState = FX_BNSl;
-    int32_t i = 0;
-    int32_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) {
-      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, int32_t iCount) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    for (int32_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, int32_t iCount) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    if (iCount < 1)
-      return;
-
-    iCount--;
-    int32_t iLevel = 0;
-    int32_t i = 0;
-    int32_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 = (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);
-  }
-
-  int32_t ReorderLevel(std::vector<CFX_Char>* chars,
-                       int32_t iCount,
-                       int32_t iBaseLevel,
-                       int32_t iStart,
-                       bool bReverse) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    ASSERT(iBaseLevel >= 0 && iBaseLevel <= FX_BIDIMAXLEVEL);
-    ASSERT(iStart >= 0 && iStart < iCount);
-
-    if (iCount < 1)
-      return 0;
-
-    bReverse = bReverse || FX_IsOdd(iBaseLevel);
-    int32_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;
-    }
-    int32_t iNum = i - iStart;
-    if (bReverse && iNum > 1)
-      ReverseString(chars, iStart, iNum);
-
-    return iNum;
-  }
-
-  void Reorder(std::vector<CFX_Char>* chars, int32_t iCount) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    int32_t i = 0;
-    while (i < iCount)
-      i += ReorderLevel(chars, iCount, 0, i, false);
-  }
-
-  void Position(std::vector<CFX_Char>* chars, int32_t iCount) {
-    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
-    for (int32_t i = 0; i < iCount; ++i)
-      (*chars)[(*chars)[i].m_iBidiPos].m_iBidiOrder = i;
-  }
-};
-
 }  // namespace
 
 namespace pdfium {
@@ -722,8 +230,3 @@
   }
   return shadda;
 }
-
-void FX_BidiLine(std::vector<CFX_Char>* chars, int32_t iCount) {
-  CFX_BidiLine blt;
-  blt.BidiLine(chars, iCount);
-}
diff --git a/core/fxcrt/fx_arabic.h b/core/fxcrt/fx_arabic.h
index 32021c3..9d23b15 100644
--- a/core/fxcrt/fx_arabic.h
+++ b/core/fxcrt/fx_arabic.h
@@ -24,6 +24,5 @@
 }  // namespace pdfium
 
 wchar_t FX_GetArabicFromShaddaTable(wchar_t shadda);
-void FX_BidiLine(std::vector<CFX_Char>* chars, int32_t iCount);
 
 #endif  // CORE_FXCRT_FX_ARABIC_H_
diff --git a/core/fxcrt/fx_bidi.cpp b/core/fxcrt/fx_bidi.cpp
index 2554454..8b20cfb 100644
--- a/core/fxcrt/fx_bidi.cpp
+++ b/core/fxcrt/fx_bidi.cpp
@@ -11,6 +11,509 @@
 #include "core/fxcrt/fx_ucd.h"
 #include "third_party/base/ptr_util.h"
 
+#ifdef PDF_ENABLE_XFA
+#include "core/fxcrt/fx_extension.h"
+
+namespace {
+
+#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, int32_t iCount) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    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,
+                     int32_t iStart,
+                     int32_t iCount) {
+    ASSERT(pdfium::IndexInBounds(*chars, iStart));
+    ASSERT(iCount >= 0 &&
+           iStart + iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    std::reverse(chars->begin() + iStart, chars->begin() + iStart + iCount);
+  }
+
+  void SetDeferredRun(std::vector<CFX_Char>* chars,
+                      bool bClass,
+                      int32_t iStart,
+                      int32_t iCount,
+                      int32_t iValue) {
+    ASSERT(iStart >= 0 && iStart <= pdfium::CollectionSize<int32_t>(*chars));
+    ASSERT(iStart - iCount > -1);
+    int32_t iLast = iStart - iCount;
+    if (bClass) {
+      for (int32_t i = iStart - 1; i >= iLast; i--)
+        (*chars)[i].m_iBidiClass = static_cast<int16_t>(iValue);
+      return;
+    }
+
+    for (int32_t i = iStart - 1; i >= iLast; i--)
+      (*chars)[i].m_iBidiLevel = static_cast<int16_t>(iValue);
+  }
+
+  void Classify(std::vector<CFX_Char>* chars, int32_t iCount, bool bWS) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    if (bWS) {
+      for (int32_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 (int32_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, int32_t iCount) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    for (int32_t i = 0; i < iCount; i++)
+      (*chars)[i].m_iBidiLevel = 0;
+  }
+
+  void ResolveWeak(std::vector<CFX_Char>* chars, int32_t iCount) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    iCount--;
+    if (iCount < 1)
+      return;
+
+    int32_t iLevelCur = 0;
+    int32_t iState = FX_BWSxl;
+    int32_t i = 0;
+    int32_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) {
+      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, int32_t iCount) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    iCount--;
+    if (iCount < 1)
+      return;
+
+    CFX_Char* pTC;
+    int32_t iLevel = 0;
+    int32_t iState = FX_BNSl;
+    int32_t i = 0;
+    int32_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) {
+      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, int32_t iCount) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    for (int32_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, int32_t iCount) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    if (iCount < 1)
+      return;
+
+    iCount--;
+    int32_t iLevel = 0;
+    int32_t i = 0;
+    int32_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 = (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);
+  }
+
+  int32_t ReorderLevel(std::vector<CFX_Char>* chars,
+                       int32_t iCount,
+                       int32_t iBaseLevel,
+                       int32_t iStart,
+                       bool bReverse) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    ASSERT(iBaseLevel >= 0 && iBaseLevel <= kBidiMaxLevel);
+    ASSERT(iStart >= 0 && iStart < iCount);
+
+    if (iCount < 1)
+      return 0;
+
+    bReverse = bReverse || FX_IsOdd(iBaseLevel);
+    int32_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;
+    }
+    int32_t iNum = i - iStart;
+    if (bReverse && iNum > 1)
+      ReverseString(chars, iStart, iNum);
+
+    return iNum;
+  }
+
+  void Reorder(std::vector<CFX_Char>* chars, int32_t iCount) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    int32_t i = 0;
+    while (i < iCount)
+      i += ReorderLevel(chars, iCount, 0, i, false);
+  }
+
+  void Position(std::vector<CFX_Char>* chars, int32_t iCount) {
+    ASSERT(iCount >= 0 && iCount <= pdfium::CollectionSize<int32_t>(*chars));
+    for (int32_t i = 0; i < iCount; ++i)
+      (*chars)[(*chars)[i].m_iBidiPos].m_iBidiOrder = i;
+  }
+};
+
+}  // namespace
+
+#endif  // PDF_ENABLE_XFA
+
 CFX_BidiChar::CFX_BidiChar()
     : m_CurrentSegment({0, 0, NEUTRAL}), m_LastSegment({0, 0, NEUTRAL}) {}
 
@@ -83,3 +586,10 @@
     m_eOverallDirection = CFX_BidiChar::RIGHT;
   }
 }
+
+#ifdef PDF_ENABLE_XFA
+void FX_BidiLine(std::vector<CFX_Char>* chars, int32_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 d5e0bd3..0dc426b 100644
--- a/core/fxcrt/fx_bidi.h
+++ b/core/fxcrt/fx_bidi.h
@@ -13,6 +13,10 @@
 #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:
@@ -72,4 +76,8 @@
   CFX_BidiChar::Direction m_eOverallDirection;
 };
 
+#if PDF_ENABLE_XFA
+void FX_BidiLine(std::vector<CFX_Char>* chars, int32_t iCount);
+#endif  // PDF_ENABLE_XFA
+
 #endif  // CORE_FXCRT_FX_BIDI_H_
diff --git a/xfa/fgas/layout/cfx_rtfbreak.cpp b/xfa/fgas/layout/cfx_rtfbreak.cpp
index 45acae5..a097361 100644
--- a/xfa/fgas/layout/cfx_rtfbreak.cpp
+++ b/xfa/fgas/layout/cfx_rtfbreak.cpp
@@ -9,6 +9,7 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_arabic.h"
+#include "core/fxcrt/fx_bidi.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fgas/layout/cfx_linebreak.h"
diff --git a/xfa/fgas/layout/cfx_txtbreak.cpp b/xfa/fgas/layout/cfx_txtbreak.cpp
index 42365af..3ba7640 100644
--- a/xfa/fgas/layout/cfx_txtbreak.cpp
+++ b/xfa/fgas/layout/cfx_txtbreak.cpp
@@ -9,6 +9,7 @@
 #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"