Cleanup some numeric code.
[pdfium.git] / core / src / fpdfapi / fpdf_parser / fpdf_parser_parser.cpp
index e9c07e2..fa114f9 100644 (file)
@@ -4,15 +4,82 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+#include <set>
 #include <utility>
 #include <vector>
 
+#include "../../../../third_party/base/nonstd_unique_ptr.h"
+#include "../../../../third_party/base/stl_util.h"
 #include "../../../include/fpdfapi/fpdf_module.h"
 #include "../../../include/fpdfapi/fpdf_page.h"
 #include "../../../include/fpdfapi/fpdf_parser.h"
+#include "../../../include/fxcrt/fx_ext.h"
 #include "../../../include/fxcrt/fx_safe_types.h"
 #include "../fpdf_page/pageint.h"
 
+namespace {
+
+struct SearchTagRecord {
+  const uint8_t* m_pTag;
+  FX_DWORD m_Len;
+  FX_DWORD m_Offset;
+};
+
+int CompareFileSize(const void* p1, const void* p2) {
+  return *(FX_FILESIZE*)p1 - *(FX_FILESIZE*)p2;
+}
+
+int32_t GetHeaderOffset(IFX_FileRead* pFile) {
+  const FX_DWORD tag = FXDWORD_FROM_LSBFIRST(0x46445025);
+  const size_t kBufSize = 4;
+  uint8_t buf[kBufSize];
+  int32_t offset = 0;
+  while (offset <= 1024) {
+    if (!pFile->ReadBlock(buf, offset, kBufSize))
+      return -1;
+
+    if (*(FX_DWORD*)buf == tag)
+      return offset;
+
+    ++offset;
+  }
+  return -1;
+}
+
+int32_t GetDirectInteger(CPDF_Dictionary* pDict, const CFX_ByteStringC& key) {
+  CPDF_Number* pObj = ToNumber(pDict->GetElement(key));
+  return pObj ? pObj->GetInteger() : 0;
+}
+
+bool CheckDirectType(CPDF_Dictionary* pDict,
+                     const CFX_ByteStringC& key,
+                     int32_t iType) {
+  CPDF_Object* pObj = pDict->GetElement(key);
+  return !pObj || pObj->GetType() == iType;
+}
+
+FX_DWORD GetVarInt(const uint8_t* p, int32_t n) {
+  FX_DWORD result = 0;
+  for (int32_t i = 0; i < n; ++i)
+    result = result * 256 + p[i];
+  return result;
+}
+
+int32_t GetStreamNCount(CPDF_StreamAcc* pObjStream) {
+  return pObjStream->GetDict()->GetInteger(FX_BSTRC("N"));
+}
+
+int32_t GetStreamFirst(CPDF_StreamAcc* pObjStream) {
+  return pObjStream->GetDict()->GetInteger(FX_BSTRC("First"));
+}
+
+}  // namespace
+
+// TODO(thestig) Using unique_ptr with ReleaseDeleter is still not ideal.
+// Come up or wait for something better.
+using ScopedFileStream =
+    nonstd::unique_ptr<IFX_FileStream, ReleaseDeleter<IFX_FileStream>>;
+
 FX_BOOL IsSignatureDict(const CPDF_Dictionary* pDict) {
   CPDF_Object* pType = pDict->GetElementValue(FX_BSTRC("Type"));
   if (!pType) {
@@ -26,22 +93,11 @@ FX_BOOL IsSignatureDict(const CPDF_Dictionary* pDict) {
   }
   return FALSE;
 }
-static int _CompareFileSize(const void* p1, const void* p2) {
-  FX_FILESIZE ret = (*(FX_FILESIZE*)p1) - (*(FX_FILESIZE*)p2);
-  if (ret > 0) {
-    return 1;
-  }
-  if (ret < 0) {
-    return -1;
-  }
-  return 0;
-}
 
 CPDF_Parser::CPDF_Parser() {
   m_pDocument = NULL;
   m_pTrailer = NULL;
   m_pEncryptDict = NULL;
-  m_pSecurityHandler = NULL;
   m_pLinearized = NULL;
   m_dwFirstPageNo = 0;
   m_dwXrefStartObjNum = 0;
@@ -98,24 +154,6 @@ void CPDF_Parser::CloseParser(FX_BOOL bReParse) {
     m_pLinearized = NULL;
   }
 }
-static int32_t GetHeaderOffset(IFX_FileRead* pFile) {
-  FX_DWORD tag = FXDWORD_FROM_LSBFIRST(0x46445025);
-  uint8_t buf[4];
-  int32_t offset = 0;
-  while (1) {
-    if (!pFile->ReadBlock(buf, offset, 4)) {
-      return -1;
-    }
-    if (*(FX_DWORD*)buf == tag) {
-      return offset;
-    }
-    offset++;
-    if (offset > 1024) {
-      return -1;
-    }
-  }
-  return -1;
-}
 CPDF_SecurityHandler* FPDF_CreateStandardSecurityHandler();
 CPDF_SecurityHandler* FPDF_CreatePubKeyHandler(void*);
 FX_DWORD CPDF_Parser::StartParse(IFX_FileRead* pFileAccess,
@@ -125,108 +163,103 @@ FX_DWORD CPDF_Parser::StartParse(IFX_FileRead* pFileAccess,
   m_bXRefStream = FALSE;
   m_LastXRefOffset = 0;
   m_bOwnFileRead = bOwnFileRead;
+
   int32_t offset = GetHeaderOffset(pFileAccess);
   if (offset == -1) {
-    if (bOwnFileRead && pFileAccess) {
+    if (bOwnFileRead && pFileAccess)
       pFileAccess->Release();
-    }
     return PDFPARSE_ERROR_FORMAT;
   }
   m_Syntax.InitParser(pFileAccess, offset);
+
   uint8_t ch;
-  if (!m_Syntax.GetCharAt(5, ch)) {
+  if (!m_Syntax.GetCharAt(5, ch))
     return PDFPARSE_ERROR_FORMAT;
-  }
-  if (ch >= '0' && ch <= '9') {
+  if (std::isdigit(ch))
     m_FileVersion = (ch - '0') * 10;
-  }
-  if (!m_Syntax.GetCharAt(7, ch)) {
+
+  if (!m_Syntax.GetCharAt(7, ch))
     return PDFPARSE_ERROR_FORMAT;
-  }
-  if (ch >= '0' && ch <= '9') {
+  if (std::isdigit(ch))
     m_FileVersion += ch - '0';
-  }
-  if (m_Syntax.m_FileLen < m_Syntax.m_HeaderOffset + 9) {
+
+  if (m_Syntax.m_FileLen < m_Syntax.m_HeaderOffset + 9)
     return PDFPARSE_ERROR_FORMAT;
-  }
+
   m_Syntax.RestorePos(m_Syntax.m_FileLen - m_Syntax.m_HeaderOffset - 9);
-  if (!bReParse) {
+  if (!bReParse)
     m_pDocument = new CPDF_Document(this);
-  }
+
   FX_BOOL bXRefRebuilt = FALSE;
   if (m_Syntax.SearchWord(FX_BSTRC("startxref"), TRUE, FALSE, 4096)) {
     FX_FILESIZE startxref_offset = m_Syntax.SavePos();
     void* pResult = FXSYS_bsearch(&startxref_offset, m_SortedOffset.GetData(),
                                   m_SortedOffset.GetSize(), sizeof(FX_FILESIZE),
-                                  _CompareFileSize);
-    if (pResult == NULL) {
+                                  CompareFileSize);
+    if (!pResult)
       m_SortedOffset.Add(startxref_offset);
-    }
+
     m_Syntax.GetKeyword();
     FX_BOOL bNumber;
     CFX_ByteString xrefpos_str = m_Syntax.GetNextWord(bNumber);
-    if (!bNumber) {
+    if (!bNumber)
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     m_LastXRefOffset = (FX_FILESIZE)FXSYS_atoi64(xrefpos_str);
     if (!LoadAllCrossRefV4(m_LastXRefOffset) &&
         !LoadAllCrossRefV5(m_LastXRefOffset)) {
-      if (!RebuildCrossRef()) {
+      if (!RebuildCrossRef())
         return PDFPARSE_ERROR_FORMAT;
-      }
+
       bXRefRebuilt = TRUE;
       m_LastXRefOffset = 0;
     }
   } else {
-    if (!RebuildCrossRef()) {
+    if (!RebuildCrossRef())
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     bXRefRebuilt = TRUE;
   }
   FX_DWORD dwRet = SetEncryptHandler();
-  if (dwRet != PDFPARSE_ERROR_SUCCESS) {
+  if (dwRet != PDFPARSE_ERROR_SUCCESS)
     return dwRet;
-  }
+
   m_pDocument->LoadDoc();
-  if (m_pDocument->GetRoot() == NULL || m_pDocument->GetPageCount() == 0) {
-    if (bXRefRebuilt) {
+  if (!m_pDocument->GetRoot() || m_pDocument->GetPageCount() == 0) {
+    if (bXRefRebuilt)
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     ReleaseEncryptHandler();
-    if (!RebuildCrossRef()) {
+    if (!RebuildCrossRef())
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     dwRet = SetEncryptHandler();
-    if (dwRet != PDFPARSE_ERROR_SUCCESS) {
+    if (dwRet != PDFPARSE_ERROR_SUCCESS)
       return dwRet;
-    }
+
     m_pDocument->LoadDoc();
-    if (m_pDocument->GetRoot() == NULL) {
+    if (!m_pDocument->GetRoot())
       return PDFPARSE_ERROR_FORMAT;
-    }
   }
   FXSYS_qsort(m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
-              sizeof(FX_FILESIZE), _CompareFileSize);
+              sizeof(FX_FILESIZE), CompareFileSize);
   FX_DWORD RootObjNum = GetRootObjNum();
   if (RootObjNum == 0) {
     ReleaseEncryptHandler();
     RebuildCrossRef();
     RootObjNum = GetRootObjNum();
-    if (RootObjNum == 0) {
+    if (RootObjNum == 0)
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     dwRet = SetEncryptHandler();
-    if (dwRet != PDFPARSE_ERROR_SUCCESS) {
+    if (dwRet != PDFPARSE_ERROR_SUCCESS)
       return dwRet;
-    }
   }
   if (m_pSecurityHandler && !m_pSecurityHandler->IsMetadataEncrypted()) {
     CPDF_Reference* pMetadata =
-        (CPDF_Reference*)m_pDocument->GetRoot()->GetElement(
-            FX_BSTRC("Metadata"));
-    if (pMetadata && pMetadata->GetType() == PDFOBJ_REFERENCE) {
+        ToReference(m_pDocument->GetRoot()->GetElement(FX_BSTRC("Metadata")));
+    if (pMetadata)
       m_Syntax.m_MetadataObjnum = pMetadata->GetRefObjNum();
-    }
   }
   return PDFPARSE_ERROR_SUCCESS;
 }
@@ -238,66 +271,56 @@ FX_DWORD CPDF_Parser::SetEncryptHandler() {
   }
   CPDF_Object* pEncryptObj = m_pTrailer->GetElement(FX_BSTRC("Encrypt"));
   if (pEncryptObj) {
-    if (pEncryptObj->GetType() == PDFOBJ_DICTIONARY) {
-      SetEncryptDictionary((CPDF_Dictionary*)pEncryptObj);
-    } else if (pEncryptObj->GetType() == PDFOBJ_REFERENCE) {
-      pEncryptObj = m_pDocument->GetIndirectObject(
-          ((CPDF_Reference*)pEncryptObj)->GetRefObjNum());
-      if (pEncryptObj) {
+    if (CPDF_Dictionary* pEncryptDict = pEncryptObj->AsDictionary()) {
+      SetEncryptDictionary(pEncryptDict);
+    } else if (CPDF_Reference* pRef = pEncryptObj->AsReference()) {
+      pEncryptObj = m_pDocument->GetIndirectObject(pRef->GetRefObjNum());
+      if (pEncryptObj)
         SetEncryptDictionary(pEncryptObj->GetDict());
-      }
     }
   }
   if (m_bForceUseSecurityHandler) {
     FX_DWORD err = PDFPARSE_ERROR_HANDLER;
-    if (m_pSecurityHandler == NULL) {
+    if (!m_pSecurityHandler) {
       return PDFPARSE_ERROR_HANDLER;
     }
     if (!m_pSecurityHandler->OnInit(this, m_pEncryptDict)) {
       return err;
     }
-    CPDF_CryptoHandler* pCryptoHandler =
-        m_pSecurityHandler->CreateCryptoHandler();
-    if (!pCryptoHandler->Init(m_pEncryptDict, m_pSecurityHandler)) {
-      delete pCryptoHandler;
-      pCryptoHandler = NULL;
+    nonstd::unique_ptr<CPDF_CryptoHandler> pCryptoHandler(
+        m_pSecurityHandler->CreateCryptoHandler());
+    if (!pCryptoHandler->Init(m_pEncryptDict, m_pSecurityHandler.get())) {
       return PDFPARSE_ERROR_HANDLER;
     }
-    m_Syntax.SetEncrypt(pCryptoHandler);
+    m_Syntax.SetEncrypt(pCryptoHandler.release());
   } else if (m_pEncryptDict) {
     CFX_ByteString filter = m_pEncryptDict->GetString(FX_BSTRC("Filter"));
-    CPDF_SecurityHandler* pSecurityHandler = NULL;
+    nonstd::unique_ptr<CPDF_SecurityHandler> pSecurityHandler;
     FX_DWORD err = PDFPARSE_ERROR_HANDLER;
     if (filter == FX_BSTRC("Standard")) {
-      pSecurityHandler = FPDF_CreateStandardSecurityHandler();
+      pSecurityHandler.reset(FPDF_CreateStandardSecurityHandler());
       err = PDFPARSE_ERROR_PASSWORD;
     }
-    if (pSecurityHandler == NULL) {
+    if (!pSecurityHandler) {
       return PDFPARSE_ERROR_HANDLER;
     }
     if (!pSecurityHandler->OnInit(this, m_pEncryptDict)) {
-      delete pSecurityHandler;
-      pSecurityHandler = NULL;
       return err;
     }
-    m_pSecurityHandler = pSecurityHandler;
-    CPDF_CryptoHandler* pCryptoHandler =
-        pSecurityHandler->CreateCryptoHandler();
-    if (!pCryptoHandler->Init(m_pEncryptDict, m_pSecurityHandler)) {
-      delete pCryptoHandler;
-      pCryptoHandler = NULL;
+    m_pSecurityHandler = nonstd::move(pSecurityHandler);
+    nonstd::unique_ptr<CPDF_CryptoHandler> pCryptoHandler(
+        m_pSecurityHandler->CreateCryptoHandler());
+    if (!pCryptoHandler->Init(m_pEncryptDict, m_pSecurityHandler.get())) {
       return PDFPARSE_ERROR_HANDLER;
     }
-    m_Syntax.SetEncrypt(pCryptoHandler);
+    m_Syntax.SetEncrypt(pCryptoHandler.release());
   }
   return PDFPARSE_ERROR_SUCCESS;
 }
 void CPDF_Parser::ReleaseEncryptHandler() {
-  delete m_Syntax.m_pCryptoHandler;
-  m_Syntax.m_pCryptoHandler = NULL;
+  m_Syntax.m_pCryptoHandler.reset();
   if (!m_bForceUseSecurityHandler) {
-    delete m_pSecurityHandler;
-    m_pSecurityHandler = NULL;
+    m_pSecurityHandler.reset();
   }
 }
 FX_FILESIZE CPDF_Parser::GetObjectOffset(FX_DWORD objnum) {
@@ -312,26 +335,7 @@ FX_FILESIZE CPDF_Parser::GetObjectOffset(FX_DWORD objnum) {
   }
   return 0;
 }
-static int32_t GetDirectInteger(CPDF_Dictionary* pDict,
-                                const CFX_ByteStringC& key) {
-  CPDF_Object* pObj = pDict->GetElement(key);
-  if (pObj == NULL) {
-    return 0;
-  }
-  if (pObj->GetType() == PDFOBJ_NUMBER) {
-    return ((CPDF_Number*)pObj)->GetInteger();
-  }
-  return 0;
-}
-static FX_BOOL CheckDirectType(CPDF_Dictionary* pDict,
-                               const CFX_ByteStringC& key,
-                               int32_t iType) {
-  CPDF_Object* pObj = pDict->GetElement(key);
-  if (!pObj) {
-    return TRUE;
-  }
-  return pObj->GetType() == iType;
-}
+
 FX_BOOL CPDF_Parser::LoadAllCrossRefV4(FX_FILESIZE xrefpos) {
   if (!LoadCrossRefV4(xrefpos, 0, TRUE, FALSE)) {
     return FALSE;
@@ -360,27 +364,26 @@ FX_BOOL CPDF_Parser::LoadAllCrossRefV4(FX_FILESIZE xrefpos) {
   while (xrefpos) {
     CrossRefList.InsertAt(0, xrefpos);
     LoadCrossRefV4(xrefpos, 0, TRUE, FALSE);
-    CPDF_Dictionary* pDict = LoadTrailerV4();
-    if (pDict == NULL) {
+    nonstd::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>> pDict(
+        LoadTrailerV4());
+    if (!pDict)
       return FALSE;
-    }
-    if (!CheckDirectType(pDict, FX_BSTRC("Prev"), PDFOBJ_NUMBER)) {
-      pDict->Release();
+
+    if (!CheckDirectType(pDict.get(), FX_BSTRC("Prev"), PDFOBJ_NUMBER))
       return FALSE;
-    }
-    newxrefpos = GetDirectInteger(pDict, FX_BSTRC("Prev"));
-    if (newxrefpos == xrefpos) {
-      pDict->Release();
+
+    newxrefpos = GetDirectInteger(pDict.get(), FX_BSTRC("Prev"));
+    if (newxrefpos == xrefpos)
       return FALSE;
-    }
+
     xrefpos = newxrefpos;
     XRefStreamList.InsertAt(0, pDict->GetInteger(FX_BSTRC("XRefStm")));
-    m_Trailers.Add(pDict);
+    m_Trailers.Add(pDict.release());
   }
-  for (int32_t i = 0; i < CrossRefList.GetSize(); i++)
-    if (!LoadCrossRefV4(CrossRefList[i], XRefStreamList[i], FALSE, i == 0)) {
+  for (int32_t i = 0; i < CrossRefList.GetSize(); i++) {
+    if (!LoadCrossRefV4(CrossRefList[i], XRefStreamList[i], FALSE, i == 0))
       return FALSE;
-    }
+  }
   return TRUE;
 }
 FX_BOOL CPDF_Parser::LoadLinearizedAllCrossRefV4(FX_FILESIZE xrefpos,
@@ -423,26 +426,25 @@ FX_BOOL CPDF_Parser::LoadLinearizedCrossRefV4(FX_FILESIZE pos,
   m_Syntax.RestorePos(dwStartPos);
   void* pResult =
       FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
-                    sizeof(FX_FILESIZE), _CompareFileSize);
+                    sizeof(FX_FILESIZE), CompareFileSize);
   if (pResult == NULL) {
     m_SortedOffset.Add(pos);
   }
   FX_DWORD start_objnum = 0;
   FX_DWORD count = dwObjCount;
   FX_FILESIZE SavedPos = m_Syntax.SavePos();
-  int32_t recordsize = 20;
-  char* pBuf = FX_Alloc(char, 1024 * recordsize + 1);
+  const int32_t recordsize = 20;
+  std::vector<char> buf(1024 * recordsize + 1);
+  char* pBuf = pdfium::vector_as_array(&buf);
   pBuf[1024 * recordsize] = '\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;
     FX_DWORD dwReadSize = block_size * recordsize;
     if ((FX_FILESIZE)(dwStartPos + dwReadSize) > m_Syntax.m_FileLen) {
-      FX_Free(pBuf);
       return FALSE;
     }
-    if (!m_Syntax.ReadBlock((uint8_t*)pBuf, dwReadSize)) {
-      FX_Free(pBuf);
+    if (!m_Syntax.ReadBlock(reinterpret_cast<uint8_t*>(pBuf), dwReadSize)) {
       return FALSE;
     }
     for (int32_t i = 0; i < block_size; i++) {
@@ -455,10 +457,8 @@ FX_BOOL CPDF_Parser::LoadLinearizedCrossRefV4(FX_FILESIZE pos,
         int32_t offset = FXSYS_atoi(pEntry);
         if (offset == 0) {
           for (int32_t c = 0; c < 10; c++) {
-            if (pEntry[c] < '0' || pEntry[c] > '9') {
-              FX_Free(pBuf);
+            if (!std::isdigit(pEntry[c]))
               return FALSE;
-            }
           }
         }
         m_CrossRef.SetAtGrow(objnum, offset);
@@ -470,7 +470,7 @@ FX_BOOL CPDF_Parser::LoadLinearizedCrossRefV4(FX_FILESIZE pos,
         if (m_CrossRef[objnum] < m_Syntax.m_FileLen) {
           void* pResult = FXSYS_bsearch(
               &m_CrossRef[objnum], m_SortedOffset.GetData(),
-              m_SortedOffset.GetSize(), sizeof(FX_FILESIZE), _CompareFileSize);
+              m_SortedOffset.GetSize(), sizeof(FX_FILESIZE), CompareFileSize);
           if (pResult == NULL) {
             m_SortedOffset.Add(m_CrossRef[objnum]);
           }
@@ -479,64 +479,62 @@ FX_BOOL CPDF_Parser::LoadLinearizedCrossRefV4(FX_FILESIZE pos,
       }
     }
   }
-  FX_Free(pBuf);
   m_Syntax.RestorePos(SavedPos + count * recordsize);
   return TRUE;
 }
-FX_BOOL CPDF_Parser::LoadCrossRefV4(FX_FILESIZE pos,
-                                    FX_FILESIZE streampos,
-                                    FX_BOOL bSkip,
-                                    FX_BOOL bFirst) {
+
+bool CPDF_Parser::FindPosInOffsets(FX_FILESIZE pos) const {
+  return FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
+                       sizeof(FX_FILESIZE), CompareFileSize);
+}
+
+bool CPDF_Parser::LoadCrossRefV4(FX_FILESIZE pos,
+                                 FX_FILESIZE streampos,
+                                 FX_BOOL bSkip,
+                                 FX_BOOL bFirst) {
   m_Syntax.RestorePos(pos);
-  if (m_Syntax.GetKeyword() != FX_BSTRC("xref")) {
-    return FALSE;
-  }
-  void* pResult =
-      FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
-                    sizeof(FX_FILESIZE), _CompareFileSize);
-  if (pResult == NULL) {
+  if (m_Syntax.GetKeyword() != FX_BSTRC("xref"))
+    return false;
+
+  if (!FindPosInOffsets(pos))
     m_SortedOffset.Add(pos);
-  }
-  if (streampos) {
-    void* pResult = FXSYS_bsearch(&streampos, m_SortedOffset.GetData(),
-                                  m_SortedOffset.GetSize(), sizeof(FX_FILESIZE),
-                                  _CompareFileSize);
-    if (pResult == NULL) {
+
+  if (streampos && !FindPosInOffsets(streampos))
       m_SortedOffset.Add(streampos);
-    }
-  }
+
   while (1) {
     FX_FILESIZE SavedPos = m_Syntax.SavePos();
     FX_BOOL bIsNumber;
     CFX_ByteString word = m_Syntax.GetNextWord(bIsNumber);
-    if (word.IsEmpty()) {
-      return FALSE;
-    }
+    if (word.IsEmpty())
+      return false;
+
     if (!bIsNumber) {
       m_Syntax.RestorePos(SavedPos);
       break;
     }
     FX_DWORD start_objnum = FXSYS_atoi(word);
-    if (start_objnum >= (1 << 20)) {
-      return FALSE;
-    }
+    if (start_objnum >= (1 << 20))
+      return false;
+
     FX_DWORD count = m_Syntax.GetDirectNum();
     m_Syntax.ToNextWord();
     SavedPos = m_Syntax.SavePos();
     FX_BOOL bFirstItem = FALSE;
-    int32_t recordsize = 20;
-    if (bFirst) {
+    const int32_t recordsize = 20;
+    if (bFirst)
       bFirstItem = TRUE;
-    }
     m_dwXrefStartObjNum = start_objnum;
     if (!bSkip) {
-      char* pBuf = FX_Alloc(char, 1024 * recordsize + 1);
+      std::vector<char> buf(1024 * recordsize + 1);
+      char* pBuf = pdfium::vector_as_array(&buf);
       pBuf[1024 * recordsize] = '\0';
       int32_t nBlocks = count / 1024 + 1;
       FX_BOOL bFirstBlock = TRUE;
       for (int32_t block = 0; block < nBlocks; block++) {
         int32_t block_size = block == nBlocks - 1 ? count % 1024 : 1024;
-        m_Syntax.ReadBlock((uint8_t*)pBuf, block_size * recordsize);
+        m_Syntax.ReadBlock(reinterpret_cast<uint8_t*>(pBuf),
+                           block_size * recordsize);
         for (int32_t i = 0; i < block_size; i++) {
           FX_DWORD objnum = start_objnum + block * 1024 + i;
           char* pEntry = pBuf + i * recordsize;
@@ -559,10 +557,8 @@ FX_BOOL CPDF_Parser::LoadCrossRefV4(FX_FILESIZE pos,
             FX_FILESIZE offset = (FX_FILESIZE)FXSYS_atoi64(pEntry);
             if (offset == 0) {
               for (int32_t c = 0; c < 10; c++) {
-                if (pEntry[c] < '0' || pEntry[c] > '9') {
-                  FX_Free(pBuf);
-                  return FALSE;
-                }
+                if (!std::isdigit(pEntry[c]))
+                  return false;
               }
             }
             m_CrossRef.SetAtGrow(objnum, offset);
@@ -571,14 +567,9 @@ FX_BOOL CPDF_Parser::LoadCrossRefV4(FX_FILESIZE pos,
               m_bVersionUpdated = TRUE;
             }
             m_ObjVersion.SetAtGrow(objnum, version);
-            if (m_CrossRef[objnum] < m_Syntax.m_FileLen) {
-              void* pResult =
-                  FXSYS_bsearch(&m_CrossRef[objnum], m_SortedOffset.GetData(),
-                                m_SortedOffset.GetSize(), sizeof(FX_FILESIZE),
-                                _CompareFileSize);
-              if (pResult == NULL) {
-                m_SortedOffset.Add(m_CrossRef[objnum]);
-              }
+            if (m_CrossRef[objnum] < m_Syntax.m_FileLen &&
+                !FindPosInOffsets(m_CrossRef[objnum])) {
+              m_SortedOffset.Add(m_CrossRef[objnum]);
             }
             m_V5Type.SetAtGrow(objnum, 1);
           }
@@ -587,16 +578,12 @@ FX_BOOL CPDF_Parser::LoadCrossRefV4(FX_FILESIZE pos,
           }
         }
       }
-      FX_Free(pBuf);
     }
     m_Syntax.RestorePos(SavedPos + count * recordsize);
   }
-  if (streampos)
-    if (!LoadCrossRefV5(streampos, streampos, FALSE)) {
-      return FALSE;
-    }
-  return TRUE;
+  return !streampos || LoadCrossRefV5(streampos, streampos, FALSE);
 }
+
 FX_BOOL CPDF_Parser::LoadAllCrossRefV5(FX_FILESIZE xrefpos) {
   if (!LoadCrossRefV5(xrefpos, xrefpos, TRUE)) {
     return FALSE;
@@ -639,37 +626,41 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
       uint8_t byte = buffer[i];
       switch (status) {
         case 0:
-          if (PDF_CharType[byte] == 'W') {
+          if (PDFCharIsWhitespace(byte))
             status = 1;
-          }
-          if (byte <= '9' && byte >= '0') {
+
+          if (std::isdigit(byte)) {
             --i;
             status = 1;
           }
+
           if (byte == '%') {
             inside_index = 0;
             status = 9;
           }
+
           if (byte == '(') {
             status = 10;
             depth = 1;
           }
+
           if (byte == '<') {
             inside_index = 1;
             status = 11;
           }
-          if (byte == '\\') {
+
+          if (byte == '\\')
             status = 13;
-          }
+
           if (byte == 't') {
             status = 7;
             inside_index = 1;
           }
           break;
         case 1:
-          if (PDF_CharType[byte] == 'W') {
+          if (PDFCharIsWhitespace(byte)) {
             break;
-          } else if (byte <= '9' && byte >= '0') {
+          } else if (std::isdigit(byte)) {
             start_pos = pos + i;
             status = 2;
             objnum = byte - '0';
@@ -685,10 +676,10 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
           }
           break;
         case 2:
-          if (byte <= '9' && byte >= '0') {
+          if (std::isdigit(byte)) {
             objnum = objnum * 10 + byte - '0';
             break;
-          } else if (PDF_CharType[byte] == 'W') {
+          } else if (PDFCharIsWhitespace(byte)) {
             status = 3;
           } else {
             --i;
@@ -697,11 +688,11 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
           }
           break;
         case 3:
-          if (byte <= '9' && byte >= '0') {
+          if (std::isdigit(byte)) {
             start_pos1 = pos + i;
             status = 4;
             gennum = byte - '0';
-          } else if (PDF_CharType[byte] == 'W') {
+          } else if (PDFCharIsWhitespace(byte)) {
             break;
           } else if (byte == 't') {
             status = 7;
@@ -712,10 +703,10 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
           }
           break;
         case 4:
-          if (byte <= '9' && byte >= '0') {
+          if (std::isdigit(byte)) {
             gennum = gennum * 10 + byte - '0';
             break;
-          } else if (PDF_CharType[byte] == 'W') {
+          } else if (PDFCharIsWhitespace(byte)) {
             status = 5;
           } else {
             --i;
@@ -726,9 +717,9 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
           if (byte == 'o') {
             status = 6;
             inside_index = 1;
-          } else if (PDF_CharType[byte] == 'W') {
+          } else if (PDFCharIsWhitespace(byte)) {
             break;
-          } else if (byte <= '9' && byte >= '0') {
+          } else if (std::isdigit(byte)) {
             objnum = gennum;
             gennum = byte - '0';
             start_pos = start_pos1;
@@ -761,7 +752,7 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
               }
               break;
             case 3:
-              if (PDF_CharType[byte] == 'W' || PDF_CharType[byte] == 'D') {
+              if (PDFCharIsWhitespace(byte) || PDFCharIsDelimiter(byte)) {
                 if (objnum > 0x1000000) {
                   status = 0;
                   break;
@@ -771,36 +762,27 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
                 void* pResult =
                     FXSYS_bsearch(&obj_pos, m_SortedOffset.GetData(),
                                   m_SortedOffset.GetSize(), sizeof(FX_FILESIZE),
-                                  _CompareFileSize);
+                                  CompareFileSize);
                 if (pResult == NULL) {
                   m_SortedOffset.Add(obj_pos);
                 }
                 FX_FILESIZE obj_end = 0;
                 CPDF_Object* pObject = ParseIndirectObjectAtByStrict(
                     m_pDocument, obj_pos, objnum, NULL, &obj_end);
-                if (pObject) {
-                  int iType = pObject->GetType();
-                  if (iType == PDFOBJ_STREAM) {
-                    CPDF_Stream* pStream = (CPDF_Stream*)pObject;
-                    CPDF_Dictionary* pDict = pStream->GetDict();
-                    if (pDict) {
-                      if (pDict->KeyExist(FX_BSTRC("Type"))) {
-                        CFX_ByteString bsValue =
-                            pDict->GetString(FX_BSTRC("Type"));
-                        if (bsValue == FX_BSTRC("XRef") &&
-                            pDict->KeyExist(FX_BSTRC("Size"))) {
-                          CPDF_Object* pRoot =
-                              pDict->GetElement(FX_BSTRC("Root"));
-                          if (pRoot && pRoot->GetDict() &&
-                              pRoot->GetDict()->GetElement(FX_BSTRC("Pages"))) {
-                            if (m_pTrailer) {
-                              m_pTrailer->Release();
-                            }
-                            m_pTrailer = (CPDF_Dictionary*)pDict->Clone();
-                          }
+                if (CPDF_Stream* pStream = ToStream(pObject)) {
+                  if (CPDF_Dictionary* pDict = pStream->GetDict()) {
+                    if ((pDict->KeyExist(FX_BSTRC("Type"))) &&
+                        (pDict->GetString(FX_BSTRC("Type")) ==
+                             FX_BSTRC("XRef") &&
+                         pDict->KeyExist(FX_BSTRC("Size")))) {
+                      CPDF_Object* pRoot = pDict->GetElement(FX_BSTRC("Root"));
+                      if (pRoot && pRoot->GetDict() &&
+                          pRoot->GetDict()->GetElement(FX_BSTRC("Pages"))) {
+                        if (m_pTrailer)
+                          m_pTrailer->Release();
+                        m_pTrailer = ToDictionary(pDict->Clone());
                         }
                       }
-                    }
                   }
                 }
                 FX_FILESIZE offset = 0;
@@ -844,45 +826,46 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
           break;
         case 7:
           if (inside_index == 7) {
-            if (PDF_CharType[byte] == 'W' || PDF_CharType[byte] == 'D') {
+            if (PDFCharIsWhitespace(byte) || PDFCharIsDelimiter(byte)) {
               last_trailer = pos + i - 7;
               m_Syntax.RestorePos(pos + i - m_Syntax.m_HeaderOffset);
               CPDF_Object* pObj = m_Syntax.GetObject(m_pDocument, 0, 0, 0);
               if (pObj) {
-                if (pObj->GetType() != PDFOBJ_DICTIONARY &&
-                    pObj->GetType() != PDFOBJ_STREAM) {
+                if (!pObj->IsDictionary() && !pObj->AsStream()) {
                   pObj->Release();
                 } else {
-                  CPDF_Dictionary* pTrailer = NULL;
-                  if (pObj->GetType() == PDFOBJ_STREAM) {
-                    pTrailer = ((CPDF_Stream*)pObj)->GetDict();
-                  } else {
-                    pTrailer = (CPDF_Dictionary*)pObj;
-                  }
-                  if (pTrailer) {
+                  CPDF_Stream* pStream = pObj->AsStream();
+                  if (CPDF_Dictionary* pTrailer =
+                          pStream ? pStream->GetDict() : pObj->AsDictionary()) {
                     if (m_pTrailer) {
                       CPDF_Object* pRoot =
                           pTrailer->GetElement(FX_BSTRC("Root"));
-                      if (pRoot == NULL ||
-                          (pRoot->GetType() == PDFOBJ_REFERENCE &&
+                      CPDF_Reference* pRef = ToReference(pRoot);
+                      if (!pRoot ||
+                          (pRef &&
                            (FX_DWORD)m_CrossRef.GetSize() >
-                               ((CPDF_Reference*)pRoot)->GetRefObjNum() &&
-                           m_CrossRef.GetAt(((CPDF_Reference*)pRoot)
-                                                ->GetRefObjNum()) != 0)) {
+                               pRef->GetRefObjNum() &&
+                           m_CrossRef.GetAt(pRef->GetRefObjNum()) != 0)) {
                         FX_POSITION pos = pTrailer->GetStartPos();
                         while (pos) {
                           CFX_ByteString key;
-                          CPDF_Object* pObj =
+                          CPDF_Object* pElement =
                               pTrailer->GetNextElement(pos, key);
-                          m_pTrailer->SetAt(key, pObj->Clone(), m_pDocument);
+                          FX_DWORD dwObjNum = pElement->GetObjNum();
+                          if (dwObjNum) {
+                            m_pTrailer->SetAtReference(key, m_pDocument,
+                                                       dwObjNum);
+                          } else {
+                            m_pTrailer->SetAt(key, pElement->Clone());
+                          }
                         }
                         pObj->Release();
                       } else {
                         pObj->Release();
                       }
                     } else {
-                      if (pObj->GetType() == PDFOBJ_STREAM) {
-                        m_pTrailer = (CPDF_Dictionary*)pTrailer->Clone();
+                      if (pObj->IsStream()) {
+                        m_pTrailer = ToDictionary(pTrailer->Clone());
                         pObj->Release();
                       } else {
                         m_pTrailer = pTrailer;
@@ -954,13 +937,13 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
           status = 0;
           break;
         case 13:
-          if (PDF_CharType[byte] == 'D' || PDF_CharType[byte] == 'W') {
+          if (PDFCharIsDelimiter(byte) || PDFCharIsWhitespace(byte)) {
             --i;
             status = 0;
           }
           break;
         case 14:
-          if (PDF_CharType[byte] == 'W') {
+          if (PDFCharIsWhitespace(byte)) {
             status = 0;
           } else if (byte == '%' || byte == '(' || byte == '<' ||
                      byte == '\\') {
@@ -989,42 +972,36 @@ FX_BOOL CPDF_Parser::RebuildCrossRef() {
   FX_FILESIZE offset = last_trailer - m_Syntax.m_HeaderOffset;
   void* pResult =
       FXSYS_bsearch(&offset, m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
-                    sizeof(FX_FILESIZE), _CompareFileSize);
+                    sizeof(FX_FILESIZE), CompareFileSize);
   if (pResult == NULL) {
     m_SortedOffset.Add(offset);
   }
   FX_Free(buffer);
   return TRUE;
 }
-static FX_DWORD _GetVarInt(const uint8_t* p, int32_t n) {
-  FX_DWORD result = 0;
-  for (int32_t i = 0; i < n; i++) {
-    result = result * 256 + p[i];
-  }
-  return result;
-}
+
 FX_BOOL CPDF_Parser::LoadCrossRefV5(FX_FILESIZE pos,
                                     FX_FILESIZE& prev,
                                     FX_BOOL bMainXRef) {
-  CPDF_Stream* pStream =
-      (CPDF_Stream*)ParseIndirectObjectAt(m_pDocument, pos, 0, NULL);
-  if (!pStream) {
+  CPDF_Object* pObject = ParseIndirectObjectAt(m_pDocument, pos, 0, nullptr);
+  if (!pObject)
     return FALSE;
-  }
+
   if (m_pDocument) {
     CPDF_Dictionary* pDict = m_pDocument->GetRoot();
-    if (!pDict || pDict->GetObjNum() != pStream->m_ObjNum) {
-      m_pDocument->InsertIndirectObject(pStream->m_ObjNum, pStream);
+    if (!pDict || pDict->GetObjNum() != pObject->m_ObjNum) {
+      m_pDocument->InsertIndirectObject(pObject->m_ObjNum, pObject);
     } else {
-      if (pStream->GetType() == PDFOBJ_STREAM) {
-        pStream->Release();
-      }
+      if (pObject->IsStream())
+        pObject->Release();
       return FALSE;
     }
   }
-  if (pStream->GetType() != PDFOBJ_STREAM) {
+
+  CPDF_Stream* pStream = pObject->AsStream();
+  if (!pStream)
     return FALSE;
-  }
+
   prev = pStream->GetDict()->GetInteger(FX_BSTRC("Prev"));
   int32_t size = pStream->GetDict()->GetInteger(FX_BSTRC("Size"));
   if (size < 0) {
@@ -1032,13 +1009,13 @@ FX_BOOL CPDF_Parser::LoadCrossRefV5(FX_FILESIZE pos,
     return FALSE;
   }
   if (bMainXRef) {
-    m_pTrailer = (CPDF_Dictionary*)pStream->GetDict()->Clone();
+    m_pTrailer = ToDictionary(pStream->GetDict()->Clone());
     m_CrossRef.SetSize(size);
     if (m_V5Type.SetSize(size)) {
       FXSYS_memset(m_V5Type.GetData(), 0, size);
     }
   } else {
-    m_Trailers.Add((CPDF_Dictionary*)pStream->GetDict()->Clone());
+    m_Trailers.Add(ToDictionary(pStream->GetDict()->Clone()));
   }
   std::vector<std::pair<int32_t, int32_t> > arrIndex;
   CPDF_Array* pArray = pStream->GetDict()->GetArray(FX_BSTRC("Index"));
@@ -1047,8 +1024,7 @@ FX_BOOL CPDF_Parser::LoadCrossRefV5(FX_FILESIZE pos,
     for (FX_DWORD i = 0; i < nPairSize; i++) {
       CPDF_Object* pStartNumObj = pArray->GetElement(i * 2);
       CPDF_Object* pCountObj = pArray->GetElement(i * 2 + 1);
-      if (pStartNumObj && pStartNumObj->GetType() == PDFOBJ_NUMBER &&
-          pCountObj && pCountObj->GetType() == PDFOBJ_NUMBER) {
+      if (ToNumber(pStartNumObj) && ToNumber(pCountObj)) {
         int nStartNum = pStartNumObj->GetInteger();
         int nCount = pCountObj->GetInteger();
         if (nStartNum >= 0 && nCount > 0) {
@@ -1109,15 +1085,15 @@ FX_BOOL CPDF_Parser::LoadCrossRefV5(FX_FILESIZE pos,
       int32_t type = 1;
       const uint8_t* entrystart = segstart + j * totalWidth;
       if (WidthArray[0]) {
-        type = _GetVarInt(entrystart, WidthArray[0]);
+        type = GetVarInt(entrystart, WidthArray[0]);
       }
       if (m_V5Type[startnum + j] == 255) {
         FX_FILESIZE offset =
-            _GetVarInt(entrystart + WidthArray[0], WidthArray[1]);
+            GetVarInt(entrystart + WidthArray[0], WidthArray[1]);
         m_CrossRef[startnum + j] = offset;
         void* pResult = FXSYS_bsearch(&offset, m_SortedOffset.GetData(),
                                       m_SortedOffset.GetSize(),
-                                      sizeof(FX_FILESIZE), _CompareFileSize);
+                                      sizeof(FX_FILESIZE), CompareFileSize);
         if (pResult == NULL) {
           m_SortedOffset.Add(offset);
         }
@@ -1131,12 +1107,12 @@ FX_BOOL CPDF_Parser::LoadCrossRefV5(FX_FILESIZE pos,
         m_CrossRef[startnum + j] = 0;
       } else {
         FX_FILESIZE offset =
-            _GetVarInt(entrystart + WidthArray[0], WidthArray[1]);
+            GetVarInt(entrystart + WidthArray[0], WidthArray[1]);
         m_CrossRef[startnum + j] = offset;
         if (type == 1) {
           void* pResult = FXSYS_bsearch(&offset, m_SortedOffset.GetData(),
                                         m_SortedOffset.GetSize(),
-                                        sizeof(FX_FILESIZE), _CompareFileSize);
+                                        sizeof(FX_FILESIZE), CompareFileSize);
           if (pResult == NULL) {
             m_SortedOffset.Add(offset);
           }
@@ -1156,33 +1132,24 @@ FX_BOOL CPDF_Parser::LoadCrossRefV5(FX_FILESIZE pos,
 }
 CPDF_Array* CPDF_Parser::GetIDArray() {
   CPDF_Object* pID = m_pTrailer ? m_pTrailer->GetElement(FX_BSTRC("ID")) : NULL;
-  if (pID == NULL) {
-    return NULL;
-  }
-  if (pID->GetType() == PDFOBJ_REFERENCE) {
-    pID = ParseIndirectObject(NULL, ((CPDF_Reference*)pID)->GetRefObjNum());
+  if (!pID)
+    return nullptr;
+
+  if (CPDF_Reference* pRef = pID->AsReference()) {
+    pID = ParseIndirectObject(nullptr, pRef->GetRefObjNum());
     m_pTrailer->SetAt(FX_BSTRC("ID"), pID);
   }
-  if (pID == NULL || pID->GetType() != PDFOBJ_ARRAY) {
-    return NULL;
-  }
-  return (CPDF_Array*)pID;
+  return ToArray(pID);
 }
 FX_DWORD CPDF_Parser::GetRootObjNum() {
-  CPDF_Object* pRef =
-      m_pTrailer ? m_pTrailer->GetElement(FX_BSTRC("Root")) : NULL;
-  if (pRef == NULL || pRef->GetType() != PDFOBJ_REFERENCE) {
-    return 0;
-  }
-  return ((CPDF_Reference*)pRef)->GetRefObjNum();
+  CPDF_Reference* pRef = ToReference(
+      m_pTrailer ? m_pTrailer->GetElement(FX_BSTRC("Root")) : nullptr);
+  return pRef ? pRef->GetRefObjNum() : 0;
 }
 FX_DWORD CPDF_Parser::GetInfoObjNum() {
-  CPDF_Object* pRef =
-      m_pTrailer ? m_pTrailer->GetElement(FX_BSTRC("Info")) : NULL;
-  if (pRef == NULL || pRef->GetType() != PDFOBJ_REFERENCE) {
-    return 0;
-  }
-  return ((CPDF_Reference*)pRef)->GetRefObjNum();
+  CPDF_Reference* pRef = ToReference(
+      m_pTrailer ? m_pTrailer->GetElement(FX_BSTRC("Info")) : nullptr);
+  return pRef ? pRef->GetRefObjNum() : 0;
 }
 FX_BOOL CPDF_Parser::IsFormStream(FX_DWORD objnum, FX_BOOL& bForm) {
   bForm = FALSE;
@@ -1198,7 +1165,7 @@ FX_BOOL CPDF_Parser::IsFormStream(FX_DWORD objnum, FX_BOOL& bForm) {
   FX_FILESIZE pos = m_CrossRef[objnum];
   void* pResult =
       FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
-                    sizeof(FX_FILESIZE), _CompareFileSize);
+                    sizeof(FX_FILESIZE), CompareFileSize);
   if (pResult == NULL) {
     return TRUE;
   }
@@ -1213,55 +1180,52 @@ FX_BOOL CPDF_Parser::IsFormStream(FX_DWORD objnum, FX_BOOL& bForm) {
   m_Syntax.RestorePos(SavedPos);
   return TRUE;
 }
+
 CPDF_Object* CPDF_Parser::ParseIndirectObject(CPDF_IndirectObjects* pObjList,
                                               FX_DWORD objnum,
                                               PARSE_CONTEXT* pContext) {
-  if (objnum >= (FX_DWORD)m_CrossRef.GetSize()) {
-    return NULL;
-  }
+  if (objnum >= (FX_DWORD)m_CrossRef.GetSize())
+    return nullptr;
+
   if (m_V5Type[objnum] == 1 || m_V5Type[objnum] == 255) {
     FX_FILESIZE pos = m_CrossRef[objnum];
-    if (pos <= 0) {
-      return NULL;
-    }
+    if (pos <= 0)
+      return nullptr;
     return ParseIndirectObjectAt(pObjList, pos, objnum, pContext);
   }
-  if (m_V5Type[objnum] == 2) {
-    CPDF_StreamAcc* pObjStream = GetObjectStream((FX_DWORD)m_CrossRef[objnum]);
-    if (pObjStream == NULL) {
-      return NULL;
-    }
-    int32_t n = pObjStream->GetDict()->GetInteger(FX_BSTRC("N"));
-    int32_t offset = pObjStream->GetDict()->GetInteger(FX_BSTRC("First"));
-    CPDF_SyntaxParser syntax;
-    CFX_SmartPointer<IFX_FileStream> file(FX_CreateMemoryStream(
-        (uint8_t*)pObjStream->GetData(), (size_t)pObjStream->GetSize(), FALSE));
-    syntax.InitParser(file.Get(), 0);
-    CPDF_Object* pRet = NULL;
-    while (n) {
-      FX_DWORD thisnum = syntax.GetDirectNum();
-      FX_DWORD thisoff = syntax.GetDirectNum();
-      if (thisnum == objnum) {
-        syntax.RestorePos(offset + thisoff);
-        pRet = syntax.GetObject(pObjList, 0, 0, pContext);
-        break;
-      }
-      n--;
-    }
-    return pRet;
-  }
-  return NULL;
+  if (m_V5Type[objnum] != 2)
+    return nullptr;
+
+  CPDF_StreamAcc* pObjStream = GetObjectStream((FX_DWORD)m_CrossRef[objnum]);
+  if (!pObjStream)
+    return nullptr;
+
+  ScopedFileStream file(FX_CreateMemoryStream(
+      (uint8_t*)pObjStream->GetData(), (size_t)pObjStream->GetSize(), FALSE));
+  CPDF_SyntaxParser syntax;
+  syntax.InitParser(file.get(), 0);
+  int32_t offset = GetStreamFirst(pObjStream);
+  for (int32_t i = GetStreamNCount(pObjStream); i > 0; --i) {
+    FX_DWORD thisnum = syntax.GetDirectNum();
+    FX_DWORD thisoff = syntax.GetDirectNum();
+    if (thisnum == objnum) {
+      syntax.RestorePos(offset + thisoff);
+      return syntax.GetObject(pObjList, 0, 0, pContext);
+    }
+  }
+  return nullptr;
 }
+
 CPDF_StreamAcc* CPDF_Parser::GetObjectStream(FX_DWORD objnum) {
-  CPDF_StreamAcc* pStreamAcc = NULL;
-  if (m_ObjectStreamMap.Lookup((void*)(uintptr_t)objnum, (void*&)pStreamAcc)) {
+  CPDF_StreamAcc* pStreamAcc = nullptr;
+  if (m_ObjectStreamMap.Lookup((void*)(uintptr_t)objnum, (void*&)pStreamAcc))
     return pStreamAcc;
-  }
+
   const CPDF_Stream* pStream =
-      m_pDocument ? (CPDF_Stream*)m_pDocument->GetIndirectObject(objnum) : NULL;
-  if (pStream == NULL || pStream->GetType() != PDFOBJ_STREAM) {
-    return NULL;
-  }
+      ToStream(m_pDocument ? m_pDocument->GetIndirectObject(objnum) : nullptr);
+  if (!pStream)
+    return nullptr;
+
   pStreamAcc = new CPDF_StreamAcc;
   pStreamAcc->LoadAllData(pStream);
   m_ObjectStreamMap.SetAt((void*)(uintptr_t)objnum, pStreamAcc);
@@ -1281,7 +1245,7 @@ FX_FILESIZE CPDF_Parser::GetObjectSize(FX_DWORD objnum) {
     }
     void* pResult = FXSYS_bsearch(&offset, m_SortedOffset.GetData(),
                                   m_SortedOffset.GetSize(), sizeof(FX_FILESIZE),
-                                  _CompareFileSize);
+                                  CompareFileSize);
     if (pResult == NULL) {
       return 0;
     }
@@ -1303,103 +1267,105 @@ void CPDF_Parser::GetIndirectBinary(FX_DWORD objnum,
   }
   if (m_V5Type[objnum] == 2) {
     CPDF_StreamAcc* pObjStream = GetObjectStream((FX_DWORD)m_CrossRef[objnum]);
-    if (pObjStream == NULL) {
+    if (!pObjStream)
       return;
-    }
-    int32_t n = pObjStream->GetDict()->GetInteger(FX_BSTRC("N"));
-    int32_t offset = pObjStream->GetDict()->GetInteger(FX_BSTRC("First"));
-    CPDF_SyntaxParser syntax;
+
+    int32_t offset = GetStreamFirst(pObjStream);
     const uint8_t* pData = pObjStream->GetData();
     FX_DWORD totalsize = pObjStream->GetSize();
-    CFX_SmartPointer<IFX_FileStream> file(
+    ScopedFileStream file(
         FX_CreateMemoryStream((uint8_t*)pData, (size_t)totalsize, FALSE));
-    syntax.InitParser(file.Get(), 0);
-    while (n) {
+    CPDF_SyntaxParser syntax;
+    syntax.InitParser(file.get(), 0);
+    for (int i = GetStreamNCount(pObjStream); i > 0; --i) {
       FX_DWORD thisnum = syntax.GetDirectNum();
       FX_DWORD thisoff = syntax.GetDirectNum();
-      if (thisnum == objnum) {
-        if (n == 1) {
-          size = totalsize - (thisoff + offset);
-        } else {
-          syntax.GetDirectNum();  // Skip nextnum.
-          FX_DWORD nextoff = syntax.GetDirectNum();
-          size = nextoff - thisoff;
-        }
-        pBuffer = FX_Alloc(uint8_t, size);
-        FXSYS_memcpy(pBuffer, pData + thisoff + offset, size);
-        return;
+      if (thisnum != objnum)
+        continue;
+
+      if (i == 1) {
+        size = totalsize - (thisoff + offset);
+      } else {
+        syntax.GetDirectNum();  // Skip nextnum.
+        FX_DWORD nextoff = syntax.GetDirectNum();
+        size = nextoff - thisoff;
       }
-      n--;
+      pBuffer = FX_Alloc(uint8_t, size);
+      FXSYS_memcpy(pBuffer, pData + thisoff + offset, size);
+      return;
     }
     return;
   }
-  if (m_V5Type[objnum] == 1) {
-    FX_FILESIZE pos = m_CrossRef[objnum];
-    if (pos == 0) {
-      return;
-    }
-    FX_FILESIZE SavedPos = m_Syntax.SavePos();
-    m_Syntax.RestorePos(pos);
-    FX_BOOL bIsNumber;
-    CFX_ByteString word = m_Syntax.GetNextWord(bIsNumber);
-    if (!bIsNumber) {
-      m_Syntax.RestorePos(SavedPos);
-      return;
-    }
-    FX_DWORD parser_objnum = FXSYS_atoi(word);
-    if (parser_objnum && parser_objnum != objnum) {
-      m_Syntax.RestorePos(SavedPos);
-      return;
-    }
+
+  if (m_V5Type[objnum] != 1)
+    return;
+
+  FX_FILESIZE pos = m_CrossRef[objnum];
+  if (pos == 0) {
+    return;
+  }
+  FX_FILESIZE SavedPos = m_Syntax.SavePos();
+  m_Syntax.RestorePos(pos);
+  FX_BOOL bIsNumber;
+  CFX_ByteString word = m_Syntax.GetNextWord(bIsNumber);
+  if (!bIsNumber) {
+    m_Syntax.RestorePos(SavedPos);
+    return;
+  }
+  FX_DWORD parser_objnum = FXSYS_atoi(word);
+  if (parser_objnum && parser_objnum != objnum) {
+    m_Syntax.RestorePos(SavedPos);
+    return;
+  }
+  word = m_Syntax.GetNextWord(bIsNumber);
+  if (!bIsNumber) {
+    m_Syntax.RestorePos(SavedPos);
+    return;
+  }
+  if (m_Syntax.GetKeyword() != FX_BSTRC("obj")) {
+    m_Syntax.RestorePos(SavedPos);
+    return;
+  }
+  void* pResult =
+      FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
+                    sizeof(FX_FILESIZE), CompareFileSize);
+  if (pResult == NULL) {
+    m_Syntax.RestorePos(SavedPos);
+    return;
+  }
+  FX_FILESIZE nextoff = ((FX_FILESIZE*)pResult)[1];
+  FX_BOOL bNextOffValid = FALSE;
+  if (nextoff != pos) {
+    m_Syntax.RestorePos(nextoff);
     word = m_Syntax.GetNextWord(bIsNumber);
-    if (!bIsNumber) {
-      m_Syntax.RestorePos(SavedPos);
-      return;
-    }
-    if (m_Syntax.GetKeyword() != FX_BSTRC("obj")) {
-      m_Syntax.RestorePos(SavedPos);
-      return;
-    }
-    void* pResult =
-        FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
-                      sizeof(FX_FILESIZE), _CompareFileSize);
-    if (pResult == NULL) {
-      m_Syntax.RestorePos(SavedPos);
-      return;
-    }
-    FX_FILESIZE nextoff = ((FX_FILESIZE*)pResult)[1];
-    FX_BOOL bNextOffValid = FALSE;
-    if (nextoff != pos) {
-      m_Syntax.RestorePos(nextoff);
+    if (word == FX_BSTRC("xref")) {
+      bNextOffValid = TRUE;
+    } else if (bIsNumber) {
       word = m_Syntax.GetNextWord(bIsNumber);
-      if (word == FX_BSTRC("xref")) {
+      if (bIsNumber && m_Syntax.GetKeyword() == FX_BSTRC("obj")) {
         bNextOffValid = TRUE;
-      } else if (bIsNumber) {
-        word = m_Syntax.GetNextWord(bIsNumber);
-        if (bIsNumber && m_Syntax.GetKeyword() == FX_BSTRC("obj")) {
-          bNextOffValid = TRUE;
-        }
       }
     }
-    if (!bNextOffValid) {
-      m_Syntax.RestorePos(pos);
-      while (1) {
-        if (m_Syntax.GetKeyword() == FX_BSTRC("endobj")) {
-          break;
-        }
-        if (m_Syntax.SavePos() == m_Syntax.m_FileLen) {
-          break;
-        }
+  }
+  if (!bNextOffValid) {
+    m_Syntax.RestorePos(pos);
+    while (1) {
+      if (m_Syntax.GetKeyword() == FX_BSTRC("endobj")) {
+        break;
+      }
+      if (m_Syntax.SavePos() == m_Syntax.m_FileLen) {
+        break;
       }
-      nextoff = m_Syntax.SavePos();
     }
-    size = (FX_DWORD)(nextoff - pos);
-    pBuffer = FX_Alloc(uint8_t, size);
-    m_Syntax.RestorePos(pos);
-    m_Syntax.ReadBlock(pBuffer, size);
-    m_Syntax.RestorePos(SavedPos);
+    nextoff = m_Syntax.SavePos();
   }
+  size = (FX_DWORD)(nextoff - pos);
+  pBuffer = FX_Alloc(uint8_t, size);
+  m_Syntax.RestorePos(pos);
+  m_Syntax.ReadBlock(pBuffer, size);
+  m_Syntax.RestorePos(SavedPos);
 }
+
 CPDF_Object* CPDF_Parser::ParseIndirectObjectAt(CPDF_IndirectObjects* pObjList,
                                                 FX_FILESIZE pos,
                                                 FX_DWORD objnum,
@@ -1482,21 +1448,20 @@ CPDF_Object* CPDF_Parser::ParseIndirectObjectAtByStrict(
   m_Syntax.RestorePos(SavedPos);
   return pObj;
 }
+
 CPDF_Dictionary* CPDF_Parser::LoadTrailerV4() {
-  if (m_Syntax.GetKeyword() != FX_BSTRC("trailer")) {
-    return NULL;
-  }
-  CPDF_Object* pObj = m_Syntax.GetObject(m_pDocument, 0, 0, 0);
-  if (pObj == NULL || pObj->GetType() != PDFOBJ_DICTIONARY) {
-    if (pObj) {
-      pObj->Release();
-    }
-    return NULL;
-  }
-  return (CPDF_Dictionary*)pObj;
+  if (m_Syntax.GetKeyword() != FX_BSTRC("trailer"))
+    return nullptr;
+
+  nonstd::unique_ptr<CPDF_Object, ReleaseDeleter<CPDF_Object>> pObj(
+      m_Syntax.GetObject(m_pDocument, 0, 0, 0));
+  if (!ToDictionary(pObj.get()))
+    return nullptr;
+  return pObj.release()->AsDictionary();
 }
+
 FX_DWORD CPDF_Parser::GetPermissions(FX_BOOL bCheckRevision) {
-  if (m_pSecurityHandler == NULL) {
+  if (!m_pSecurityHandler) {
     return (FX_DWORD)-1;
   }
   FX_DWORD dwPermission = m_pSecurityHandler->GetPermissions();
@@ -1515,17 +1480,12 @@ FX_BOOL CPDF_Parser::IsOwner() {
 }
 void CPDF_Parser::SetSecurityHandler(CPDF_SecurityHandler* pSecurityHandler,
                                      FX_BOOL bForced) {
-  ASSERT(m_pSecurityHandler == NULL);
-  if (!m_bForceUseSecurityHandler) {
-    delete m_pSecurityHandler;
-    m_pSecurityHandler = NULL;
-  }
   m_bForceUseSecurityHandler = bForced;
-  m_pSecurityHandler = pSecurityHandler;
+  m_pSecurityHandler.reset(pSecurityHandler);
   if (m_bForceUseSecurityHandler) {
     return;
   }
-  m_Syntax.m_pCryptoHandler = pSecurityHandler->CreateCryptoHandler();
+  m_Syntax.m_pCryptoHandler.reset(pSecurityHandler->CreateCryptoHandler());
   m_Syntax.m_pCryptoHandler->Init(NULL, pSecurityHandler);
 }
 FX_BOOL CPDF_Parser::IsLinearizedFile(IFX_FileRead* pFileAccess,
@@ -1552,10 +1512,12 @@ FX_BOOL CPDF_Parser::IsLinearizedFile(IFX_FileRead* pFileAccess,
   if (!m_pLinearized) {
     return FALSE;
   }
-  if (m_pLinearized->GetDict() &&
-      m_pLinearized->GetDict()->GetElement(FX_BSTRC("Linearized"))) {
+
+  CPDF_Dictionary* pDict = m_pLinearized->GetDict();
+  if (pDict && pDict->GetElement(FX_BSTRC("Linearized"))) {
     m_Syntax.GetNextWord(bIsNumber);
-    CPDF_Object* pLen = m_pLinearized->GetDict()->GetElement(FX_BSTRC("L"));
+
+    CPDF_Object* pLen = pDict->GetElement(FX_BSTRC("L"));
     if (!pLen) {
       m_pLinearized->Release();
       m_pLinearized = NULL;
@@ -1564,14 +1526,13 @@ FX_BOOL CPDF_Parser::IsLinearizedFile(IFX_FileRead* pFileAccess,
     if (pLen->GetInteger() != (int)pFileAccess->GetSize()) {
       return FALSE;
     }
-    CPDF_Object* pNo = m_pLinearized->GetDict()->GetElement(FX_BSTRC("P"));
-    if (pNo && pNo->GetType() == PDFOBJ_NUMBER) {
+
+    if (CPDF_Number* pNo = ToNumber(pDict->GetElement(FX_BSTRC("P"))))
       m_dwFirstPageNo = pNo->GetInteger();
-    }
-    CPDF_Object* pTable = m_pLinearized->GetDict()->GetElement(FX_BSTRC("T"));
-    if (pTable && pTable->GetType() == PDFOBJ_NUMBER) {
+
+    if (CPDF_Number* pTable = ToNumber(pDict->GetElement(FX_BSTRC("T"))))
       m_LastXRefOffset = pTable->GetInteger();
-    }
+
     return TRUE;
   }
   m_pLinearized->Release();
@@ -1641,7 +1602,7 @@ FX_DWORD CPDF_Parser::StartAsynParse(IFX_FileRead* pFileAccess,
     }
   }
   FXSYS_qsort(m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
-              sizeof(FX_FILESIZE), _CompareFileSize);
+              sizeof(FX_FILESIZE), CompareFileSize);
   FX_DWORD RootObjNum = GetRootObjNum();
   if (RootObjNum == 0) {
     ReleaseEncryptHandler();
@@ -1656,11 +1617,9 @@ FX_DWORD CPDF_Parser::StartAsynParse(IFX_FileRead* pFileAccess,
     }
   }
   if (m_pSecurityHandler && m_pSecurityHandler->IsMetadataEncrypted()) {
-    CPDF_Object* pMetadata =
-        m_pDocument->GetRoot()->GetElement(FX_BSTRC("Metadata"));
-    if (pMetadata && pMetadata->GetType() == PDFOBJ_REFERENCE) {
-      m_Syntax.m_MetadataObjnum = ((CPDF_Reference*)pMetadata)->GetRefObjNum();
-    }
+    if (CPDF_Reference* pMetadata = ToReference(
+            m_pDocument->GetRoot()->GetElement(FX_BSTRC("Metadata"))))
+      m_Syntax.m_MetadataObjnum = pMetadata->GetRefObjNum();
   }
   return PDFPARSE_ERROR_SUCCESS;
 }
@@ -1687,15 +1646,13 @@ FX_DWORD CPDF_Parser::LoadLinearizedMainXRefTable() {
   uint8_t ch = 0;
   FX_DWORD dwCount = 0;
   m_Syntax.GetNextChar(ch);
-  int32_t type = PDF_CharType[ch];
-  while (type == 'W') {
+  while (PDFCharIsWhitespace(ch)) {
     ++dwCount;
     if (m_Syntax.m_FileLen >=
         (FX_FILESIZE)(m_Syntax.SavePos() + m_Syntax.m_HeaderOffset)) {
       break;
     }
     m_Syntax.GetNextChar(ch);
-    type = PDF_CharType[ch];
   }
   m_LastXRefOffset += dwCount;
   FX_POSITION pos = m_ObjectStreamMap.GetStartPosition();
@@ -1713,7 +1670,7 @@ FX_DWORD CPDF_Parser::LoadLinearizedMainXRefTable() {
     return PDFPARSE_ERROR_FORMAT;
   }
   FXSYS_qsort(m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
-              sizeof(FX_FILESIZE), _CompareFileSize);
+              sizeof(FX_FILESIZE), CompareFileSize);
   m_Syntax.m_MetadataObjnum = dwSaveMetadataObjnum;
   return PDFPARSE_ERROR_SUCCESS;
 }
@@ -1723,7 +1680,6 @@ int CPDF_SyntaxParser::s_CurrentRecursionDepth = 0;
 
 CPDF_SyntaxParser::CPDF_SyntaxParser() {
   m_pFileAccess = NULL;
-  m_pCryptoHandler = NULL;
   m_pFileBuf = NULL;
   m_BufSize = CPDF_ModuleMgr::kFileBufSize;
   m_pFileBuf = NULL;
@@ -1732,17 +1688,15 @@ CPDF_SyntaxParser::CPDF_SyntaxParser() {
   m_bFileStream = FALSE;
 }
 CPDF_SyntaxParser::~CPDF_SyntaxParser() {
-  if (m_pFileBuf) {
-    FX_Free(m_pFileBuf);
-  }
+  FX_Free(m_pFileBuf);
 }
+
 FX_BOOL CPDF_SyntaxParser::GetCharAt(FX_FILESIZE pos, uint8_t& ch) {
-  FX_FILESIZE save_pos = m_Pos;
+  CFX_AutoRestorer<FX_FILESIZE> save_pos(&m_Pos);
   m_Pos = pos;
-  FX_BOOL ret = GetNextChar(ch);
-  m_Pos = save_pos;
-  return ret;
+  return GetNextChar(ch);
 }
+
 FX_BOOL CPDF_SyntaxParser::GetNextChar(uint8_t& ch) {
   FX_FILESIZE pos = m_Pos + m_HeaderOffset;
   if (pos >= m_FileLen) {
@@ -1815,77 +1769,66 @@ void CPDF_SyntaxParser::GetNextWord() {
   if (!GetNextChar(ch)) {
     return;
   }
-  uint8_t type = PDF_CharType[ch];
   while (1) {
-    while (type == 'W') {
-      if (!GetNextChar(ch)) {
+    while (PDFCharIsWhitespace(ch)) {
+      if (!GetNextChar(ch))
         return;
-      }
-      type = PDF_CharType[ch];
     }
-    if (ch != '%') {
+    if (ch != '%')
       break;
-    }
+
     while (1) {
-      if (!GetNextChar(ch)) {
+      if (!GetNextChar(ch))
         return;
-      }
-      if (ch == '\r' || ch == '\n') {
+      if (PDFCharIsLineEnding(ch))
         break;
-      }
     }
-    type = PDF_CharType[ch];
   }
-  if (type == 'D') {
+
+  if (PDFCharIsDelimiter(ch)) {
     m_bIsNumber = FALSE;
     m_WordBuffer[m_WordSize++] = ch;
     if (ch == '/') {
       while (1) {
-        if (!GetNextChar(ch)) {
+        if (!GetNextChar(ch))
           return;
-        }
-        type = PDF_CharType[ch];
-        if (type != 'R' && type != 'N') {
+
+        if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) {
           m_Pos--;
           return;
         }
-        if (m_WordSize < MAX_WORD_BUFFER) {
+
+        if (m_WordSize < MAX_WORD_BUFFER)
           m_WordBuffer[m_WordSize++] = ch;
-        }
       }
     } else if (ch == '<') {
-      if (!GetNextChar(ch)) {
+      if (!GetNextChar(ch))
         return;
-      }
-      if (ch == '<') {
+      if (ch == '<')
         m_WordBuffer[m_WordSize++] = ch;
-      } else {
+      else
         m_Pos--;
-      }
     } else if (ch == '>') {
-      if (!GetNextChar(ch)) {
+      if (!GetNextChar(ch))
         return;
-      }
-      if (ch == '>') {
+      if (ch == '>')
         m_WordBuffer[m_WordSize++] = ch;
-      } else {
+      else
         m_Pos--;
-      }
     }
     return;
   }
+
   while (1) {
-    if (m_WordSize < MAX_WORD_BUFFER) {
+    if (m_WordSize < MAX_WORD_BUFFER)
       m_WordBuffer[m_WordSize++] = ch;
-    }
-    if (type != 'N') {
+
+    if (!PDFCharIsNumeric(ch))
       m_bIsNumber = FALSE;
-    }
-    if (!GetNextChar(ch)) {
+    if (!GetNextChar(ch))
       return;
-    }
-    type = PDF_CharType[ch];
-    if (type == 'D' || type == 'W') {
+
+    if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) {
       m_Pos--;
       break;
     }
@@ -1979,48 +1922,33 @@ CFX_ByteString CPDF_SyntaxParser::ReadString() {
 }
 CFX_ByteString CPDF_SyntaxParser::ReadHexString() {
   uint8_t ch;
-  if (!GetNextChar(ch)) {
+  if (!GetNextChar(ch))
     return CFX_ByteString();
-  }
+
   CFX_BinaryBuf buf;
   FX_BOOL bFirst = TRUE;
   uint8_t code = 0;
   while (1) {
-    if (ch == '>') {
+    if (ch == '>')
       break;
-    }
-    if (ch >= '0' && ch <= '9') {
-      if (bFirst) {
-        code = (ch - '0') * 16;
-      } else {
-        code += ch - '0';
-        buf.AppendByte((uint8_t)code);
-      }
-      bFirst = !bFirst;
-    } else if (ch >= 'A' && ch <= 'F') {
-      if (bFirst) {
-        code = (ch - 'A' + 10) * 16;
-      } else {
-        code += ch - 'A' + 10;
-        buf.AppendByte((uint8_t)code);
-      }
-      bFirst = !bFirst;
-    } else if (ch >= 'a' && ch <= 'f') {
+
+    if (std::isxdigit(ch)) {
+      int val = HexCharToDigit(ch);
       if (bFirst) {
-        code = (ch - 'a' + 10) * 16;
+        code = val * 16;
       } else {
-        code += ch - 'a' + 10;
+        code += val;
         buf.AppendByte((uint8_t)code);
       }
       bFirst = !bFirst;
     }
-    if (!GetNextChar(ch)) {
+
+    if (!GetNextChar(ch))
       break;
-    }
   }
-  if (!bFirst) {
+  if (!bFirst)
     buf.AppendByte((uint8_t)code);
-  }
+
   return buf.GetByteString();
 }
 void CPDF_SyntaxParser::ToNextLine() {
@@ -2040,33 +1968,29 @@ void CPDF_SyntaxParser::ToNextLine() {
 }
 void CPDF_SyntaxParser::ToNextWord() {
   uint8_t ch;
-  if (!GetNextChar(ch)) {
+  if (!GetNextChar(ch))
     return;
-  }
-  uint8_t type = PDF_CharType[ch];
+
   while (1) {
-    while (type == 'W') {
+    while (PDFCharIsWhitespace(ch)) {
       m_dwWordPos = m_Pos;
-      if (!GetNextChar(ch)) {
+      if (!GetNextChar(ch))
         return;
-      }
-      type = PDF_CharType[ch];
     }
-    if (ch != '%') {
+
+    if (ch != '%')
       break;
-    }
+
     while (1) {
-      if (!GetNextChar(ch)) {
+      if (!GetNextChar(ch))
         return;
-      }
-      if (ch == '\r' || ch == '\n') {
+      if (PDFCharIsLineEnding(ch))
         break;
-      }
     }
-    type = PDF_CharType[ch];
   }
   m_Pos--;
 }
+
 CFX_ByteString CPDF_SyntaxParser::GetNextWord(FX_BOOL& bIsNumber) {
   GetNextWord();
   bIsNumber = m_bIsNumber;
@@ -2090,9 +2014,8 @@ CPDF_Object* CPDF_SyntaxParser::GetObject(CPDF_IndirectObjects* pObjList,
   FX_BOOL bIsNumber;
   CFX_ByteString word = GetNextWord(bIsNumber);
   if (word.GetLength() == 0) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_INVALID;
-    }
     return NULL;
   }
   if (bIsNumber) {
@@ -2102,34 +2025,29 @@ CPDF_Object* CPDF_SyntaxParser::GetObject(CPDF_IndirectObjects* pObjList,
       CFX_ByteString nextword2 = GetNextWord(bIsNumber);
       if (nextword2 == FX_BSTRC("R")) {
         FX_DWORD objnum = FXSYS_atoi(word);
-        if (bTypeOnly) {
+        if (bTypeOnly)
           return (CPDF_Object*)PDFOBJ_REFERENCE;
-        }
         return new CPDF_Reference(pObjList, objnum);
       }
     }
     m_Pos = SavedPos;
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_NUMBER;
-    }
     return CPDF_Number::Create(word);
   }
   if (word == FX_BSTRC("true") || word == FX_BSTRC("false")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_BOOLEAN;
-    }
     return CPDF_Boolean::Create(word == FX_BSTRC("true"));
   }
   if (word == FX_BSTRC("null")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_NULL;
-    }
     return CPDF_Null::Create();
   }
   if (word == FX_BSTRC("(")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_STRING;
-    }
     CFX_ByteString str = ReadString();
     if (m_pCryptoHandler && bDecrypt) {
       m_pCryptoHandler->Decrypt(objnum, gennum, str);
@@ -2137,9 +2055,8 @@ CPDF_Object* CPDF_SyntaxParser::GetObject(CPDF_IndirectObjects* pObjList,
     return CPDF_String::Create(str, FALSE);
   }
   if (word == FX_BSTRC("<")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_STRING;
-    }
     CFX_ByteString str = ReadHexString();
     if (m_pCryptoHandler && bDecrypt) {
       m_pCryptoHandler->Decrypt(objnum, gennum, str);
@@ -2147,74 +2064,74 @@ CPDF_Object* CPDF_SyntaxParser::GetObject(CPDF_IndirectObjects* pObjList,
     return CPDF_String::Create(str, TRUE);
   }
   if (word == FX_BSTRC("[")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_ARRAY;
-    }
     CPDF_Array* pArray = CPDF_Array::Create();
-    while (1) {
-      CPDF_Object* pObj = GetObject(pObjList, objnum, gennum);
-      if (pObj == NULL) {
-        return pArray;
-      }
+    while (CPDF_Object* pObj = GetObject(pObjList, objnum, gennum))
       pArray->Add(pObj);
-    }
+
+    return pArray;
   }
   if (word[0] == '/') {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_NAME;
-    }
     return CPDF_Name::Create(
         PDF_NameDecode(CFX_ByteStringC(m_WordBuffer + 1, m_WordSize - 1)));
   }
   if (word == FX_BSTRC("<<")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_DICTIONARY;
-    }
-    if (pContext) {
+
+    if (pContext)
       pContext->m_DictStart = SavedPos;
-    }
-    CPDF_Dictionary* pDict = CPDF_Dictionary::Create();
+
     int32_t nKeys = 0;
     FX_FILESIZE dwSignValuePos = 0;
+    nonstd::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>> pDict(
+        CPDF_Dictionary::Create());
     while (1) {
       FX_BOOL bIsNumber;
       CFX_ByteString key = GetNextWord(bIsNumber);
-      if (key.IsEmpty()) {
-        if (pDict)
-          pDict->Release();
-        return NULL;
-      }
+      if (key.IsEmpty())
+        return nullptr;
+
       FX_FILESIZE SavedPos = m_Pos - key.GetLength();
-      if (key == FX_BSTRC(">>")) {
+      if (key == FX_BSTRC(">>"))
         break;
-      }
+
       if (key == FX_BSTRC("endobj")) {
         m_Pos = SavedPos;
         break;
       }
-      if (key[0] != '/') {
+      if (key[0] != '/')
         continue;
-      }
-      nKeys++;
+
+      ++nKeys;
       key = PDF_NameDecode(key);
-      if (key == FX_BSTRC("/Contents")) {
+      if (key.IsEmpty())
+        continue;
+
+      CFX_ByteStringC keyNoSlash(key.c_str() + 1, key.GetLength() - 1);
+      if (keyNoSlash.IsEmpty())
+        continue;
+
+      if (key == FX_BSTRC("/Contents"))
         dwSignValuePos = m_Pos;
-      }
+
       CPDF_Object* pObj = GetObject(pObjList, objnum, gennum);
-      if (pObj == NULL) {
+      if (!pObj)
         continue;
-      }
-      if (key.GetLength() >= 1) {
-        if (nKeys < 32) {
-          pDict->SetAt(CFX_ByteStringC(key.c_str() + 1, key.GetLength() - 1),
-                       pObj);
-        } else {
-          pDict->AddValue(CFX_ByteStringC(key.c_str() + 1, key.GetLength() - 1),
-                          pObj);
-        }
+
+      // TODO(thestig): Remove this conditional once CPDF_Dictionary has a
+      // better underlying map implementation.
+      if (nKeys < 32) {
+        pDict->SetAt(keyNoSlash, pObj);
+      } else {
+        pDict->AddValue(keyNoSlash, pObj);
       }
     }
-    if (IsSignatureDict(pDict)) {
+
+    if (IsSignatureDict(pDict.get())) {
       FX_FILESIZE dwSavePos = m_Pos;
       m_Pos = dwSignValuePos;
       CPDF_Object* pObj = GetObject(pObjList, objnum, gennum, NULL, FALSE);
@@ -2224,34 +2141,29 @@ CPDF_Object* CPDF_SyntaxParser::GetObject(CPDF_IndirectObjects* pObjList,
     if (pContext) {
       pContext->m_DictEnd = m_Pos;
       if (pContext->m_Flags & PDFPARSE_NOSTREAM) {
-        return pDict;
+        return pDict.release();
       }
     }
     FX_FILESIZE SavedPos = m_Pos;
     FX_BOOL bIsNumber;
     CFX_ByteString nextword = GetNextWord(bIsNumber);
-    if (nextword == FX_BSTRC("stream")) {
-      CPDF_Stream* pStream = ReadStream(pDict, pContext, objnum, gennum);
-      if (pStream) {
-        return pStream;
-      }
-      if (pDict)
-        pDict->Release();
-      return NULL;
-    } else {
+    if (nextword != FX_BSTRC("stream")) {
       m_Pos = SavedPos;
-      return pDict;
+      return pDict.release();
     }
+
+    return ReadStream(pDict.release(), pContext, objnum, gennum);
   }
   if (word == FX_BSTRC(">>")) {
     m_Pos = SavedPos;
-    return NULL;
+    return nullptr;
   }
-  if (bTypeOnly) {
+  if (bTypeOnly)
     return (CPDF_Object*)PDFOBJ_INVALID;
-  }
-  return NULL;
+
+  return nullptr;
 }
+
 CPDF_Object* CPDF_SyntaxParser::GetObjectByStrict(
     CPDF_IndirectObjects* pObjList,
     FX_DWORD objnum,
@@ -2266,10 +2178,9 @@ CPDF_Object* CPDF_SyntaxParser::GetObjectByStrict(
   FX_BOOL bIsNumber;
   CFX_ByteString word = GetNextWord(bIsNumber);
   if (word.GetLength() == 0) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_INVALID;
-    }
-    return NULL;
+    return nullptr;
   }
   if (bIsNumber) {
     FX_FILESIZE SavedPos = m_Pos;
@@ -2277,248 +2188,274 @@ CPDF_Object* CPDF_SyntaxParser::GetObjectByStrict(
     if (bIsNumber) {
       CFX_ByteString nextword2 = GetNextWord(bIsNumber);
       if (nextword2 == FX_BSTRC("R")) {
-        if (bTypeOnly) {
+        if (bTypeOnly)
           return (CPDF_Object*)PDFOBJ_REFERENCE;
-        }
         FX_DWORD objnum = FXSYS_atoi(word);
         return new CPDF_Reference(pObjList, objnum);
       }
     }
     m_Pos = SavedPos;
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_NUMBER;
-    }
     return CPDF_Number::Create(word);
   }
   if (word == FX_BSTRC("true") || word == FX_BSTRC("false")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_BOOLEAN;
-    }
     return CPDF_Boolean::Create(word == FX_BSTRC("true"));
   }
   if (word == FX_BSTRC("null")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_NULL;
-    }
     return CPDF_Null::Create();
   }
   if (word == FX_BSTRC("(")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_STRING;
-    }
     CFX_ByteString str = ReadString();
-    if (m_pCryptoHandler) {
+    if (m_pCryptoHandler)
       m_pCryptoHandler->Decrypt(objnum, gennum, str);
-    }
     return CPDF_String::Create(str, FALSE);
   }
   if (word == FX_BSTRC("<")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_STRING;
-    }
     CFX_ByteString str = ReadHexString();
-    if (m_pCryptoHandler) {
+    if (m_pCryptoHandler)
       m_pCryptoHandler->Decrypt(objnum, gennum, str);
-    }
     return CPDF_String::Create(str, TRUE);
   }
   if (word == FX_BSTRC("[")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_ARRAY;
-    }
-    CPDF_Array* pArray = CPDF_Array::Create();
-    while (1) {
-      CPDF_Object* pObj = GetObject(pObjList, objnum, gennum);
-      if (pObj == NULL) {
-        if (m_WordBuffer[0] == ']') {
-          return pArray;
-        }
-        if (pArray) {
-          pArray->Release();
-        }
-        return NULL;
-      }
+    nonstd::unique_ptr<CPDF_Array, ReleaseDeleter<CPDF_Array>> pArray(
+        CPDF_Array::Create());
+    while (CPDF_Object* pObj = GetObject(pObjList, objnum, gennum))
       pArray->Add(pObj);
-    }
+    return m_WordBuffer[0] == ']' ? pArray.release() : nullptr;
   }
   if (word[0] == '/') {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_NAME;
-    }
     return CPDF_Name::Create(
         PDF_NameDecode(CFX_ByteStringC(m_WordBuffer + 1, m_WordSize - 1)));
   }
   if (word == FX_BSTRC("<<")) {
-    if (bTypeOnly) {
+    if (bTypeOnly)
       return (CPDF_Object*)PDFOBJ_DICTIONARY;
-    }
-    if (pContext) {
+    if (pContext)
       pContext->m_DictStart = SavedPos;
-    }
-    CPDF_Dictionary* pDict = CPDF_Dictionary::Create();
+
+    nonstd::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>> pDict(
+        CPDF_Dictionary::Create());
     while (1) {
       FX_BOOL bIsNumber;
       FX_FILESIZE SavedPos = m_Pos;
       CFX_ByteString key = GetNextWord(bIsNumber);
-      if (key.IsEmpty()) {
-        if (pDict) {
-          pDict->Release();
-        }
-        return NULL;
-      }
-      if (key == FX_BSTRC(">>")) {
+      if (key.IsEmpty())
+        return nullptr;
+
+      if (key == FX_BSTRC(">>"))
         break;
-      }
+
       if (key == FX_BSTRC("endobj")) {
         m_Pos = SavedPos;
         break;
       }
-      if (key[0] != '/') {
+      if (key[0] != '/')
         continue;
-      }
+
       key = PDF_NameDecode(key);
-      CPDF_Object* pObj = GetObject(pObjList, objnum, gennum);
-      if (pObj == NULL) {
-        if (pDict) {
-          pDict->Release();
-        }
+      nonstd::unique_ptr<CPDF_Object, ReleaseDeleter<CPDF_Object>> obj(
+          GetObject(pObjList, objnum, gennum));
+      if (!obj) {
         uint8_t ch;
-        while (1) {
-          if (!GetNextChar(ch)) {
-            break;
-          }
-          if (ch == 0x0A || ch == 0x0D) {
-            break;
-          }
+        while (GetNextChar(ch) && ch != 0x0A && ch != 0x0D) {
         }
-        return NULL;
+        return nullptr;
       }
       if (key.GetLength() > 1) {
         pDict->AddValue(CFX_ByteStringC(key.c_str() + 1, key.GetLength() - 1),
-                        pObj);
+                        obj.release());
       }
     }
     if (pContext) {
       pContext->m_DictEnd = m_Pos;
       if (pContext->m_Flags & PDFPARSE_NOSTREAM) {
-        return pDict;
+        return pDict.release();
       }
     }
     FX_FILESIZE SavedPos = m_Pos;
     FX_BOOL bIsNumber;
     CFX_ByteString nextword = GetNextWord(bIsNumber);
-    if (nextword == FX_BSTRC("stream")) {
-      CPDF_Stream* pStream = ReadStream(pDict, pContext, objnum, gennum);
-      if (pStream) {
-        return pStream;
-      }
-      if (pDict) {
-        pDict->Release();
-      }
-      return NULL;
-    } else {
+    if (nextword != FX_BSTRC("stream")) {
       m_Pos = SavedPos;
-      return pDict;
+      return pDict.release();
     }
+
+    return ReadStream(pDict.release(), pContext, objnum, gennum);
   }
   if (word == FX_BSTRC(">>")) {
     m_Pos = SavedPos;
-    return NULL;
+    return nullptr;
   }
-  if (bTypeOnly) {
+  if (bTypeOnly)
     return (CPDF_Object*)PDFOBJ_INVALID;
-  }
-  return NULL;
+  return nullptr;
+}
+
+unsigned int CPDF_SyntaxParser::ReadEOLMarkers(FX_FILESIZE pos) {
+  unsigned char byte1 = 0;
+  unsigned char byte2 = 0;
+  GetCharAt(pos, byte1);
+  GetCharAt(pos + 1, byte2);
+  unsigned int markers = 0;
+  if (byte1 == '\r' && byte2 == '\n') {
+    markers = 2;
+  } else if (byte1 == '\r' || byte1 == '\n') {
+    markers = 1;
+  }
+  return markers;
 }
 CPDF_Stream* CPDF_SyntaxParser::ReadStream(CPDF_Dictionary* pDict,
                                            PARSE_CONTEXT* pContext,
                                            FX_DWORD objnum,
                                            FX_DWORD gennum) {
   CPDF_Object* pLenObj = pDict->GetElement(FX_BSTRC("Length"));
-  FX_FILESIZE len = 0;
-  if (pLenObj && ((pLenObj->GetType() != PDFOBJ_REFERENCE) ||
-                  ((((CPDF_Reference*)pLenObj)->GetObjList() != NULL) &&
-                   ((CPDF_Reference*)pLenObj)->GetRefObjNum() != objnum))) {
+  FX_FILESIZE len = -1;
+  CPDF_Reference* pLenObjRef = ToReference(pLenObj);
+
+  bool differingObjNum = !pLenObjRef || (pLenObjRef->GetObjList() &&
+                                         pLenObjRef->GetRefObjNum() != objnum);
+  if (pLenObj && differingObjNum)
     len = pLenObj->GetInteger();
-  }
 
+  // Locate the start of stream.
   ToNextLine();
-  FX_FILESIZE StreamStartPos = m_Pos;
+  FX_FILESIZE streamStartPos = m_Pos;
   if (pContext) {
-    pContext->m_DataStart = m_Pos;
+    pContext->m_DataStart = streamStartPos;
   }
-
+  const unsigned int ENDSTREAM_LEN = sizeof("endstream") - 1;
+  const unsigned int ENDOBJ_LEN = sizeof("endobj") - 1;
   CPDF_CryptoHandler* pCryptoHandler =
-      objnum == (FX_DWORD)m_MetadataObjnum ? NULL : m_pCryptoHandler;
-  if (pCryptoHandler == NULL) {
-    pdfium::base::CheckedNumeric<FX_FILESIZE> pos = m_Pos;
-    pos += len;
-    if (pos.IsValid() && pos.ValueOrDie() < m_FileLen) {
-      m_Pos = pos.ValueOrDie();
-    }
-    GetNextWord();
-    if (m_WordSize < 9 || FXSYS_memcmp(m_WordBuffer, "endstream", 9)) {
-      m_Pos = StreamStartPos;
-      FX_FILESIZE offset = FindTag(FX_BSTRC("endstream"), 0);
-      if (offset >= 0) {
-        FX_FILESIZE curPos = m_Pos;
-        m_Pos = StreamStartPos;
-        FX_FILESIZE endobjOffset = FindTag(FX_BSTRC("endobj"), 0);
-        if (endobjOffset < offset && endobjOffset >= 0) {
-          offset = endobjOffset;
-        } else {
-          m_Pos = curPos;
+      objnum == (FX_DWORD)m_MetadataObjnum ? nullptr : m_pCryptoHandler.get();
+  if (!pCryptoHandler) {
+    FX_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();
+      }
+      m_Pos += ReadEOLMarkers(m_Pos);
+      FXSYS_memset(m_WordBuffer, 0, ENDSTREAM_LEN + 1);
+      GetNextWord();
+      if (FXSYS_memcmp(m_WordBuffer, "endstream", ENDSTREAM_LEN) == 0 &&
+          IsWholeWord(m_Pos - ENDSTREAM_LEN, m_FileLen,
+                      FX_BSTRC("endstream").GetPtr(), ENDSTREAM_LEN, TRUE)) {
+        bSearchForKeyword = FALSE;
+      }
+    }
+    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(FX_BSTRC("endstream"), 0);
+        if (endStreamOffset < 0) {
+          // Can't find any "endstream".
+          break;
+        }
+        if (IsWholeWord(m_Pos - ENDSTREAM_LEN, m_FileLen,
+                        FX_BSTRC("endstream").GetPtr(), ENDSTREAM_LEN, TRUE)) {
+          // Stop searching when the keyword "endstream" is found.
+          endStreamOffset = m_Pos - streamStartPos - ENDSTREAM_LEN;
+          break;
+        }
+      }
+      m_Pos = streamStartPos;
+      FX_FILESIZE endObjOffset = 0;
+      while (endObjOffset >= 0) {
+        endObjOffset = FindTag(FX_BSTRC("endobj"), 0);
+        if (endObjOffset < 0) {
+          // Can't find any "endobj".
+          break;
         }
-        uint8_t byte1, byte2;
-        GetCharAt(StreamStartPos + offset - 1, byte1);
-        GetCharAt(StreamStartPos + offset - 2, byte2);
-        if (byte1 == 0x0a && byte2 == 0x0d) {
-          len -= 2;
-        } else if (byte1 == 0x0a || byte1 == 0x0d) {
-          len--;
+        if (IsWholeWord(m_Pos - ENDOBJ_LEN, m_FileLen,
+                        FX_BSTRC("endobj").GetPtr(), ENDOBJ_LEN, TRUE)) {
+          // Stop searching when the keyword "endobj" is found.
+          endObjOffset = m_Pos - streamStartPos - ENDOBJ_LEN;
+          break;
         }
-        len = (FX_DWORD)offset;
-        pDict->SetAtInteger(FX_BSTRC("Length"), len);
+      }
+      if (endStreamOffset < 0 && endObjOffset < 0) {
+        // Can't find "endstream" or "endobj".
+        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 {
-        m_Pos = StreamStartPos;
-        if (FindTag(FX_BSTRC("endobj"), 0) < 0) {
-          return NULL;
+        numMarkers = ReadEOLMarkers(streamStartPos + endStreamOffset - 1);
+        if (numMarkers == 1) {
+          len -= 1;
         }
       }
-    }
-    m_Pos = StreamStartPos;
-  }
-  CPDF_Stream* pStream;
-  uint8_t* pData = FX_Alloc(uint8_t, len);
-  ReadBlock(pData, len);
-  if (pCryptoHandler) {
-    CFX_BinaryBuf dest_buf;
-    dest_buf.EstimateSize(pCryptoHandler->DecryptGetSize(len));
-    void* context = pCryptoHandler->DecryptStart(objnum, gennum);
-    pCryptoHandler->DecryptStream(context, pData, len, dest_buf);
-    pCryptoHandler->DecryptFinish(context, dest_buf);
-    FX_Free(pData);
-    pData = dest_buf.GetBuffer();
-    len = dest_buf.GetSize();
-    dest_buf.DetachBuffer();
-  }
-  pStream = new CPDF_Stream(pData, len, pDict);
+      if (len < 0) {
+        return nullptr;
+      }
+      pDict->SetAtInteger(FX_BSTRC("Length"), len);
+    }
+    m_Pos = streamStartPos;
+  }
+  if (len < 0) {
+    return nullptr;
+  }
+  uint8_t* pData = nullptr;
+  if (len > 0) {
+    pData = FX_Alloc(uint8_t, len);
+    ReadBlock(pData, len);
+    if (pCryptoHandler) {
+      CFX_BinaryBuf dest_buf;
+      dest_buf.EstimateSize(pCryptoHandler->DecryptGetSize(len));
+      void* context = pCryptoHandler->DecryptStart(objnum, gennum);
+      pCryptoHandler->DecryptStream(context, pData, len, dest_buf);
+      pCryptoHandler->DecryptFinish(context, dest_buf);
+      FX_Free(pData);
+      pData = dest_buf.GetBuffer();
+      len = dest_buf.GetSize();
+      dest_buf.DetachBuffer();
+    }
+  }
+  CPDF_Stream* pStream = new CPDF_Stream(pData, len, pDict);
   if (pContext) {
     pContext->m_DataEnd = pContext->m_DataStart + len;
   }
-  StreamStartPos = m_Pos;
+  streamStartPos = m_Pos;
+  FXSYS_memset(m_WordBuffer, 0, ENDOBJ_LEN + 1);
   GetNextWord();
-  if (m_WordSize == 6 && 0 == FXSYS_memcmp(m_WordBuffer, "endobj", 6)) {
-    m_Pos = StreamStartPos;
+  int numMarkers = ReadEOLMarkers(m_Pos);
+  if (m_WordSize == ENDOBJ_LEN && numMarkers != 0 &&
+      FXSYS_memcmp(m_WordBuffer, "endobj", ENDOBJ_LEN) == 0) {
+    m_Pos = streamStartPos;
   }
   return pStream;
 }
 void CPDF_SyntaxParser::InitParser(IFX_FileRead* pFileAccess,
                                    FX_DWORD HeaderOffset) {
-  if (m_pFileBuf) {
-    FX_Free(m_pFileBuf);
-    m_pFileBuf = NULL;
-  }
+  FX_Free(m_pFileBuf);
   m_pFileBuf = FX_Alloc(uint8_t, m_BufSize);
   m_HeaderOffset = HeaderOffset;
   m_FileLen = pFileAccess->GetSize();
@@ -2540,22 +2477,23 @@ int32_t CPDF_SyntaxParser::GetDirectNum() {
 FX_BOOL CPDF_SyntaxParser::IsWholeWord(FX_FILESIZE startpos,
                                        FX_FILESIZE limit,
                                        const uint8_t* tag,
-                                       FX_DWORD taglen) {
-  uint8_t type = PDF_CharType[tag[0]];
-  FX_BOOL bCheckLeft = type != 'D' && type != 'W';
-  type = PDF_CharType[tag[taglen - 1]];
-  FX_BOOL bCheckRight = type != 'D' && type != 'W';
+                                       FX_DWORD taglen,
+                                       FX_BOOL checkKeyword) {
+  bool bCheckLeft = !PDFCharIsDelimiter(tag[0]) && !PDFCharIsWhitespace(tag[0]);
+  bool bCheckRight = !PDFCharIsDelimiter(tag[taglen - 1]) &&
+                     !PDFCharIsWhitespace(tag[taglen - 1]);
   uint8_t ch;
   if (bCheckRight && startpos + (int32_t)taglen <= limit &&
       GetCharAt(startpos + (int32_t)taglen, ch)) {
-    uint8_t type = PDF_CharType[ch];
-    if (type == 'N' || type == 'R') {
+    if (PDFCharIsNumeric(ch) || PDFCharIsOther(ch) ||
+        (checkKeyword && PDFCharIsDelimiter(ch))) {
       return FALSE;
     }
   }
+
   if (bCheckLeft && startpos > 0 && GetCharAt(startpos - 1, ch)) {
-    uint8_t type = PDF_CharType[ch];
-    if (type == 'N' || type == 'R') {
+    if (PDFCharIsNumeric(ch) || PDFCharIsOther(ch) ||
+        (checkKeyword && PDFCharIsDelimiter(ch))) {
       return FALSE;
     }
   }
@@ -2611,7 +2549,8 @@ FX_BOOL CPDF_SyntaxParser::SearchWord(const CFX_ByteStringC& tag,
         }
       }
       FX_FILESIZE startpos = bForward ? pos - taglen + 1 : pos;
-      if (!bWholeWord || IsWholeWord(startpos, limit, tag.GetPtr(), taglen)) {
+      if (!bWholeWord ||
+          IsWholeWord(startpos, limit, tag.GetPtr(), taglen, FALSE)) {
         m_Pos = startpos;
         return TRUE;
       }
@@ -2629,76 +2568,61 @@ FX_BOOL CPDF_SyntaxParser::SearchWord(const CFX_ByteStringC& tag,
   }
   return FALSE;
 }
-struct _SearchTagRecord {
-  const uint8_t* m_pTag;
-  FX_DWORD m_Len;
-  FX_DWORD m_Offset;
-};
+
 int32_t CPDF_SyntaxParser::SearchMultiWord(const CFX_ByteStringC& tags,
                                            FX_BOOL bWholeWord,
                                            FX_FILESIZE limit) {
-  int32_t ntags = 1, i;
-  for (i = 0; i < tags.GetLength(); i++)
+  int32_t ntags = 1;
+  for (int i = 0; i < tags.GetLength(); ++i) {
     if (tags[i] == 0) {
-      ntags++;
+      ++ntags;
     }
-  _SearchTagRecord* pPatterns = FX_Alloc(_SearchTagRecord, ntags);
-  FX_DWORD start = 0, itag = 0, max_len = 0;
-  for (i = 0; i <= tags.GetLength(); i++) {
+  }
+
+  std::vector<SearchTagRecord> patterns(ntags);
+  FX_DWORD start = 0;
+  FX_DWORD itag = 0;
+  FX_DWORD max_len = 0;
+  for (int i = 0; i <= tags.GetLength(); ++i) {
     if (tags[i] == 0) {
       FX_DWORD len = i - start;
-      if (len > max_len) {
-        max_len = len;
-      }
-      pPatterns[itag].m_pTag = tags.GetPtr() + start;
-      pPatterns[itag].m_Len = len;
-      pPatterns[itag].m_Offset = 0;
+      max_len = std::max(len, max_len);
+      patterns[itag].m_pTag = tags.GetPtr() + start;
+      patterns[itag].m_Len = len;
+      patterns[itag].m_Offset = 0;
       start = i + 1;
-      itag++;
+      ++itag;
     }
   }
-  FX_FILESIZE pos = m_Pos;
-  uint8_t byte;
-  GetCharAt(pos++, byte);
-  int32_t found = -1;
-  while (1) {
-    for (i = 0; i < ntags; i++) {
-      if (pPatterns[i].m_pTag[pPatterns[i].m_Offset] == byte) {
-        pPatterns[i].m_Offset++;
-        if (pPatterns[i].m_Offset == pPatterns[i].m_Len) {
-          if (!bWholeWord ||
-              IsWholeWord(pos - pPatterns[i].m_Len, limit, pPatterns[i].m_pTag,
-                          pPatterns[i].m_Len)) {
-            found = i;
-            goto end;
-          } else {
-            if (pPatterns[i].m_pTag[0] == byte) {
-              pPatterns[i].m_Offset = 1;
-            } else {
-              pPatterns[i].m_Offset = 0;
-            }
-          }
-        }
-      } else {
-        if (pPatterns[i].m_pTag[0] == byte) {
-          pPatterns[i].m_Offset = 1;
-        } else {
-          pPatterns[i].m_Offset = 0;
-        }
+
+  const FX_FILESIZE pos_limit = m_Pos + limit;
+  for (FX_FILESIZE pos = m_Pos; !limit || pos < pos_limit; ++pos) {
+    uint8_t byte;
+    if (!GetCharAt(pos, byte))
+      break;
+
+    for (int i = 0; i < ntags; ++i) {
+      SearchTagRecord& pat = patterns[i];
+      if (pat.m_pTag[pat.m_Offset] != byte) {
+        pat.m_Offset = (pat.m_pTag[0] == byte) ? 1 : 0;
+        continue;
       }
+
+      ++pat.m_Offset;
+      if (pat.m_Offset != pat.m_Len)
+        continue;
+
+      if (!bWholeWord ||
+          IsWholeWord(pos - pat.m_Len, limit, pat.m_pTag, pat.m_Len, FALSE)) {
+        return i;
+      }
+
+      pat.m_Offset = (pat.m_pTag[0] == byte) ? 1 : 0;
     }
-    if (limit && pos >= m_Pos + limit) {
-      goto end;
-    }
-    if (!GetCharAt(pos, byte)) {
-      goto end;
-    }
-    pos++;
   }
-end:
-  FX_Free(pPatterns);
-  return found;
+  return -1;
 }
+
 FX_FILESIZE CPDF_SyntaxParser::FindTag(const CFX_ByteStringC& tag,
                                        FX_FILESIZE limit) {
   int32_t taglen = tag.GetLength();
@@ -2741,26 +2665,26 @@ void CPDF_SyntaxParser::GetBinary(uint8_t* buffer, FX_DWORD size) {
 class CPDF_DataAvail final : public IPDF_DataAvail {
  public:
   CPDF_DataAvail(IFX_FileAvail* pFileAvail, IFX_FileRead* pFileRead);
-  ~CPDF_DataAvail();
+  ~CPDF_DataAvail() override;
 
-  virtual FX_BOOL IsDocAvail(IFX_DownloadHints* pHints) override;
+  FX_BOOL IsDocAvail(IFX_DownloadHints* pHints) override;
 
-  virtual void SetDocument(CPDF_Document* pDoc) override;
+  void SetDocument(CPDF_Document* pDoc) override;
 
-  virtual FX_BOOL IsPageAvail(int iPage, IFX_DownloadHints* pHints) override;
+  FX_BOOL IsPageAvail(int iPage, IFX_DownloadHints* pHints) override;
 
-  virtual int32_t IsFormAvail(IFX_DownloadHints* pHints) override;
+  int32_t IsFormAvail(IFX_DownloadHints* pHints) override;
 
-  virtual int32_t IsLinearizedPDF() override;
+  int32_t IsLinearizedPDF() override;
 
-  virtual FX_BOOL IsLinearized() override { return m_bLinearized; }
+  FX_BOOL IsLinearized() override { return m_bLinearized; }
 
-  virtual void GetLinearizedMainXRefInfo(FX_FILESIZE* pPos,
-                                         FX_DWORD* pSize) override;
+  void GetLinearizedMainXRefInfo(FX_FILESIZE* pPos, FX_DWORD* pSize) override;
 
  protected:
   static const int kMaxDataAvailRecursionDepth = 64;
   static int s_CurrentDataAvailRecursionDepth;
+  static const int kMaxPageRecursionDepth = 1024;
 
   FX_DWORD GetObjectSize(FX_DWORD objnum, FX_FILESIZE& offset);
   FX_BOOL IsObjectsAvail(CFX_PtrArray& obj_array,
@@ -2813,7 +2737,8 @@ class CPDF_DataAvail final : public IPDF_DataAvail {
   FX_BOOL CheckPageNode(CPDF_PageNode& pageNodes,
                         int32_t iPage,
                         int32_t& iCount,
-                        IFX_DownloadHints* pHints);
+                        IFX_DownloadHints* pHints,
+                        int level);
   FX_BOOL CheckUnkownPageNode(FX_DWORD dwPageNo,
                               CPDF_PageNode* pPageNode,
                               IFX_DownloadHints* pHints);
@@ -2934,9 +2859,8 @@ class CPDF_DataAvail final : public IPDF_DataAvail {
 
   CPDF_PageNode m_pageNodes;
 
-  CFX_CMapDWordToDWord* m_pageMapCheckState;
-
-  CFX_CMapDWordToDWord* m_pagesLoadState;
+  std::set<FX_DWORD> m_pageMapCheckState;
+  std::set<FX_DWORD> m_pagesLoadState;
 };
 
 IPDF_DataAvail::IPDF_DataAvail(IFX_FileAvail* pFileAvail,
@@ -2994,13 +2918,11 @@ CPDF_DataAvail::CPDF_DataAvail(IFX_FileAvail* pFileAvail,
   m_pAcroForm = NULL;
   m_pPageDict = NULL;
   m_pPageResource = NULL;
-  m_pageMapCheckState = NULL;
   m_docStatus = PDF_DATAAVAIL_HEADER;
   m_parser.m_bOwnFileRead = FALSE;
   m_bTotalLoadPageTree = FALSE;
   m_bCurPageDictLoadOK = FALSE;
   m_bLinearedDataOK = FALSE;
-  m_pagesLoadState = NULL;
 }
 CPDF_DataAvail::~CPDF_DataAvail() {
   if (m_pLinearized) {
@@ -3012,12 +2934,10 @@ CPDF_DataAvail::~CPDF_DataAvail() {
   if (m_pTrailer) {
     m_pTrailer->Release();
   }
-  delete m_pageMapCheckState;
-  delete m_pagesLoadState;
   int32_t i = 0;
   int32_t iSize = m_arrayAcroforms.GetSize();
   for (i = 0; i < iSize; ++i) {
-    ((CPDF_Object*)m_arrayAcroforms.GetAt(i))->Release();
+    static_cast<CPDF_Object*>(m_arrayAcroforms.GetAt(i))->Release();
   }
 }
 void CPDF_DataAvail::SetDocument(CPDF_Document* pDoc) {
@@ -3041,7 +2961,7 @@ FX_DWORD CPDF_DataAvail::GetObjectSize(FX_DWORD objnum, FX_FILESIZE& offset) {
     }
     void* pResult = FXSYS_bsearch(&offset, pParser->m_SortedOffset.GetData(),
                                   pParser->m_SortedOffset.GetSize(),
-                                  sizeof(FX_FILESIZE), _CompareFileSize);
+                                  sizeof(FX_FILESIZE), CompareFileSize);
     if (pResult == NULL) {
       return 0;
     }
@@ -3065,10 +2985,10 @@ FX_BOOL CPDF_DataAvail::IsObjectsAvail(CFX_PtrArray& obj_array,
   CFX_PtrArray new_obj_array;
   int32_t i = 0;
   for (i = 0; i < obj_array.GetSize(); i++) {
-    CPDF_Object* pObj = (CPDF_Object*)obj_array[i];
-    if (!pObj) {
+    CPDF_Object* pObj = static_cast<CPDF_Object*>(obj_array[i]);
+    if (!pObj)
       continue;
-    }
+
     int32_t type = pObj->GetType();
     switch (type) {
       case PDFOBJ_ARRAY: {
@@ -3095,7 +3015,7 @@ FX_BOOL CPDF_DataAvail::IsObjectsAvail(CFX_PtrArray& obj_array,
         }
       } break;
       case PDFOBJ_REFERENCE: {
-        CPDF_Reference* pRef = (CPDF_Reference*)pObj;
+        CPDF_Reference* pRef = pObj->AsReference();
         FX_DWORD dwNum = pRef->GetRefObjNum();
         FX_FILESIZE offset;
         FX_DWORD original_size = GetObjectSize(dwNum, offset);
@@ -3136,14 +3056,11 @@ FX_BOOL CPDF_DataAvail::IsObjectsAvail(CFX_PtrArray& obj_array,
   if (count > 0) {
     int32_t iSize = new_obj_array.GetSize();
     for (i = 0; i < iSize; ++i) {
-      CPDF_Object* pObj = (CPDF_Object*)new_obj_array[i];
-      int32_t type = pObj->GetType();
-      if (type == PDFOBJ_REFERENCE) {
-        CPDF_Reference* pRef = (CPDF_Reference*)pObj;
+      CPDF_Object* pObj = static_cast<CPDF_Object*>(new_obj_array[i]);
+      if (CPDF_Reference* pRef = pObj->AsReference()) {
         FX_DWORD dwNum = pRef->GetRefObjNum();
-        if (!m_objnum_array.Find(dwNum)) {
+        if (!m_objnum_array.Find(dwNum))
           ret_array.Add(pObj);
-        }
       } else {
         ret_array.Add(pObj);
       }
@@ -3185,7 +3102,7 @@ FX_BOOL CPDF_DataAvail::CheckAcroFormSubObject(IFX_DownloadHints* pHints) {
   if (bRet) {
     int32_t iSize = m_arrayAcroforms.GetSize();
     for (int32_t i = 0; i < iSize; ++i) {
-      ((CPDF_Object*)m_arrayAcroforms.GetAt(i))->Release();
+      static_cast<CPDF_Object*>(m_arrayAcroforms.GetAt(i))->Release();
     }
     m_arrayAcroforms.RemoveAll();
   } else {
@@ -3293,7 +3210,7 @@ FX_BOOL CPDF_DataAvail::LoadAllXref(IFX_DownloadHints* pHints) {
   }
   FXSYS_qsort(m_parser.m_SortedOffset.GetData(),
               m_parser.m_SortedOffset.GetSize(), sizeof(FX_FILESIZE),
-              _CompareFileSize);
+              CompareFileSize);
   m_dwRootObjNum = m_parser.GetRootObjNum();
   m_dwInfoObjNum = m_parser.GetInfoObjNum();
   m_pCurrentParser = &m_parser;
@@ -3411,66 +3328,51 @@ FX_BOOL CPDF_DataAvail::CheckRoot(IFX_DownloadHints* pHints) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return FALSE;
   }
-  CPDF_Reference* pRef = (CPDF_Reference*)pDict->GetElement(FX_BSTRC("Pages"));
-  if (pRef == NULL || pRef->GetType() != PDFOBJ_REFERENCE) {
+  CPDF_Reference* pRef = ToReference(pDict->GetElement(FX_BSTRC("Pages")));
+  if (!pRef) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return FALSE;
   }
+
   m_PagesObjNum = pRef->GetRefObjNum();
   CPDF_Reference* pAcroFormRef =
-      (CPDF_Reference*)m_pRoot->GetDict()->GetElement(FX_BSTRC("AcroForm"));
-  if (pAcroFormRef && pAcroFormRef->GetType() == PDFOBJ_REFERENCE) {
+      ToReference(m_pRoot->GetDict()->GetElement(FX_BSTRC("AcroForm")));
+  if (pAcroFormRef) {
     m_bHaveAcroForm = TRUE;
     m_dwAcroFormObjNum = pAcroFormRef->GetRefObjNum();
   }
+
   if (m_dwInfoObjNum) {
     m_docStatus = PDF_DATAAVAIL_INFO;
   } else {
-    if (m_bHaveAcroForm) {
-      m_docStatus = PDF_DATAAVAIL_ACROFORM;
-    } else {
-      m_docStatus = PDF_DATAAVAIL_PAGETREE;
-    }
+    m_docStatus =
+        m_bHaveAcroForm ? PDF_DATAAVAIL_ACROFORM : PDF_DATAAVAIL_PAGETREE;
   }
   return TRUE;
 }
 FX_BOOL CPDF_DataAvail::PreparePageItem() {
   CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
   CPDF_Reference* pRef =
-      pRoot ? (CPDF_Reference*)pRoot->GetElement(FX_BSTRC("Pages")) : NULL;
-  if (pRef == NULL || pRef->GetType() != PDFOBJ_REFERENCE) {
+      ToReference(pRoot ? pRoot->GetElement(FX_BSTRC("Pages")) : nullptr);
+  if (!pRef) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return FALSE;
   }
+
   m_PagesObjNum = pRef->GetRefObjNum();
   m_pCurrentParser = (CPDF_Parser*)m_pDocument->GetParser();
   m_docStatus = PDF_DATAAVAIL_PAGETREE;
   return TRUE;
 }
 FX_BOOL CPDF_DataAvail::IsFirstCheck(int iPage) {
-  if (NULL == m_pageMapCheckState) {
-    m_pageMapCheckState = new CFX_CMapDWordToDWord();
-  }
-  FX_DWORD dwValue = 0;
-  if (!m_pageMapCheckState->Lookup(iPage, dwValue)) {
-    m_pageMapCheckState->SetAt(iPage, 1);
-    return TRUE;
-  }
-  if (dwValue != 0) {
+  if (m_pageMapCheckState.find(iPage) != m_pageMapCheckState.end())
     return FALSE;
-  }
-  m_pageMapCheckState->SetAt(iPage, 1);
+
+  m_pageMapCheckState.insert(iPage);
   return TRUE;
 }
 void CPDF_DataAvail::ResetFirstCheck(int iPage) {
-  if (NULL == m_pageMapCheckState) {
-    m_pageMapCheckState = new CFX_CMapDWordToDWord();
-  }
-  FX_DWORD dwValue = 1;
-  if (!m_pageMapCheckState->Lookup(iPage, dwValue)) {
-    return;
-  }
-  m_pageMapCheckState->SetAt(iPage, 0);
+  m_pageMapCheckState.erase(iPage);
 }
 FX_BOOL CPDF_DataAvail::CheckPage(IFX_DownloadHints* pHints) {
   FX_DWORD iPageObjs = m_PageObjList.GetSize();
@@ -3485,20 +3387,17 @@ FX_BOOL CPDF_DataAvail::CheckPage(IFX_DownloadHints* pHints) {
       }
       continue;
     }
-    if (pObj->GetType() == PDFOBJ_ARRAY) {
+    if (pObj->IsArray()) {
       CPDF_Array* pArray = pObj->GetArray();
       if (pArray) {
         int32_t iSize = pArray->GetCount();
-        CPDF_Object* pItem = NULL;
         for (int32_t j = 0; j < iSize; ++j) {
-          pItem = pArray->GetElement(j);
-          if (pItem && pItem->GetType() == PDFOBJ_REFERENCE) {
-            UnavailObjList.Add(((CPDF_Reference*)pItem)->GetRefObjNum());
-          }
+          if (CPDF_Reference* pRef = ToReference(pArray->GetElement(j)))
+            UnavailObjList.Add(pRef->GetRefObjNum());
         }
       }
     }
-    if (pObj->GetType() != PDFOBJ_DICTIONARY) {
+    if (!pObj->IsDictionary()) {
       pObj->Release();
       continue;
     }
@@ -3516,14 +3415,14 @@ FX_BOOL CPDF_DataAvail::CheckPage(IFX_DownloadHints* pHints) {
   }
   FX_DWORD iPages = m_PagesArray.GetSize();
   for (FX_DWORD i = 0; i < iPages; i++) {
-    CPDF_Object* pPages = (CPDF_Object*)m_PagesArray.GetAt(i);
-    if (!pPages) {
+    CPDF_Object* pPages = static_cast<CPDF_Object*>(m_PagesArray.GetAt(i));
+    if (!pPages)
       continue;
-    }
+
     if (!GetPageKids(m_pCurrentParser, pPages)) {
       pPages->Release();
       while (++i < iPages) {
-        pPages = (CPDF_Object*)m_PagesArray.GetAt(i);
+        pPages = static_cast<CPDF_Object*>(m_PagesArray.GetAt(i));
         pPages->Release();
       }
       m_PagesArray.RemoveAll();
@@ -3549,17 +3448,14 @@ FX_BOOL CPDF_DataAvail::GetPageKids(CPDF_Parser* pParser, CPDF_Object* pPages) {
     return TRUE;
   }
   switch (pKids->GetType()) {
-    case PDFOBJ_REFERENCE: {
-      CPDF_Reference* pKid = (CPDF_Reference*)pKids;
-      m_PageObjList.Add(pKid->GetRefObjNum());
-    } break;
+    case PDFOBJ_REFERENCE:
+      m_PageObjList.Add(pKids->AsReference()->GetRefObjNum());
+      break;
     case PDFOBJ_ARRAY: {
-      CPDF_Array* pKidsArray = (CPDF_Array*)pKids;
+      CPDF_Array* pKidsArray = pKids->AsArray();
       for (FX_DWORD i = 0; i < pKidsArray->GetCount(); ++i) {
-        CPDF_Object* pKid = (CPDF_Object*)pKidsArray->GetElement(i);
-        if (pKid && pKid->GetType() == PDFOBJ_REFERENCE) {
-          m_PageObjList.Add(((CPDF_Reference*)pKid)->GetRefObjNum());
-        }
+        if (CPDF_Reference* pRef = ToReference(pKidsArray->GetElement(i)))
+          m_PageObjList.Add(pRef->GetRefObjNum());
       }
     } break;
     default:
@@ -3630,7 +3526,7 @@ FX_BOOL CPDF_DataAvail::CheckFirstPage(IFX_DownloadHints* pHints) {
     return FALSE;
   }
   FX_BOOL bNeedDownLoad = FALSE;
-  if (pEndOffSet->GetType() == PDFOBJ_NUMBER) {
+  if (pEndOffSet->IsNumber()) {
     FX_DWORD dwEnd = pEndOffSet->GetInteger();
     dwEnd += 512;
     if ((FX_FILESIZE)dwEnd > m_dwFileLen) {
@@ -3645,12 +3541,12 @@ FX_BOOL CPDF_DataAvail::CheckFirstPage(IFX_DownloadHints* pHints) {
   }
   m_dwLastXRefOffset = 0;
   FX_FILESIZE dwFileLen = 0;
-  if (pXRefOffset->GetType() == PDFOBJ_NUMBER) {
+  if (pXRefOffset->IsNumber())
     m_dwLastXRefOffset = pXRefOffset->GetInteger();
-  }
-  if (pFileLen->GetType() == PDFOBJ_NUMBER) {
+
+  if (pFileLen->IsNumber())
     dwFileLen = pFileLen->GetInteger();
-  }
+
   if (!m_pFileAvail->IsDataAvail(m_dwLastXRefOffset,
                                  (FX_DWORD)(dwFileLen - m_dwLastXRefOffset))) {
     if (m_docStatus == PDF_DATAAVAIL_FIRSTPAGE) {
@@ -3718,15 +3614,14 @@ int32_t CPDF_DataAvail::IsLinearizedPDF() {
   return PDF_NOT_LINEARIZED;
 }
 FX_BOOL CPDF_DataAvail::IsLinearizedFile(uint8_t* pData, FX_DWORD dwLen) {
-  CFX_SmartPointer<IFX_FileStream> file(
-      FX_CreateMemoryStream(pData, (size_t)dwLen, FALSE));
-  int32_t offset = GetHeaderOffset(file.Get());
+  ScopedFileStream file(FX_CreateMemoryStream(pData, (size_t)dwLen, FALSE));
+  int32_t offset = GetHeaderOffset(file.get());
   if (offset == -1) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return FALSE;
   }
   m_dwHeaderOffset = offset;
-  m_syntaxParser.InitParser(file.Get(), offset);
+  m_syntaxParser.InitParser(file.get(), offset);
   m_syntaxParser.RestorePos(m_syntaxParser.m_HeaderOffset + 9);
   FX_BOOL bNumber = FALSE;
   CFX_ByteString wordObjNum = m_syntaxParser.GetNextWord(bNumber);
@@ -3743,9 +3638,10 @@ FX_BOOL CPDF_DataAvail::IsLinearizedFile(uint8_t* pData, FX_DWORD dwLen) {
   if (!m_pLinearized) {
     return FALSE;
   }
-  if (m_pLinearized->GetDict() &&
-      m_pLinearized->GetDict()->GetElement(FX_BSTRC("Linearized"))) {
-    CPDF_Object* pLen = m_pLinearized->GetDict()->GetElement(FX_BSTRC("L"));
+
+  CPDF_Dictionary* pDict = m_pLinearized->GetDict();
+  if (pDict && pDict->GetElement(FX_BSTRC("Linearized"))) {
+    CPDF_Object* pLen = pDict->GetElement(FX_BSTRC("L"));
     if (!pLen) {
       return FALSE;
     }
@@ -3753,10 +3649,10 @@ FX_BOOL CPDF_DataAvail::IsLinearizedFile(uint8_t* pData, FX_DWORD dwLen) {
       return FALSE;
     }
     m_bLinearized = TRUE;
-    CPDF_Object* pNo = m_pLinearized->GetDict()->GetElement(FX_BSTRC("P"));
-    if (pNo && pNo->GetType() == PDFOBJ_NUMBER) {
+
+    if (CPDF_Number* pNo = ToNumber(pDict->GetElement(FX_BSTRC("P"))))
       m_dwFirstPageNo = pNo->GetInteger();
-    }
+
     return TRUE;
   }
   return FALSE;
@@ -3767,9 +3663,8 @@ FX_BOOL CPDF_DataAvail::CheckEnd(IFX_DownloadHints* pHints) {
   if (m_pFileAvail->IsDataAvail(req_pos, dwSize)) {
     uint8_t buffer[1024];
     m_pFileRead->ReadBlock(buffer, req_pos, dwSize);
-    CFX_SmartPointer<IFX_FileStream> file(
-        FX_CreateMemoryStream(buffer, (size_t)dwSize, FALSE));
-    m_syntaxParser.InitParser(file.Get(), 0);
+    ScopedFileStream file(FX_CreateMemoryStream(buffer, (size_t)dwSize, FALSE));
+    m_syntaxParser.InitParser(file.get(), 0);
     m_syntaxParser.RestorePos(dwSize - 1);
     if (m_syntaxParser.SearchWord(FX_BSTRC("startxref"), TRUE, FALSE, dwSize)) {
       FX_BOOL bNumber;
@@ -3805,9 +3700,8 @@ int32_t CPDF_DataAvail::CheckCrossRefStream(IFX_DownloadHints* pHints,
     CFX_BinaryBuf buf(iSize);
     uint8_t* pBuf = buf.GetBuffer();
     m_pFileRead->ReadBlock(pBuf, m_dwCurrentXRefSteam, iSize);
-    CFX_SmartPointer<IFX_FileStream> file(
-        FX_CreateMemoryStream(pBuf, (size_t)iSize, FALSE));
-    m_parser.m_Syntax.InitParser(file.Get(), 0);
+    ScopedFileStream file(FX_CreateMemoryStream(pBuf, (size_t)iSize, FALSE));
+    m_parser.m_Syntax.InitParser(file.get(), 0);
     FX_BOOL bNumber = FALSE;
     CFX_ByteString objnum = m_parser.m_Syntax.GetNextWord(bNumber);
     if (!bNumber) {
@@ -3820,8 +3714,9 @@ int32_t CPDF_DataAvail::CheckCrossRefStream(IFX_DownloadHints* pHints,
       return 0;
     }
     CPDF_Dictionary* pDict = pObj->GetDict();
-    CPDF_Object* pName = pDict ? pDict->GetElement(FX_BSTRC("Type")) : NULL;
-    if (pName && pName->GetType() == PDFOBJ_NAME) {
+    CPDF_Name* pName =
+        ToName(pDict ? pDict->GetElement(FX_BSTRC("Type")) : nullptr);
+    if (pName) {
       if (pName->GetString() == FX_BSTRC("XRef")) {
         m_Pos += m_parser.m_Syntax.SavePos();
         xref_offset = pObj->GetDict()->GetInteger(FX_BSTRC("Prev"));
@@ -3842,84 +3737,79 @@ inline void CPDF_DataAvail::SetStartOffset(FX_FILESIZE dwOffset) {
 FX_BOOL CPDF_DataAvail::GetNextToken(CFX_ByteString& token) {
   m_WordSize = 0;
   uint8_t ch;
-  if (!GetNextChar(ch)) {
+  if (!GetNextChar(ch))
     return FALSE;
-  }
-  uint8_t type = PDF_CharType[ch];
+
   while (1) {
-    while (type == 'W') {
-      if (!GetNextChar(ch)) {
+    while (PDFCharIsWhitespace(ch)) {
+      if (!GetNextChar(ch))
         return FALSE;
-      }
-      type = PDF_CharType[ch];
     }
-    if (ch != '%') {
+
+    if (ch != '%')
       break;
-    }
+
     while (1) {
-      if (!GetNextChar(ch)) {
+      if (!GetNextChar(ch))
         return FALSE;
-      }
-      if (ch == '\r' || ch == '\n') {
+      if (PDFCharIsLineEnding(ch))
         break;
-      }
     }
-    type = PDF_CharType[ch];
   }
-  if (type == 'D') {
+
+  if (PDFCharIsDelimiter(ch)) {
     m_WordBuffer[m_WordSize++] = ch;
     if (ch == '/') {
       while (1) {
-        if (!GetNextChar(ch)) {
+        if (!GetNextChar(ch))
           return FALSE;
-        }
-        type = PDF_CharType[ch];
-        if (type != 'R' && type != 'N') {
+
+        if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) {
           m_Pos--;
           CFX_ByteString ret(m_WordBuffer, m_WordSize);
           token = ret;
           return TRUE;
         }
-        if (m_WordSize < MAX_WORD_BUFFER) {
+
+        if (m_WordSize < MAX_WORD_BUFFER)
           m_WordBuffer[m_WordSize++] = ch;
-        }
       }
     } else if (ch == '<') {
-      if (!GetNextChar(ch)) {
+      if (!GetNextChar(ch))
         return FALSE;
-      }
-      if (ch == '<') {
+
+      if (ch == '<')
         m_WordBuffer[m_WordSize++] = ch;
-      } else {
+      else
         m_Pos--;
-      }
     } else if (ch == '>') {
-      if (!GetNextChar(ch)) {
+      if (!GetNextChar(ch))
         return FALSE;
-      }
-      if (ch == '>') {
+
+      if (ch == '>')
         m_WordBuffer[m_WordSize++] = ch;
-      } else {
+      else
         m_Pos--;
-      }
     }
+
     CFX_ByteString ret(m_WordBuffer, m_WordSize);
     token = ret;
     return TRUE;
   }
+
   while (1) {
-    if (m_WordSize < MAX_WORD_BUFFER) {
+    if (m_WordSize < MAX_WORD_BUFFER)
       m_WordBuffer[m_WordSize++] = ch;
-    }
-    if (!GetNextChar(ch)) {
+
+    if (!GetNextChar(ch))
       return FALSE;
-    }
-    type = PDF_CharType[ch];
-    if (type == 'D' || type == 'W') {
+
+    if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) {
       m_Pos--;
       break;
     }
   }
+
   CFX_ByteString ret(m_WordBuffer, m_WordSize);
   token = ret;
   return TRUE;
@@ -4030,6 +3920,7 @@ FX_BOOL CPDF_DataAvail::CheckTrailerAppend(IFX_DownloadHints* pHints) {
   }
   return TRUE;
 }
+
 FX_BOOL CPDF_DataAvail::CheckTrailer(IFX_DownloadHints* pHints) {
   int32_t iTrailerSize =
       (int32_t)(m_Pos + 512 > m_dwFileLen ? m_dwFileLen - m_Pos : 512);
@@ -4044,32 +3935,28 @@ FX_BOOL CPDF_DataAvail::CheckTrailer(IFX_DownloadHints* pHints) {
     if (!m_pFileRead->ReadBlock(pBuf, m_dwTrailerOffset, iSize)) {
       return FALSE;
     }
-    CFX_SmartPointer<IFX_FileStream> file(
-        FX_CreateMemoryStream(pBuf, (size_t)iSize, FALSE));
-    m_syntaxParser.InitParser(file.Get(), 0);
-    CPDF_Object* pTrailer = m_syntaxParser.GetObject(NULL, 0, 0, 0);
+    ScopedFileStream file(FX_CreateMemoryStream(pBuf, (size_t)iSize, FALSE));
+    m_syntaxParser.InitParser(file.get(), 0);
+    nonstd::unique_ptr<CPDF_Object, ReleaseDeleter<CPDF_Object>> pTrailer(
+        m_syntaxParser.GetObject(nullptr, 0, 0));
     if (!pTrailer) {
       m_Pos += m_syntaxParser.SavePos();
       pHints->AddSegment(m_Pos, iTrailerSize);
       return FALSE;
     }
-    if (pTrailer->GetType() != PDFOBJ_DICTIONARY) {
+    if (!pTrailer->IsDictionary())
       return FALSE;
-    }
+
     CPDF_Dictionary* pTrailerDict = pTrailer->GetDict();
-    if (pTrailerDict) {
-      CPDF_Object* pEncrypt = pTrailerDict->GetElement("Encrypt");
-      if (pEncrypt && pEncrypt->GetType() == PDFOBJ_REFERENCE) {
-        m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
-        pTrailer->Release();
-        return TRUE;
-      }
+    CPDF_Object* pEncrypt = pTrailerDict->GetElement("Encrypt");
+    if (ToReference(pEncrypt)) {
+      m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
+      return TRUE;
     }
-    FX_DWORD xrefpos = GetDirectInteger(pTrailer->GetDict(), FX_BSTRC("Prev"));
+
+    FX_DWORD xrefpos = GetDirectInteger(pTrailerDict, FX_BSTRC("Prev"));
     if (xrefpos) {
-      m_dwPrevXRefOffset =
-          GetDirectInteger(pTrailer->GetDict(), FX_BSTRC("XRefStm"));
-      pTrailer->Release();
+      m_dwPrevXRefOffset = GetDirectInteger(pTrailerDict, FX_BSTRC("XRefStm"));
       if (m_dwPrevXRefOffset) {
         m_docStatus = PDF_DATAAVAIL_LOADALLFILE;
       } else {
@@ -4085,12 +3972,12 @@ FX_BOOL CPDF_DataAvail::CheckTrailer(IFX_DownloadHints* pHints) {
     }
     m_dwPrevXRefOffset = 0;
     m_docStatus = PDF_DATAAVAIL_TRAILER_APPEND;
-    pTrailer->Release();
     return TRUE;
   }
   pHints->AddSegment(m_Pos, iTrailerSize);
   return FALSE;
 }
+
 FX_BOOL CPDF_DataAvail::CheckPage(int32_t iPage, IFX_DownloadHints* pHints) {
   while (TRUE) {
     switch (m_docStatus) {
@@ -4131,21 +4018,23 @@ FX_BOOL CPDF_DataAvail::CheckArrayPageNode(FX_DWORD dwPageNo,
     }
     return FALSE;
   }
-  if (pPages->GetType() != PDFOBJ_ARRAY) {
+
+  CPDF_Array* pArray = pPages->AsArray();
+  if (!pArray) {
     pPages->Release();
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return FALSE;
   }
+
   pPageNode->m_type = PDF_PAGENODE_PAGES;
-  CPDF_Array* pArray = (CPDF_Array*)pPages;
   for (FX_DWORD i = 0; i < pArray->GetCount(); ++i) {
-    CPDF_Object* pKid = (CPDF_Object*)pArray->GetElement(i);
-    if (!pKid || pKid->GetType() != PDFOBJ_REFERENCE) {
+    CPDF_Reference* pKid = ToReference(pArray->GetElement(i));
+    if (!pKid)
       continue;
-    }
+
     CPDF_PageNode* pNode = new CPDF_PageNode();
     pPageNode->m_childNode.Add(pNode);
-    pNode->m_dwPageNo = ((CPDF_Reference*)pKid)->GetRefObjNum();
+    pNode->m_dwPageNo = pKid->GetRefObjNum();
   }
   pPages->Release();
   return TRUE;
@@ -4166,21 +4055,20 @@ FX_BOOL CPDF_DataAvail::CheckUnkownPageNode(FX_DWORD dwPageNo,
     }
     return FALSE;
   }
-  if (pPage->GetType() == PDFOBJ_ARRAY) {
+  if (pPage->IsArray()) {
     pPageNode->m_dwPageNo = dwPageNo;
     pPageNode->m_type = PDF_PAGENODE_ARRAY;
     pPage->Release();
     return TRUE;
   }
-  if (pPage->GetType() != PDFOBJ_DICTIONARY) {
+  if (!pPage->IsDictionary()) {
     pPage->Release();
     m_docStatus = PDF_DATAAVAIL_ERROR;
     return FALSE;
   }
   pPageNode->m_dwPageNo = dwPageNo;
   CPDF_Dictionary* pDict = pPage->GetDict();
-  CFX_ByteString type =
-      pDict ? pDict->GetString(FX_BSTRC("Type")) : CFX_ByteString();
+  CFX_ByteString type = pDict->GetString(FX_BSTRC("Type"));
   if (type == FX_BSTRC("Pages")) {
     pPageNode->m_type = PDF_PAGENODE_PAGES;
     CPDF_Object* pKids = pDict->GetElement(FX_BSTRC("Kids"));
@@ -4190,21 +4078,21 @@ FX_BOOL CPDF_DataAvail::CheckUnkownPageNode(FX_DWORD dwPageNo,
     }
     switch (pKids->GetType()) {
       case PDFOBJ_REFERENCE: {
-        CPDF_Reference* pKid = (CPDF_Reference*)pKids;
+        CPDF_Reference* pKid = pKids->AsReference();
         CPDF_PageNode* pNode = new CPDF_PageNode();
         pPageNode->m_childNode.Add(pNode);
         pNode->m_dwPageNo = pKid->GetRefObjNum();
       } break;
       case PDFOBJ_ARRAY: {
-        CPDF_Array* pKidsArray = (CPDF_Array*)pKids;
+        CPDF_Array* pKidsArray = pKids->AsArray();
         for (FX_DWORD i = 0; i < pKidsArray->GetCount(); ++i) {
-          CPDF_Object* pKid = (CPDF_Object*)pKidsArray->GetElement(i);
-          if (!pKid || pKid->GetType() != PDFOBJ_REFERENCE) {
+          CPDF_Reference* pKid = ToReference(pKidsArray->GetElement(i));
+          if (!pKid)
             continue;
-          }
+
           CPDF_PageNode* pNode = new CPDF_PageNode();
           pPageNode->m_childNode.Add(pNode);
-          pNode->m_dwPageNo = ((CPDF_Reference*)pKid)->GetRefObjNum();
+          pNode->m_dwPageNo = pKid->GetRefObjNum();
         }
       } break;
       default:
@@ -4223,7 +4111,11 @@ FX_BOOL CPDF_DataAvail::CheckUnkownPageNode(FX_DWORD dwPageNo,
 FX_BOOL CPDF_DataAvail::CheckPageNode(CPDF_PageNode& pageNodes,
                                       int32_t iPage,
                                       int32_t& iCount,
-                                      IFX_DownloadHints* pHints) {
+                                      IFX_DownloadHints* pHints,
+                                      int level) {
+  if (level >= kMaxPageRecursionDepth) {
+    return FALSE;
+  }
   int32_t iSize = pageNodes.m_childNode.GetSize();
   if (iSize <= 0 || iPage >= iSize) {
     m_docStatus = PDF_DATAAVAIL_ERROR;
@@ -4248,7 +4140,7 @@ FX_BOOL CPDF_DataAvail::CheckPageNode(CPDF_PageNode& pageNodes,
         }
         break;
       case PDF_PAGENODE_PAGES:
-        if (!CheckPageNode(*pNode, iPage, iCount, pHints)) {
+        if (!CheckPageNode(*pNode, iPage, iCount, pHints, level + 1)) {
           return FALSE;
         }
         break;
@@ -4281,7 +4173,7 @@ FX_BOOL CPDF_DataAvail::LoadDocPage(int32_t iPage, IFX_DownloadHints* pHints) {
     return TRUE;
   }
   int32_t iCount = -1;
-  return CheckPageNode(m_pageNodes, iPage, iCount, pHints);
+  return CheckPageNode(m_pageNodes, iPage, iCount, pHints, 0);
 }
 FX_BOOL CPDF_DataAvail::CheckPageCount(IFX_DownloadHints* pHints) {
   FX_BOOL bExist = FALSE;
@@ -4441,16 +4333,12 @@ FX_BOOL CPDF_DataAvail::IsPageAvail(int32_t iPage, IFX_DownloadHints* pHints) {
     m_objs_array.RemoveAll();
     m_objnum_array.RemoveAll();
   }
-  if (m_pagesLoadState == NULL) {
-    m_pagesLoadState = new CFX_CMapDWordToDWord();
-  }
-  FX_DWORD dwPageLoad = 0;
-  if (m_pagesLoadState->Lookup(iPage, dwPageLoad) && dwPageLoad != 0) {
+  if (m_pagesLoadState.find(iPage) != m_pagesLoadState.end()) {
     return TRUE;
   }
   if (m_bLinearized) {
     if ((FX_DWORD)iPage == m_dwFirstPageNo) {
-      m_pagesLoadState->SetAt(iPage, TRUE);
+      m_pagesLoadState.insert(iPage);
       return TRUE;
     }
     if (!CheckLinearizedData(pHints)) {
@@ -4545,7 +4433,7 @@ FX_BOOL CPDF_DataAvail::IsPageAvail(int32_t iPage, IFX_DownloadHints* pHints) {
   m_bAnnotsLoad = FALSE;
   m_bCurPageDictLoadOK = FALSE;
   ResetFirstCheck(iPage);
-  m_pagesLoadState->SetAt(iPage, TRUE);
+  m_pagesLoadState.insert(iPage);
   return TRUE;
 }
 FX_BOOL CPDF_DataAvail::CheckResources(IFX_DownloadHints* pHints) {