| // Copyright 2014 PDFium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
| |
| #include "parser_int.h" |
| |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include "core/include/fpdfapi/fpdf_module.h" |
| #include "core/include/fpdfapi/fpdf_page.h" |
| #include "core/include/fpdfapi/fpdf_parser.h" |
| #include "core/include/fxcrt/fx_ext.h" |
| #include "core/include/fxcrt/fx_safe_types.h" |
| #include "core/src/fpdfapi/fpdf_page/pageint.h" |
| #include "third_party/base/nonstd_unique_ptr.h" |
| #include "third_party/base/stl_util.h" |
| |
| namespace { |
| |
| // A limit on the size of the xref table. Theoretical limits are higher, but |
| // this may be large enough in practice. |
| const int32_t kMaxXRefSize = 1048576; |
| |
| // A limit on the maximum object number in the xref table. Theoretical limits |
| // are higher, but this may be large enough in practice. |
| const FX_DWORD kMaxObjectNumber = 1048576; |
| |
| struct SearchTagRecord { |
| const char* 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("N"); |
| } |
| |
| int32_t GetStreamFirst(CPDF_StreamAcc* pObjStream) { |
| return pObjStream->GetDict()->GetInteger("First"); |
| } |
| |
| bool CanReadFromBitStream(const CFX_BitStream* hStream, |
| const FX_SAFE_DWORD& num_bits) { |
| return (num_bits.IsValid() && |
| hStream->BitsRemaining() >= num_bits.ValueOrDie()); |
| } |
| |
| } // 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("Type"); |
| if (!pType) { |
| pType = pDict->GetElementValue("FT"); |
| if (!pType) { |
| return FALSE; |
| } |
| } |
| if (pType->GetString() == "Sig") { |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| CPDF_Parser::CPDF_Parser() { |
| m_pDocument = NULL; |
| m_pTrailer = NULL; |
| m_pEncryptDict = NULL; |
| m_pLinearized = NULL; |
| m_dwFirstPageNo = 0; |
| m_dwXrefStartObjNum = 0; |
| m_bOwnFileRead = TRUE; |
| m_FileVersion = 0; |
| m_bForceUseSecurityHandler = FALSE; |
| } |
| CPDF_Parser::~CPDF_Parser() { |
| CloseParser(FALSE); |
| } |
| |
| FX_DWORD CPDF_Parser::GetLastObjNum() const { |
| return m_ObjectInfo.empty() ? 0 : m_ObjectInfo.rbegin()->first; |
| } |
| |
| bool CPDF_Parser::IsValidObjectNumber(FX_DWORD objnum) const { |
| return !m_ObjectInfo.empty() && objnum <= m_ObjectInfo.rbegin()->first; |
| } |
| |
| void CPDF_Parser::SetEncryptDictionary(CPDF_Dictionary* pDict) { |
| m_pEncryptDict = pDict; |
| } |
| void CPDF_Parser::CloseParser(FX_BOOL bReParse) { |
| m_bVersionUpdated = FALSE; |
| if (!bReParse) { |
| delete m_pDocument; |
| m_pDocument = NULL; |
| } |
| if (m_pTrailer) { |
| m_pTrailer->Release(); |
| m_pTrailer = NULL; |
| } |
| ReleaseEncryptHandler(); |
| SetEncryptDictionary(NULL); |
| if (m_bOwnFileRead && m_Syntax.m_pFileAccess) { |
| m_Syntax.m_pFileAccess->Release(); |
| m_Syntax.m_pFileAccess = NULL; |
| } |
| FX_POSITION pos = m_ObjectStreamMap.GetStartPosition(); |
| while (pos) { |
| void* objnum; |
| CPDF_StreamAcc* pStream; |
| m_ObjectStreamMap.GetNextAssoc(pos, objnum, (void*&)pStream); |
| delete pStream; |
| } |
| m_ObjectStreamMap.RemoveAll(); |
| m_ObjCache.clear(); |
| |
| m_SortedOffset.RemoveAll(); |
| m_ObjectInfo.clear(); |
| m_V5Type.RemoveAll(); |
| m_ObjVersion.RemoveAll(); |
| int32_t iLen = m_Trailers.GetSize(); |
| for (int32_t i = 0; i < iLen; ++i) { |
| if (CPDF_Dictionary* trailer = m_Trailers.GetAt(i)) |
| trailer->Release(); |
| } |
| m_Trailers.RemoveAll(); |
| if (m_pLinearized) { |
| m_pLinearized->Release(); |
| m_pLinearized = NULL; |
| } |
| } |
| CPDF_SecurityHandler* FPDF_CreateStandardSecurityHandler(); |
| CPDF_SecurityHandler* FPDF_CreatePubKeyHandler(void*); |
| FX_DWORD CPDF_Parser::StartParse(IFX_FileRead* pFileAccess, |
| FX_BOOL bReParse, |
| FX_BOOL bOwnFileRead) { |
| CloseParser(bReParse); |
| m_bXRefStream = FALSE; |
| m_LastXRefOffset = 0; |
| m_bOwnFileRead = bOwnFileRead; |
| |
| int32_t offset = GetHeaderOffset(pFileAccess); |
| if (offset == -1) { |
| if (bOwnFileRead && pFileAccess) |
| pFileAccess->Release(); |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| m_Syntax.InitParser(pFileAccess, offset); |
| |
| uint8_t ch; |
| if (!m_Syntax.GetCharAt(5, ch)) |
| return PDFPARSE_ERROR_FORMAT; |
| if (std::isdigit(ch)) |
| m_FileVersion = FXSYS_toDecimalDigit(ch) * 10; |
| |
| if (!m_Syntax.GetCharAt(7, ch)) |
| return PDFPARSE_ERROR_FORMAT; |
| if (std::isdigit(ch)) |
| m_FileVersion += FXSYS_toDecimalDigit(ch); |
| |
| 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) |
| m_pDocument = new CPDF_Document(this); |
| |
| FX_BOOL bXRefRebuilt = FALSE; |
| if (m_Syntax.SearchWord("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) |
| m_SortedOffset.Add(startxref_offset); |
| |
| m_Syntax.GetKeyword(); |
| FX_BOOL bNumber; |
| CFX_ByteString xrefpos_str = m_Syntax.GetNextWord(bNumber); |
| if (!bNumber) |
| return PDFPARSE_ERROR_FORMAT; |
| |
| m_LastXRefOffset = (FX_FILESIZE)FXSYS_atoi64(xrefpos_str); |
| if (!LoadAllCrossRefV4(m_LastXRefOffset) && |
| !LoadAllCrossRefV5(m_LastXRefOffset)) { |
| if (!RebuildCrossRef()) |
| return PDFPARSE_ERROR_FORMAT; |
| |
| bXRefRebuilt = TRUE; |
| m_LastXRefOffset = 0; |
| } |
| } else { |
| if (!RebuildCrossRef()) |
| return PDFPARSE_ERROR_FORMAT; |
| |
| bXRefRebuilt = TRUE; |
| } |
| FX_DWORD dwRet = SetEncryptHandler(); |
| if (dwRet != PDFPARSE_ERROR_SUCCESS) |
| return dwRet; |
| |
| m_pDocument->LoadDoc(); |
| if (!m_pDocument->GetRoot() || m_pDocument->GetPageCount() == 0) { |
| if (bXRefRebuilt) |
| return PDFPARSE_ERROR_FORMAT; |
| |
| ReleaseEncryptHandler(); |
| if (!RebuildCrossRef()) |
| return PDFPARSE_ERROR_FORMAT; |
| |
| dwRet = SetEncryptHandler(); |
| if (dwRet != PDFPARSE_ERROR_SUCCESS) |
| return dwRet; |
| |
| m_pDocument->LoadDoc(); |
| if (!m_pDocument->GetRoot()) |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| FXSYS_qsort(m_SortedOffset.GetData(), m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| if (GetRootObjNum() == 0) { |
| ReleaseEncryptHandler(); |
| if (!RebuildCrossRef() || GetRootObjNum() == 0) |
| return PDFPARSE_ERROR_FORMAT; |
| |
| dwRet = SetEncryptHandler(); |
| if (dwRet != PDFPARSE_ERROR_SUCCESS) |
| return dwRet; |
| } |
| if (m_pSecurityHandler && !m_pSecurityHandler->IsMetadataEncrypted()) { |
| CPDF_Reference* pMetadata = |
| ToReference(m_pDocument->GetRoot()->GetElement("Metadata")); |
| if (pMetadata) |
| m_Syntax.m_MetadataObjnum = pMetadata->GetRefObjNum(); |
| } |
| return PDFPARSE_ERROR_SUCCESS; |
| } |
| FX_DWORD CPDF_Parser::SetEncryptHandler() { |
| ReleaseEncryptHandler(); |
| SetEncryptDictionary(NULL); |
| if (!m_pTrailer) { |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| CPDF_Object* pEncryptObj = m_pTrailer->GetElement("Encrypt"); |
| 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) { |
| return PDFPARSE_ERROR_HANDLER; |
| } |
| if (!m_pSecurityHandler->OnInit(this, m_pEncryptDict)) { |
| return err; |
| } |
| 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.release()); |
| } else if (m_pEncryptDict) { |
| CFX_ByteString filter = m_pEncryptDict->GetString("Filter"); |
| nonstd::unique_ptr<CPDF_SecurityHandler> pSecurityHandler; |
| FX_DWORD err = PDFPARSE_ERROR_HANDLER; |
| if (filter == "Standard") { |
| pSecurityHandler.reset(FPDF_CreateStandardSecurityHandler()); |
| err = PDFPARSE_ERROR_PASSWORD; |
| } |
| if (!pSecurityHandler) { |
| return PDFPARSE_ERROR_HANDLER; |
| } |
| if (!pSecurityHandler->OnInit(this, m_pEncryptDict)) { |
| return err; |
| } |
| m_pSecurityHandler = std::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.release()); |
| } |
| return PDFPARSE_ERROR_SUCCESS; |
| } |
| void CPDF_Parser::ReleaseEncryptHandler() { |
| m_Syntax.m_pCryptoHandler.reset(); |
| if (!m_bForceUseSecurityHandler) { |
| m_pSecurityHandler.reset(); |
| } |
| } |
| FX_FILESIZE CPDF_Parser::GetObjectOffset(FX_DWORD objnum) { |
| if (!IsValidObjectNumber(objnum)) |
| return 0; |
| |
| if (m_V5Type[objnum] == 1) |
| return m_ObjectInfo[objnum].pos; |
| |
| if (m_V5Type[objnum] == 2) { |
| FX_FILESIZE pos = m_ObjectInfo[objnum].pos; |
| return m_ObjectInfo[pos].pos; |
| } |
| return 0; |
| } |
| |
| FX_BOOL CPDF_Parser::LoadAllCrossRefV4(FX_FILESIZE xrefpos) { |
| if (!LoadCrossRefV4(xrefpos, 0, TRUE)) { |
| return FALSE; |
| } |
| m_pTrailer = LoadTrailerV4(); |
| if (!m_pTrailer) { |
| return FALSE; |
| } |
| int32_t xrefsize = GetDirectInteger(m_pTrailer, "Size"); |
| if (xrefsize <= 0 || xrefsize > kMaxXRefSize) { |
| return FALSE; |
| } |
| m_ObjectInfo[0].pos = 0; |
| m_V5Type.SetSize(xrefsize); |
| CFX_FileSizeArray CrossRefList, XRefStreamList; |
| CrossRefList.Add(xrefpos); |
| XRefStreamList.Add(GetDirectInteger(m_pTrailer, "XRefStm")); |
| if (!CheckDirectType(m_pTrailer, "Prev", PDFOBJ_NUMBER)) { |
| return FALSE; |
| } |
| FX_FILESIZE newxrefpos = GetDirectInteger(m_pTrailer, "Prev"); |
| if (newxrefpos == xrefpos) { |
| return FALSE; |
| } |
| xrefpos = newxrefpos; |
| while (xrefpos) { |
| CrossRefList.InsertAt(0, xrefpos); |
| LoadCrossRefV4(xrefpos, 0, TRUE); |
| nonstd::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>> pDict( |
| LoadTrailerV4()); |
| if (!pDict) |
| return FALSE; |
| |
| if (!CheckDirectType(pDict.get(), "Prev", PDFOBJ_NUMBER)) |
| return FALSE; |
| |
| newxrefpos = GetDirectInteger(pDict.get(), "Prev"); |
| if (newxrefpos == xrefpos) |
| return FALSE; |
| |
| xrefpos = newxrefpos; |
| XRefStreamList.InsertAt(0, pDict->GetInteger("XRefStm")); |
| m_Trailers.Add(pDict.release()); |
| } |
| for (int32_t i = 0; i < CrossRefList.GetSize(); i++) { |
| if (!LoadCrossRefV4(CrossRefList[i], XRefStreamList[i], FALSE)) |
| return FALSE; |
| } |
| return TRUE; |
| } |
| FX_BOOL CPDF_Parser::LoadLinearizedAllCrossRefV4(FX_FILESIZE xrefpos, |
| FX_DWORD dwObjCount) { |
| if (!LoadLinearizedCrossRefV4(xrefpos, dwObjCount)) { |
| return FALSE; |
| } |
| m_pTrailer = LoadTrailerV4(); |
| if (!m_pTrailer) { |
| return FALSE; |
| } |
| int32_t xrefsize = GetDirectInteger(m_pTrailer, "Size"); |
| if (xrefsize == 0) { |
| return FALSE; |
| } |
| CFX_FileSizeArray CrossRefList, XRefStreamList; |
| CrossRefList.Add(xrefpos); |
| XRefStreamList.Add(GetDirectInteger(m_pTrailer, "XRefStm")); |
| xrefpos = GetDirectInteger(m_pTrailer, "Prev"); |
| while (xrefpos) { |
| CrossRefList.InsertAt(0, xrefpos); |
| LoadCrossRefV4(xrefpos, 0, TRUE); |
| CPDF_Dictionary* pDict = LoadTrailerV4(); |
| if (!pDict) { |
| return FALSE; |
| } |
| xrefpos = GetDirectInteger(pDict, "Prev"); |
| XRefStreamList.InsertAt(0, pDict->GetInteger("XRefStm")); |
| m_Trailers.Add(pDict); |
| } |
| for (int32_t i = 1; i < CrossRefList.GetSize(); i++) |
| if (!LoadCrossRefV4(CrossRefList[i], XRefStreamList[i], FALSE)) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| FX_BOOL CPDF_Parser::LoadLinearizedCrossRefV4(FX_FILESIZE pos, |
| FX_DWORD dwObjCount) { |
| FX_FILESIZE dwStartPos = pos - m_Syntax.m_HeaderOffset; |
| m_Syntax.RestorePos(dwStartPos); |
| void* pResult = |
| FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| if (!pResult) { |
| m_SortedOffset.Add(pos); |
| } |
| FX_DWORD start_objnum = 0; |
| FX_DWORD count = dwObjCount; |
| FX_FILESIZE SavedPos = m_Syntax.SavePos(); |
| const int32_t recordsize = 20; |
| std::vector<char> buf(1024 * recordsize + 1); |
| buf[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) { |
| return FALSE; |
| } |
| if (!m_Syntax.ReadBlock(reinterpret_cast<uint8_t*>(buf.data()), |
| dwReadSize)) { |
| return FALSE; |
| } |
| for (int32_t i = 0; i < block_size; i++) { |
| FX_DWORD objnum = start_objnum + block * 1024 + i; |
| char* pEntry = &buf[i * recordsize]; |
| if (pEntry[17] == 'f') { |
| m_ObjectInfo[objnum].pos = 0; |
| m_V5Type.SetAtGrow(objnum, 0); |
| } else { |
| int32_t offset = FXSYS_atoi(pEntry); |
| if (offset == 0) { |
| for (int32_t c = 0; c < 10; c++) { |
| if (!std::isdigit(pEntry[c])) |
| return FALSE; |
| } |
| } |
| m_ObjectInfo[objnum].pos = offset; |
| int32_t version = FXSYS_atoi(pEntry + 11); |
| if (version >= 1) { |
| m_bVersionUpdated = TRUE; |
| } |
| m_ObjVersion.SetAtGrow(objnum, version); |
| if (m_ObjectInfo[objnum].pos < m_Syntax.m_FileLen) { |
| void* pResult = FXSYS_bsearch( |
| &m_ObjectInfo[objnum].pos, m_SortedOffset.GetData(), |
| m_SortedOffset.GetSize(), sizeof(FX_FILESIZE), CompareFileSize); |
| if (!pResult) { |
| m_SortedOffset.Add(m_ObjectInfo[objnum].pos); |
| } |
| } |
| m_V5Type.SetAtGrow(objnum, 1); |
| } |
| } |
| } |
| m_Syntax.RestorePos(SavedPos + count * recordsize); |
| return TRUE; |
| } |
| |
| 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) { |
| m_Syntax.RestorePos(pos); |
| if (m_Syntax.GetKeyword() != "xref") |
| return false; |
| |
| if (!FindPosInOffsets(pos)) |
| m_SortedOffset.Add(pos); |
| |
| 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 (!bIsNumber) { |
| m_Syntax.RestorePos(SavedPos); |
| break; |
| } |
| FX_DWORD start_objnum = FXSYS_atoi(word); |
| if (start_objnum >= kMaxObjectNumber) |
| return false; |
| |
| FX_DWORD count = m_Syntax.GetDirectNum(); |
| m_Syntax.ToNextWord(); |
| SavedPos = m_Syntax.SavePos(); |
| const int32_t recordsize = 20; |
| m_dwXrefStartObjNum = start_objnum; |
| if (!bSkip) { |
| std::vector<char> buf(1024 * recordsize + 1); |
| buf[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; |
| m_Syntax.ReadBlock(reinterpret_cast<uint8_t*>(buf.data()), |
| block_size * recordsize); |
| for (int32_t i = 0; i < block_size; i++) { |
| FX_DWORD objnum = start_objnum + block * 1024 + i; |
| char* pEntry = &buf[i * recordsize]; |
| if (pEntry[17] == 'f') { |
| m_ObjectInfo[objnum].pos = 0; |
| m_V5Type.SetAtGrow(objnum, 0); |
| } else { |
| FX_FILESIZE offset = (FX_FILESIZE)FXSYS_atoi64(pEntry); |
| if (offset == 0) { |
| for (int32_t c = 0; c < 10; c++) { |
| if (!std::isdigit(pEntry[c])) |
| return false; |
| } |
| } |
| m_ObjectInfo[objnum].pos = offset; |
| int32_t version = FXSYS_atoi(pEntry + 11); |
| if (version >= 1) { |
| m_bVersionUpdated = TRUE; |
| } |
| m_ObjVersion.SetAtGrow(objnum, version); |
| if (m_ObjectInfo[objnum].pos < m_Syntax.m_FileLen && |
| !FindPosInOffsets(m_ObjectInfo[objnum].pos)) { |
| m_SortedOffset.Add(m_ObjectInfo[objnum].pos); |
| } |
| m_V5Type.SetAtGrow(objnum, 1); |
| } |
| } |
| } |
| } |
| m_Syntax.RestorePos(SavedPos + count * recordsize); |
| } |
| return !streampos || LoadCrossRefV5(&streampos, FALSE); |
| } |
| |
| FX_BOOL CPDF_Parser::LoadAllCrossRefV5(FX_FILESIZE xrefpos) { |
| if (!LoadCrossRefV5(&xrefpos, TRUE)) { |
| return FALSE; |
| } |
| std::set<FX_FILESIZE> seen_xrefpos; |
| while (xrefpos) { |
| seen_xrefpos.insert(xrefpos); |
| if (!LoadCrossRefV5(&xrefpos, FALSE)) { |
| return FALSE; |
| } |
| // Check for circular references. |
| if (pdfium::ContainsKey(seen_xrefpos, xrefpos)) { |
| return FALSE; |
| } |
| } |
| m_ObjectStreamMap.InitHashTable(101, FALSE); |
| m_bXRefStream = TRUE; |
| return TRUE; |
| } |
| |
| FX_BOOL CPDF_Parser::RebuildCrossRef() { |
| m_ObjectInfo.clear(); |
| m_V5Type.RemoveAll(); |
| m_SortedOffset.RemoveAll(); |
| m_ObjVersion.RemoveAll(); |
| if (m_pTrailer) { |
| m_pTrailer->Release(); |
| m_pTrailer = NULL; |
| } |
| int32_t status = 0; |
| int32_t inside_index = 0; |
| FX_DWORD objnum = 0, gennum = 0; |
| int32_t depth = 0; |
| uint8_t* buffer = FX_Alloc(uint8_t, 4096); |
| FX_FILESIZE pos = m_Syntax.m_HeaderOffset; |
| FX_FILESIZE start_pos = 0, start_pos1 = 0; |
| FX_FILESIZE last_obj = -1, last_xref = -1, last_trailer = -1; |
| while (pos < m_Syntax.m_FileLen) { |
| FX_BOOL bOverFlow = FALSE; |
| FX_DWORD size = (FX_DWORD)(m_Syntax.m_FileLen - pos); |
| if (size > 4096) { |
| size = 4096; |
| } |
| if (!m_Syntax.m_pFileAccess->ReadBlock(buffer, pos, size)) { |
| break; |
| } |
| for (FX_DWORD i = 0; i < size; i++) { |
| uint8_t byte = buffer[i]; |
| switch (status) { |
| case 0: |
| if (PDFCharIsWhitespace(byte)) |
| status = 1; |
| |
| 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 == '\\') |
| status = 13; |
| |
| if (byte == 't') { |
| status = 7; |
| inside_index = 1; |
| } |
| break; |
| case 1: |
| if (PDFCharIsWhitespace(byte)) { |
| break; |
| } else if (std::isdigit(byte)) { |
| start_pos = pos + i; |
| status = 2; |
| objnum = FXSYS_toDecimalDigit(byte); |
| } else if (byte == 't') { |
| status = 7; |
| inside_index = 1; |
| } else if (byte == 'x') { |
| status = 8; |
| inside_index = 1; |
| } else { |
| --i; |
| status = 0; |
| } |
| break; |
| case 2: |
| if (std::isdigit(byte)) { |
| objnum = objnum * 10 + FXSYS_toDecimalDigit(byte); |
| break; |
| } else if (PDFCharIsWhitespace(byte)) { |
| status = 3; |
| } else { |
| --i; |
| status = 14; |
| inside_index = 0; |
| } |
| break; |
| case 3: |
| if (std::isdigit(byte)) { |
| start_pos1 = pos + i; |
| status = 4; |
| gennum = FXSYS_toDecimalDigit(byte); |
| } else if (PDFCharIsWhitespace(byte)) { |
| break; |
| } else if (byte == 't') { |
| status = 7; |
| inside_index = 1; |
| } else { |
| --i; |
| status = 0; |
| } |
| break; |
| case 4: |
| if (std::isdigit(byte)) { |
| gennum = gennum * 10 + FXSYS_toDecimalDigit(byte); |
| break; |
| } else if (PDFCharIsWhitespace(byte)) { |
| status = 5; |
| } else { |
| --i; |
| status = 0; |
| } |
| break; |
| case 5: |
| if (byte == 'o') { |
| status = 6; |
| inside_index = 1; |
| } else if (PDFCharIsWhitespace(byte)) { |
| break; |
| } else if (std::isdigit(byte)) { |
| objnum = gennum; |
| gennum = FXSYS_toDecimalDigit(byte); |
| start_pos = start_pos1; |
| start_pos1 = pos + i; |
| status = 4; |
| } else if (byte == 't') { |
| status = 7; |
| inside_index = 1; |
| } else { |
| --i; |
| status = 0; |
| } |
| break; |
| case 6: |
| switch (inside_index) { |
| case 1: |
| if (byte != 'b') { |
| --i; |
| status = 0; |
| } else { |
| inside_index++; |
| } |
| break; |
| case 2: |
| if (byte != 'j') { |
| --i; |
| status = 0; |
| } else { |
| inside_index++; |
| } |
| break; |
| case 3: |
| if (PDFCharIsWhitespace(byte) || PDFCharIsDelimiter(byte)) { |
| if (objnum > 0x1000000) { |
| status = 0; |
| break; |
| } |
| FX_FILESIZE obj_pos = start_pos - m_Syntax.m_HeaderOffset; |
| last_obj = start_pos; |
| void* pResult = |
| FXSYS_bsearch(&obj_pos, m_SortedOffset.GetData(), |
| m_SortedOffset.GetSize(), sizeof(FX_FILESIZE), |
| CompareFileSize); |
| if (!pResult) { |
| m_SortedOffset.Add(obj_pos); |
| } |
| FX_FILESIZE obj_end = 0; |
| CPDF_Object* pObject = ParseIndirectObjectAtByStrict( |
| m_pDocument, obj_pos, objnum, NULL, &obj_end); |
| if (CPDF_Stream* pStream = ToStream(pObject)) { |
| if (CPDF_Dictionary* pDict = pStream->GetDict()) { |
| if ((pDict->KeyExist("Type")) && |
| (pDict->GetString("Type") == "XRef" && |
| pDict->KeyExist("Size"))) { |
| CPDF_Object* pRoot = pDict->GetElement("Root"); |
| if (pRoot && pRoot->GetDict() && |
| pRoot->GetDict()->GetElement("Pages")) { |
| if (m_pTrailer) |
| m_pTrailer->Release(); |
| m_pTrailer = ToDictionary(pDict->Clone()); |
| } |
| } |
| } |
| } |
| FX_FILESIZE offset = 0; |
| m_Syntax.RestorePos(obj_pos); |
| offset = m_Syntax.FindTag("obj", 0); |
| if (offset == -1) { |
| offset = 0; |
| } else { |
| offset += 3; |
| } |
| FX_FILESIZE nLen = obj_end - obj_pos - offset; |
| if ((FX_DWORD)nLen > size - i) { |
| pos = obj_end + m_Syntax.m_HeaderOffset; |
| bOverFlow = TRUE; |
| } else { |
| i += (FX_DWORD)nLen; |
| } |
| if (!m_ObjectInfo.empty() && IsValidObjectNumber(objnum) && |
| m_ObjectInfo[objnum].pos) { |
| if (pObject) { |
| FX_DWORD oldgen = m_ObjVersion.GetAt(objnum); |
| m_ObjectInfo[objnum].pos = obj_pos; |
| m_ObjVersion.SetAt(objnum, (int16_t)gennum); |
| if (oldgen != gennum) { |
| m_bVersionUpdated = TRUE; |
| } |
| } |
| } else { |
| m_ObjectInfo[objnum].pos = obj_pos; |
| m_V5Type.SetAtGrow(objnum, 1); |
| m_ObjVersion.SetAtGrow(objnum, (int16_t)gennum); |
| } |
| if (pObject) { |
| pObject->Release(); |
| } |
| } |
| --i; |
| status = 0; |
| break; |
| } |
| break; |
| case 7: |
| if (inside_index == 7) { |
| 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->IsDictionary() && !pObj->AsStream()) { |
| pObj->Release(); |
| } else { |
| CPDF_Stream* pStream = pObj->AsStream(); |
| if (CPDF_Dictionary* pTrailer = |
| pStream ? pStream->GetDict() : pObj->AsDictionary()) { |
| if (m_pTrailer) { |
| CPDF_Object* pRoot = pTrailer->GetElement("Root"); |
| CPDF_Reference* pRef = ToReference(pRoot); |
| if (!pRoot || |
| (pRef && IsValidObjectNumber(pRef->GetRefObjNum()) && |
| m_ObjectInfo[pRef->GetRefObjNum()].pos != 0)) { |
| FX_POSITION pos = pTrailer->GetStartPos(); |
| while (pos) { |
| CFX_ByteString key; |
| CPDF_Object* pElement = |
| pTrailer->GetNextElement(pos, key); |
| FX_DWORD dwObjNum = |
| pElement ? pElement->GetObjNum() : 0; |
| if (dwObjNum) { |
| m_pTrailer->SetAtReference(key, m_pDocument, |
| dwObjNum); |
| } else { |
| m_pTrailer->SetAt(key, pElement->Clone()); |
| } |
| } |
| pObj->Release(); |
| } else { |
| pObj->Release(); |
| } |
| } else { |
| if (pObj->IsStream()) { |
| m_pTrailer = ToDictionary(pTrailer->Clone()); |
| pObj->Release(); |
| } else { |
| m_pTrailer = pTrailer; |
| } |
| FX_FILESIZE dwSavePos = m_Syntax.SavePos(); |
| CFX_ByteString strWord = m_Syntax.GetKeyword(); |
| if (!strWord.Compare("startxref")) { |
| FX_BOOL bNumber = FALSE; |
| CFX_ByteString bsOffset = m_Syntax.GetNextWord(bNumber); |
| if (bNumber) { |
| m_LastXRefOffset = FXSYS_atoi(bsOffset); |
| } |
| } |
| m_Syntax.RestorePos(dwSavePos); |
| } |
| } else { |
| pObj->Release(); |
| } |
| } |
| } |
| } |
| --i; |
| status = 0; |
| } else if (byte == "trailer"[inside_index]) { |
| inside_index++; |
| } else { |
| --i; |
| status = 0; |
| } |
| break; |
| case 8: |
| if (inside_index == 4) { |
| last_xref = pos + i - 4; |
| status = 1; |
| } else if (byte == "xref"[inside_index]) { |
| inside_index++; |
| } else { |
| --i; |
| status = 0; |
| } |
| break; |
| case 9: |
| if (byte == '\r' || byte == '\n') { |
| status = 0; |
| } |
| break; |
| case 10: |
| if (byte == ')') { |
| if (depth > 0) { |
| depth--; |
| } |
| } else if (byte == '(') { |
| depth++; |
| } |
| if (!depth) { |
| status = 0; |
| } |
| break; |
| case 11: |
| if (byte == '>' || (byte == '<' && inside_index == 1)) |
| status = 0; |
| inside_index = 0; |
| break; |
| case 13: |
| if (PDFCharIsDelimiter(byte) || PDFCharIsWhitespace(byte)) { |
| --i; |
| status = 0; |
| } |
| break; |
| case 14: |
| if (PDFCharIsWhitespace(byte)) { |
| status = 0; |
| } else if (byte == '%' || byte == '(' || byte == '<' || |
| byte == '\\') { |
| status = 0; |
| --i; |
| } else if (inside_index == 6) { |
| status = 0; |
| --i; |
| } else if (byte == "endobj"[inside_index]) { |
| inside_index++; |
| } |
| break; |
| } |
| if (bOverFlow) { |
| size = 0; |
| break; |
| } |
| } |
| pos += size; |
| } |
| if (last_xref != -1 && last_xref > last_obj) { |
| last_trailer = last_xref; |
| } else if (last_trailer == -1 || last_xref < last_obj) { |
| last_trailer = m_Syntax.m_FileLen; |
| } |
| FX_FILESIZE offset = last_trailer - m_Syntax.m_HeaderOffset; |
| void* pResult = |
| FXSYS_bsearch(&offset, m_SortedOffset.GetData(), m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| if (!pResult) { |
| m_SortedOffset.Add(offset); |
| } |
| FX_Free(buffer); |
| return m_pTrailer && !m_ObjectInfo.empty(); |
| } |
| |
| FX_BOOL CPDF_Parser::LoadCrossRefV5(FX_FILESIZE* pos, FX_BOOL bMainXRef) { |
| CPDF_Object* pObject = ParseIndirectObjectAt(m_pDocument, *pos, 0, nullptr); |
| if (!pObject) |
| return FALSE; |
| if (m_pDocument) { |
| FX_BOOL bInserted = FALSE; |
| CPDF_Dictionary* pDict = m_pDocument->GetRoot(); |
| if (!pDict || pDict->GetObjNum() != pObject->m_ObjNum) { |
| bInserted = m_pDocument->InsertIndirectObject(pObject->m_ObjNum, pObject); |
| } else { |
| if (pObject->IsStream()) |
| pObject->Release(); |
| } |
| if (!bInserted) |
| return FALSE; |
| } |
| |
| CPDF_Stream* pStream = pObject->AsStream(); |
| if (!pStream) |
| return FALSE; |
| |
| *pos = pStream->GetDict()->GetInteger("Prev"); |
| int32_t size = pStream->GetDict()->GetInteger("Size"); |
| if (size < 0) { |
| pStream->Release(); |
| return FALSE; |
| } |
| if (bMainXRef) { |
| m_pTrailer = ToDictionary(pStream->GetDict()->Clone()); |
| m_ObjectInfo[0].pos = 0; |
| if (m_V5Type.SetSize(size)) { |
| FXSYS_memset(m_V5Type.GetData(), 0, size); |
| } |
| } else { |
| m_Trailers.Add(ToDictionary(pStream->GetDict()->Clone())); |
| } |
| std::vector<std::pair<int32_t, int32_t> > arrIndex; |
| CPDF_Array* pArray = pStream->GetDict()->GetArray("Index"); |
| if (pArray) { |
| FX_DWORD nPairSize = pArray->GetCount() / 2; |
| for (FX_DWORD i = 0; i < nPairSize; i++) { |
| CPDF_Object* pStartNumObj = pArray->GetElement(i * 2); |
| CPDF_Object* pCountObj = pArray->GetElement(i * 2 + 1); |
| if (ToNumber(pStartNumObj) && ToNumber(pCountObj)) { |
| int nStartNum = pStartNumObj->GetInteger(); |
| int nCount = pCountObj->GetInteger(); |
| if (nStartNum >= 0 && nCount > 0) { |
| arrIndex.push_back(std::make_pair(nStartNum, nCount)); |
| } |
| } |
| } |
| } |
| if (arrIndex.size() == 0) { |
| arrIndex.push_back(std::make_pair(0, size)); |
| } |
| pArray = pStream->GetDict()->GetArray("W"); |
| if (!pArray) { |
| pStream->Release(); |
| return FALSE; |
| } |
| CFX_DWordArray WidthArray; |
| FX_SAFE_DWORD dwAccWidth = 0; |
| for (FX_DWORD i = 0; i < pArray->GetCount(); i++) { |
| WidthArray.Add(pArray->GetInteger(i)); |
| dwAccWidth += WidthArray[i]; |
| } |
| if (!dwAccWidth.IsValid() || WidthArray.GetSize() < 3) { |
| pStream->Release(); |
| return FALSE; |
| } |
| FX_DWORD totalWidth = dwAccWidth.ValueOrDie(); |
| CPDF_StreamAcc acc; |
| acc.LoadAllData(pStream); |
| const uint8_t* pData = acc.GetData(); |
| FX_DWORD dwTotalSize = acc.GetSize(); |
| FX_DWORD segindex = 0; |
| for (FX_DWORD i = 0; i < arrIndex.size(); i++) { |
| int32_t startnum = arrIndex[i].first; |
| if (startnum < 0) { |
| continue; |
| } |
| m_dwXrefStartObjNum = |
| pdfium::base::checked_cast<FX_DWORD, int32_t>(startnum); |
| FX_DWORD count = |
| pdfium::base::checked_cast<FX_DWORD, int32_t>(arrIndex[i].second); |
| FX_SAFE_DWORD dwCaculatedSize = segindex; |
| dwCaculatedSize += count; |
| dwCaculatedSize *= totalWidth; |
| if (!dwCaculatedSize.IsValid() || |
| dwCaculatedSize.ValueOrDie() > dwTotalSize) { |
| continue; |
| } |
| const uint8_t* segstart = pData + segindex * totalWidth; |
| FX_SAFE_DWORD dwMaxObjNum = startnum; |
| dwMaxObjNum += count; |
| FX_DWORD dwV5Size = |
| pdfium::base::checked_cast<FX_DWORD, int32_t>(m_V5Type.GetSize()); |
| if (!dwMaxObjNum.IsValid() || dwMaxObjNum.ValueOrDie() > dwV5Size) { |
| continue; |
| } |
| for (FX_DWORD j = 0; j < count; j++) { |
| int32_t type = 1; |
| const uint8_t* entrystart = segstart + j * totalWidth; |
| if (WidthArray[0]) { |
| type = GetVarInt(entrystart, WidthArray[0]); |
| } |
| if (m_V5Type[startnum + j] == 255) { |
| FX_FILESIZE offset = |
| GetVarInt(entrystart + WidthArray[0], WidthArray[1]); |
| m_ObjectInfo[startnum + j].pos = offset; |
| void* pResult = FXSYS_bsearch(&offset, m_SortedOffset.GetData(), |
| m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| if (!pResult) { |
| m_SortedOffset.Add(offset); |
| } |
| continue; |
| } |
| if (m_V5Type[startnum + j]) { |
| continue; |
| } |
| m_V5Type[startnum + j] = type; |
| if (type == 0) { |
| m_ObjectInfo[startnum + j].pos = 0; |
| } else { |
| FX_FILESIZE offset = |
| GetVarInt(entrystart + WidthArray[0], WidthArray[1]); |
| m_ObjectInfo[startnum + j].pos = offset; |
| if (type == 1) { |
| void* pResult = FXSYS_bsearch(&offset, m_SortedOffset.GetData(), |
| m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| if (!pResult) { |
| m_SortedOffset.Add(offset); |
| } |
| } else { |
| if (offset < 0 || offset >= m_V5Type.GetSize()) { |
| pStream->Release(); |
| return FALSE; |
| } |
| m_V5Type[offset] = 255; |
| } |
| } |
| } |
| segindex += count; |
| } |
| pStream->Release(); |
| return TRUE; |
| } |
| CPDF_Array* CPDF_Parser::GetIDArray() { |
| CPDF_Object* pID = m_pTrailer ? m_pTrailer->GetElement("ID") : NULL; |
| if (!pID) |
| return nullptr; |
| |
| if (CPDF_Reference* pRef = pID->AsReference()) { |
| pID = ParseIndirectObject(nullptr, pRef->GetRefObjNum()); |
| m_pTrailer->SetAt("ID", pID); |
| } |
| return ToArray(pID); |
| } |
| FX_DWORD CPDF_Parser::GetRootObjNum() { |
| CPDF_Reference* pRef = |
| ToReference(m_pTrailer ? m_pTrailer->GetElement("Root") : nullptr); |
| return pRef ? pRef->GetRefObjNum() : 0; |
| } |
| FX_DWORD CPDF_Parser::GetInfoObjNum() { |
| CPDF_Reference* pRef = |
| ToReference(m_pTrailer ? m_pTrailer->GetElement("Info") : nullptr); |
| return pRef ? pRef->GetRefObjNum() : 0; |
| } |
| FX_BOOL CPDF_Parser::IsFormStream(FX_DWORD objnum, FX_BOOL& bForm) { |
| bForm = FALSE; |
| if (!IsValidObjectNumber(objnum)) |
| return TRUE; |
| if (m_V5Type[objnum] == 0) |
| return TRUE; |
| if (m_V5Type[objnum] == 2) |
| return TRUE; |
| FX_FILESIZE pos = m_ObjectInfo[objnum].pos; |
| void* pResult = |
| FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| if (!pResult) { |
| return TRUE; |
| } |
| if ((FX_FILESIZE*)pResult - (FX_FILESIZE*)m_SortedOffset.GetData() == |
| m_SortedOffset.GetSize() - 1) { |
| return FALSE; |
| } |
| FX_FILESIZE size = ((FX_FILESIZE*)pResult)[1] - pos; |
| FX_FILESIZE SavedPos = m_Syntax.SavePos(); |
| m_Syntax.RestorePos(pos); |
| const char kFormStream[] = "/Form\0stream"; |
| const CFX_ByteStringC kFormStreamStr(kFormStream, sizeof(kFormStream) - 1); |
| bForm = m_Syntax.SearchMultiWord(kFormStreamStr, TRUE, size) == 0; |
| m_Syntax.RestorePos(SavedPos); |
| return TRUE; |
| } |
| |
| CPDF_Object* CPDF_Parser::ParseIndirectObject(CPDF_IndirectObjects* pObjList, |
| FX_DWORD objnum, |
| PARSE_CONTEXT* pContext) { |
| if (!IsValidObjectNumber(objnum)) |
| return nullptr; |
| |
| if (m_V5Type[objnum] == 1 || m_V5Type[objnum] == 255) { |
| FX_FILESIZE pos = m_ObjectInfo[objnum].pos; |
| if (pos <= 0) |
| return nullptr; |
| return ParseIndirectObjectAt(pObjList, pos, objnum, pContext); |
| } |
| if (m_V5Type[objnum] != 2) |
| return nullptr; |
| |
| CPDF_StreamAcc* pObjStream = GetObjectStream(m_ObjectInfo[objnum].pos); |
| 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); |
| const int32_t offset = GetStreamFirst(pObjStream); |
| |
| // Read object numbers from |pObjStream| into a cache. |
| if (!pdfium::ContainsKey(m_ObjCache, pObjStream)) { |
| for (int32_t i = GetStreamNCount(pObjStream); i > 0; --i) { |
| FX_DWORD thisnum = syntax.GetDirectNum(); |
| FX_DWORD thisoff = syntax.GetDirectNum(); |
| m_ObjCache[pObjStream][thisnum] = thisoff; |
| } |
| } |
| |
| const auto it = m_ObjCache[pObjStream].find(objnum); |
| if (it == m_ObjCache[pObjStream].end()) |
| return nullptr; |
| |
| syntax.RestorePos(offset + it->second); |
| return syntax.GetObject(pObjList, 0, 0, pContext); |
| } |
| |
| CPDF_StreamAcc* CPDF_Parser::GetObjectStream(FX_DWORD objnum) { |
| CPDF_StreamAcc* pStreamAcc = nullptr; |
| if (m_ObjectStreamMap.Lookup((void*)(uintptr_t)objnum, (void*&)pStreamAcc)) |
| return pStreamAcc; |
| |
| const CPDF_Stream* pStream = |
| 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); |
| return pStreamAcc; |
| } |
| FX_FILESIZE CPDF_Parser::GetObjectSize(FX_DWORD objnum) { |
| if (!IsValidObjectNumber(objnum)) |
| return 0; |
| |
| if (m_V5Type[objnum] == 2) { |
| objnum = m_ObjectInfo[objnum].pos; |
| } |
| if (m_V5Type[objnum] == 1 || m_V5Type[objnum] == 255) { |
| FX_FILESIZE offset = m_ObjectInfo[objnum].pos; |
| if (offset == 0) { |
| return 0; |
| } |
| void* pResult = FXSYS_bsearch(&offset, m_SortedOffset.GetData(), |
| m_SortedOffset.GetSize(), sizeof(FX_FILESIZE), |
| CompareFileSize); |
| if (!pResult) { |
| return 0; |
| } |
| if ((FX_FILESIZE*)pResult - (FX_FILESIZE*)m_SortedOffset.GetData() == |
| m_SortedOffset.GetSize() - 1) { |
| return 0; |
| } |
| return ((FX_FILESIZE*)pResult)[1] - offset; |
| } |
| return 0; |
| } |
| void CPDF_Parser::GetIndirectBinary(FX_DWORD objnum, |
| uint8_t*& pBuffer, |
| FX_DWORD& size) { |
| pBuffer = NULL; |
| size = 0; |
| if (!IsValidObjectNumber(objnum)) |
| return; |
| |
| if (m_V5Type[objnum] == 2) { |
| CPDF_StreamAcc* pObjStream = GetObjectStream(m_ObjectInfo[objnum].pos); |
| if (!pObjStream) |
| return; |
| |
| int32_t offset = GetStreamFirst(pObjStream); |
| const uint8_t* pData = pObjStream->GetData(); |
| FX_DWORD totalsize = pObjStream->GetSize(); |
| ScopedFileStream file( |
| FX_CreateMemoryStream((uint8_t*)pData, (size_t)totalsize, FALSE)); |
| 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) |
| continue; |
| |
| if (i == 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; |
| } |
| return; |
| } |
| |
| if (m_V5Type[objnum] != 1) |
| return; |
| |
| FX_FILESIZE pos = m_ObjectInfo[objnum].pos; |
| 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() != "obj") { |
| m_Syntax.RestorePos(SavedPos); |
| return; |
| } |
| void* pResult = |
| FXSYS_bsearch(&pos, m_SortedOffset.GetData(), m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| if (!pResult) { |
| 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 (word == "xref") { |
| bNextOffValid = TRUE; |
| } else if (bIsNumber) { |
| word = m_Syntax.GetNextWord(bIsNumber); |
| if (bIsNumber && m_Syntax.GetKeyword() == "obj") { |
| bNextOffValid = TRUE; |
| } |
| } |
| } |
| if (!bNextOffValid) { |
| m_Syntax.RestorePos(pos); |
| while (1) { |
| if (m_Syntax.GetKeyword() == "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); |
| } |
| |
| CPDF_Object* CPDF_Parser::ParseIndirectObjectAt(CPDF_IndirectObjects* pObjList, |
| FX_FILESIZE pos, |
| FX_DWORD objnum, |
| PARSE_CONTEXT* pContext) { |
| 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 NULL; |
| } |
| FX_FILESIZE objOffset = m_Syntax.SavePos(); |
| objOffset -= word.GetLength(); |
| FX_DWORD parser_objnum = FXSYS_atoi(word); |
| if (objnum && parser_objnum != objnum) { |
| m_Syntax.RestorePos(SavedPos); |
| return NULL; |
| } |
| word = m_Syntax.GetNextWord(bIsNumber); |
| if (!bIsNumber) { |
| m_Syntax.RestorePos(SavedPos); |
| return NULL; |
| } |
| FX_DWORD parser_gennum = FXSYS_atoi(word); |
| if (m_Syntax.GetKeyword() != "obj") { |
| m_Syntax.RestorePos(SavedPos); |
| return NULL; |
| } |
| CPDF_Object* pObj = |
| m_Syntax.GetObject(pObjList, objnum, parser_gennum, pContext); |
| m_Syntax.SavePos(); |
| CFX_ByteString bsWord = m_Syntax.GetKeyword(); |
| if (bsWord == "endobj") { |
| m_Syntax.SavePos(); |
| } |
| m_Syntax.RestorePos(SavedPos); |
| if (pObj) { |
| if (!objnum) { |
| pObj->m_ObjNum = parser_objnum; |
| } |
| pObj->m_GenNum = parser_gennum; |
| } |
| return pObj; |
| } |
| CPDF_Object* CPDF_Parser::ParseIndirectObjectAtByStrict( |
| CPDF_IndirectObjects* pObjList, |
| FX_FILESIZE pos, |
| FX_DWORD objnum, |
| struct PARSE_CONTEXT* pContext, |
| FX_FILESIZE* pResultPos) { |
| 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 NULL; |
| } |
| FX_DWORD parser_objnum = FXSYS_atoi(word); |
| if (objnum && parser_objnum != objnum) { |
| m_Syntax.RestorePos(SavedPos); |
| return NULL; |
| } |
| word = m_Syntax.GetNextWord(bIsNumber); |
| if (!bIsNumber) { |
| m_Syntax.RestorePos(SavedPos); |
| return NULL; |
| } |
| FX_DWORD gennum = FXSYS_atoi(word); |
| if (m_Syntax.GetKeyword() != "obj") { |
| m_Syntax.RestorePos(SavedPos); |
| return NULL; |
| } |
| CPDF_Object* pObj = |
| m_Syntax.GetObjectByStrict(pObjList, objnum, gennum, pContext); |
| if (pResultPos) { |
| *pResultPos = m_Syntax.m_Pos; |
| } |
| m_Syntax.RestorePos(SavedPos); |
| return pObj; |
| } |
| |
| CPDF_Dictionary* CPDF_Parser::LoadTrailerV4() { |
| if (m_Syntax.GetKeyword() != "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) { |
| return (FX_DWORD)-1; |
| } |
| FX_DWORD dwPermission = m_pSecurityHandler->GetPermissions(); |
| if (m_pEncryptDict && m_pEncryptDict->GetString("Filter") == "Standard") { |
| dwPermission &= 0xFFFFFFFC; |
| dwPermission |= 0xFFFFF0C0; |
| if (bCheckRevision && m_pEncryptDict->GetInteger("R") == 2) { |
| dwPermission &= 0xFFFFF0FF; |
| } |
| } |
| return dwPermission; |
| } |
| FX_BOOL CPDF_Parser::IsOwner() { |
| return !m_pSecurityHandler || m_pSecurityHandler->IsOwner(); |
| } |
| void CPDF_Parser::SetSecurityHandler(CPDF_SecurityHandler* pSecurityHandler, |
| FX_BOOL bForced) { |
| m_bForceUseSecurityHandler = bForced; |
| m_pSecurityHandler.reset(pSecurityHandler); |
| if (m_bForceUseSecurityHandler) { |
| return; |
| } |
| m_Syntax.m_pCryptoHandler.reset(pSecurityHandler->CreateCryptoHandler()); |
| m_Syntax.m_pCryptoHandler->Init(NULL, pSecurityHandler); |
| } |
| FX_BOOL CPDF_Parser::IsLinearizedFile(IFX_FileRead* pFileAccess, |
| FX_DWORD offset) { |
| m_Syntax.InitParser(pFileAccess, offset); |
| m_Syntax.RestorePos(m_Syntax.m_HeaderOffset + 9); |
| FX_FILESIZE SavedPos = m_Syntax.SavePos(); |
| FX_BOOL bIsNumber; |
| CFX_ByteString word = m_Syntax.GetNextWord(bIsNumber); |
| if (!bIsNumber) { |
| return FALSE; |
| } |
| FX_DWORD objnum = FXSYS_atoi(word); |
| word = m_Syntax.GetNextWord(bIsNumber); |
| if (!bIsNumber) { |
| return FALSE; |
| } |
| FX_DWORD gennum = FXSYS_atoi(word); |
| if (m_Syntax.GetKeyword() != "obj") { |
| m_Syntax.RestorePos(SavedPos); |
| return FALSE; |
| } |
| m_pLinearized = m_Syntax.GetObject(NULL, objnum, gennum, 0); |
| if (!m_pLinearized) { |
| return FALSE; |
| } |
| |
| CPDF_Dictionary* pDict = m_pLinearized->GetDict(); |
| if (pDict && pDict->GetElement("Linearized")) { |
| m_Syntax.GetNextWord(bIsNumber); |
| |
| CPDF_Object* pLen = pDict->GetElement("L"); |
| if (!pLen) { |
| m_pLinearized->Release(); |
| m_pLinearized = NULL; |
| return FALSE; |
| } |
| if (pLen->GetInteger() != (int)pFileAccess->GetSize()) { |
| return FALSE; |
| } |
| |
| if (CPDF_Number* pNo = ToNumber(pDict->GetElement("P"))) |
| m_dwFirstPageNo = pNo->GetInteger(); |
| |
| if (CPDF_Number* pTable = ToNumber(pDict->GetElement("T"))) |
| m_LastXRefOffset = pTable->GetInteger(); |
| |
| return TRUE; |
| } |
| m_pLinearized->Release(); |
| m_pLinearized = NULL; |
| return FALSE; |
| } |
| FX_DWORD CPDF_Parser::StartAsynParse(IFX_FileRead* pFileAccess, |
| FX_BOOL bReParse, |
| FX_BOOL bOwnFileRead) { |
| CloseParser(bReParse); |
| m_bXRefStream = FALSE; |
| m_LastXRefOffset = 0; |
| m_bOwnFileRead = bOwnFileRead; |
| int32_t offset = GetHeaderOffset(pFileAccess); |
| if (offset == -1) { |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| if (!IsLinearizedFile(pFileAccess, offset)) { |
| m_Syntax.m_pFileAccess = NULL; |
| return StartParse(pFileAccess, bReParse, bOwnFileRead); |
| } |
| if (!bReParse) { |
| m_pDocument = new CPDF_Document(this); |
| } |
| FX_FILESIZE dwFirstXRefOffset = m_Syntax.SavePos(); |
| FX_BOOL bXRefRebuilt = FALSE; |
| FX_BOOL bLoadV4 = FALSE; |
| if (!(bLoadV4 = LoadCrossRefV4(dwFirstXRefOffset, 0, FALSE)) && |
| !LoadCrossRefV5(&dwFirstXRefOffset, TRUE)) { |
| if (!RebuildCrossRef()) { |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| bXRefRebuilt = TRUE; |
| m_LastXRefOffset = 0; |
| } |
| if (bLoadV4) { |
| m_pTrailer = LoadTrailerV4(); |
| if (!m_pTrailer) { |
| return FALSE; |
| } |
| int32_t xrefsize = GetDirectInteger(m_pTrailer, "Size"); |
| if (xrefsize > 0) { |
| m_ObjectInfo[0].pos = 0; |
| m_V5Type.SetSize(xrefsize); |
| } |
| } |
| FX_DWORD dwRet = SetEncryptHandler(); |
| if (dwRet != PDFPARSE_ERROR_SUCCESS) { |
| return dwRet; |
| } |
| m_pDocument->LoadAsynDoc(m_pLinearized->GetDict()); |
| if (!m_pDocument->GetRoot() || m_pDocument->GetPageCount() == 0) { |
| if (bXRefRebuilt) { |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| ReleaseEncryptHandler(); |
| if (!RebuildCrossRef()) { |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| dwRet = SetEncryptHandler(); |
| if (dwRet != PDFPARSE_ERROR_SUCCESS) { |
| return dwRet; |
| } |
| m_pDocument->LoadAsynDoc(m_pLinearized->GetDict()); |
| if (!m_pDocument->GetRoot()) { |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| } |
| FXSYS_qsort(m_SortedOffset.GetData(), m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| if (GetRootObjNum() == 0) { |
| ReleaseEncryptHandler(); |
| if (!RebuildCrossRef() || GetRootObjNum() == 0) |
| return PDFPARSE_ERROR_FORMAT; |
| |
| dwRet = SetEncryptHandler(); |
| if (dwRet != PDFPARSE_ERROR_SUCCESS) { |
| return dwRet; |
| } |
| } |
| if (m_pSecurityHandler && m_pSecurityHandler->IsMetadataEncrypted()) { |
| if (CPDF_Reference* pMetadata = |
| ToReference(m_pDocument->GetRoot()->GetElement("Metadata"))) |
| m_Syntax.m_MetadataObjnum = pMetadata->GetRefObjNum(); |
| } |
| return PDFPARSE_ERROR_SUCCESS; |
| } |
| FX_BOOL CPDF_Parser::LoadLinearizedAllCrossRefV5(FX_FILESIZE xrefpos) { |
| if (!LoadCrossRefV5(&xrefpos, FALSE)) { |
| return FALSE; |
| } |
| std::set<FX_FILESIZE> seen_xrefpos; |
| while (xrefpos) { |
| seen_xrefpos.insert(xrefpos); |
| if (!LoadCrossRefV5(&xrefpos, FALSE)) { |
| return FALSE; |
| } |
| // Check for circular references. |
| if (pdfium::ContainsKey(seen_xrefpos, xrefpos)) { |
| return FALSE; |
| } |
| } |
| m_ObjectStreamMap.InitHashTable(101, FALSE); |
| m_bXRefStream = TRUE; |
| return TRUE; |
| } |
| FX_DWORD CPDF_Parser::LoadLinearizedMainXRefTable() { |
| FX_DWORD dwSaveMetadataObjnum = m_Syntax.m_MetadataObjnum; |
| m_Syntax.m_MetadataObjnum = 0; |
| if (m_pTrailer) { |
| m_pTrailer->Release(); |
| m_pTrailer = NULL; |
| } |
| m_Syntax.RestorePos(m_LastXRefOffset - m_Syntax.m_HeaderOffset); |
| uint8_t ch = 0; |
| FX_DWORD dwCount = 0; |
| m_Syntax.GetNextChar(ch); |
| while (PDFCharIsWhitespace(ch)) { |
| ++dwCount; |
| if (m_Syntax.m_FileLen >= |
| (FX_FILESIZE)(m_Syntax.SavePos() + m_Syntax.m_HeaderOffset)) { |
| break; |
| } |
| m_Syntax.GetNextChar(ch); |
| } |
| m_LastXRefOffset += dwCount; |
| FX_POSITION pos = m_ObjectStreamMap.GetStartPosition(); |
| while (pos) { |
| void* objnum; |
| CPDF_StreamAcc* pStream; |
| m_ObjectStreamMap.GetNextAssoc(pos, objnum, (void*&)pStream); |
| delete pStream; |
| } |
| m_ObjectStreamMap.RemoveAll(); |
| m_ObjCache.clear(); |
| |
| if (!LoadLinearizedAllCrossRefV4(m_LastXRefOffset, m_dwXrefStartObjNum) && |
| !LoadLinearizedAllCrossRefV5(m_LastXRefOffset)) { |
| m_LastXRefOffset = 0; |
| m_Syntax.m_MetadataObjnum = dwSaveMetadataObjnum; |
| return PDFPARSE_ERROR_FORMAT; |
| } |
| FXSYS_qsort(m_SortedOffset.GetData(), m_SortedOffset.GetSize(), |
| sizeof(FX_FILESIZE), CompareFileSize); |
| m_Syntax.m_MetadataObjnum = dwSaveMetadataObjnum; |
| return PDFPARSE_ERROR_SUCCESS; |
| } |
| |
| // static |
| int CPDF_SyntaxParser::s_CurrentRecursionDepth = 0; |
| |
| CPDF_SyntaxParser::CPDF_SyntaxParser() { |
| m_pFileAccess = NULL; |
| m_pFileBuf = NULL; |
| m_BufSize = CPDF_ModuleMgr::kFileBufSize; |
| m_pFileBuf = NULL; |
| m_MetadataObjnum = 0; |
| m_dwWordPos = 0; |
| m_bFileStream = FALSE; |
| } |
| CPDF_SyntaxParser::~CPDF_SyntaxParser() { |
| FX_Free(m_pFileBuf); |
| } |
| |
| FX_BOOL CPDF_SyntaxParser::GetCharAt(FX_FILESIZE pos, uint8_t& ch) { |
| CFX_AutoRestorer<FX_FILESIZE> save_pos(&m_Pos); |
| m_Pos = pos; |
| return GetNextChar(ch); |
| } |
| |
| FX_BOOL CPDF_SyntaxParser::GetNextChar(uint8_t& ch) { |
| FX_FILESIZE pos = m_Pos + m_HeaderOffset; |
| if (pos >= m_FileLen) { |
| return FALSE; |
| } |
| if (m_BufOffset >= pos || (FX_FILESIZE)(m_BufOffset + m_BufSize) <= pos) { |
| FX_FILESIZE read_pos = pos; |
| FX_DWORD read_size = m_BufSize; |
| if ((FX_FILESIZE)read_size > m_FileLen) { |
| read_size = (FX_DWORD)m_FileLen; |
| } |
| if ((FX_FILESIZE)(read_pos + read_size) > m_FileLen) { |
| if (m_FileLen < (FX_FILESIZE)read_size) { |
| read_pos = 0; |
| read_size = (FX_DWORD)m_FileLen; |
| } else { |
| read_pos = m_FileLen - read_size; |
| } |
| } |
| if (!m_pFileAccess->ReadBlock(m_pFileBuf, read_pos, read_size)) { |
| return FALSE; |
| } |
| m_BufOffset = read_pos; |
| } |
| ch = m_pFileBuf[pos - m_BufOffset]; |
| m_Pos++; |
| return TRUE; |
| } |
| FX_BOOL CPDF_SyntaxParser::GetCharAtBackward(FX_FILESIZE pos, uint8_t& ch) { |
| pos += m_HeaderOffset; |
| if (pos >= m_FileLen) { |
| return FALSE; |
| } |
| if (m_BufOffset >= pos || (FX_FILESIZE)(m_BufOffset + m_BufSize) <= pos) { |
| FX_FILESIZE read_pos; |
| if (pos < (FX_FILESIZE)m_BufSize) { |
| read_pos = 0; |
| } else { |
| read_pos = pos - m_BufSize + 1; |
| } |
| FX_DWORD read_size = m_BufSize; |
| if ((FX_FILESIZE)(read_pos + read_size) > m_FileLen) { |
| if (m_FileLen < (FX_FILESIZE)read_size) { |
| read_pos = 0; |
| read_size = (FX_DWORD)m_FileLen; |
| } else { |
| read_pos = m_FileLen - read_size; |
| } |
| } |
| if (!m_pFileAccess->ReadBlock(m_pFileBuf, read_pos, read_size)) { |
| return FALSE; |
| } |
| m_BufOffset = read_pos; |
| } |
| ch = m_pFileBuf[pos - m_BufOffset]; |
| return TRUE; |
| } |
| FX_BOOL CPDF_SyntaxParser::ReadBlock(uint8_t* pBuf, FX_DWORD size) { |
| if (!m_pFileAccess->ReadBlock(pBuf, m_Pos + m_HeaderOffset, size)) { |
| return FALSE; |
| } |
| m_Pos += size; |
| return TRUE; |
| } |
| #define MAX_WORD_BUFFER 256 |
| void CPDF_SyntaxParser::GetNextWord() { |
| m_WordSize = 0; |
| m_bIsNumber = TRUE; |
| uint8_t ch; |
| if (!GetNextChar(ch)) { |
| return; |
| } |
| while (1) { |
| while (PDFCharIsWhitespace(ch)) { |
| if (!GetNextChar(ch)) |
| return; |
| } |
| if (ch != '%') |
| break; |
| |
| while (1) { |
| if (!GetNextChar(ch)) |
| return; |
| if (PDFCharIsLineEnding(ch)) |
| break; |
| } |
| } |
| |
| if (PDFCharIsDelimiter(ch)) { |
| m_bIsNumber = FALSE; |
| m_WordBuffer[m_WordSize++] = ch; |
| if (ch == '/') { |
| while (1) { |
| if (!GetNextChar(ch)) |
| return; |
| |
| if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) { |
| m_Pos--; |
| return; |
| } |
| |
| if (m_WordSize < MAX_WORD_BUFFER) |
| m_WordBuffer[m_WordSize++] = ch; |
| } |
| } else if (ch == '<') { |
| if (!GetNextChar(ch)) |
| return; |
| if (ch == '<') |
| m_WordBuffer[m_WordSize++] = ch; |
| else |
| m_Pos--; |
| } else if (ch == '>') { |
| if (!GetNextChar(ch)) |
| return; |
| if (ch == '>') |
| m_WordBuffer[m_WordSize++] = ch; |
| else |
| m_Pos--; |
| } |
| return; |
| } |
| |
| while (1) { |
| if (m_WordSize < MAX_WORD_BUFFER) |
| m_WordBuffer[m_WordSize++] = ch; |
| |
| if (!PDFCharIsNumeric(ch)) |
| m_bIsNumber = FALSE; |
| if (!GetNextChar(ch)) |
| return; |
| |
| if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) { |
| m_Pos--; |
| break; |
| } |
| } |
| } |
| CFX_ByteString CPDF_SyntaxParser::ReadString() { |
| uint8_t ch; |
| if (!GetNextChar(ch)) { |
| return CFX_ByteString(); |
| } |
| CFX_ByteTextBuf buf; |
| int32_t parlevel = 0; |
| int32_t status = 0, iEscCode = 0; |
| while (1) { |
| switch (status) { |
| case 0: |
| if (ch == ')') { |
| if (parlevel == 0) { |
| return buf.GetByteString(); |
| } |
| parlevel--; |
| buf.AppendChar(')'); |
| } else if (ch == '(') { |
| parlevel++; |
| buf.AppendChar('('); |
| } else if (ch == '\\') { |
| status = 1; |
| } else { |
| buf.AppendChar(ch); |
| } |
| break; |
| case 1: |
| if (ch >= '0' && ch <= '7') { |
| iEscCode = FXSYS_toDecimalDigit(ch); |
| status = 2; |
| break; |
| } |
| if (ch == 'n') { |
| buf.AppendChar('\n'); |
| } else if (ch == 'r') { |
| buf.AppendChar('\r'); |
| } else if (ch == 't') { |
| buf.AppendChar('\t'); |
| } else if (ch == 'b') { |
| buf.AppendChar('\b'); |
| } else if (ch == 'f') { |
| buf.AppendChar('\f'); |
| } else if (ch == '\r') { |
| status = 4; |
| break; |
| } else if (ch == '\n') { |
| } else { |
| buf.AppendChar(ch); |
| } |
| status = 0; |
| break; |
| case 2: |
| if (ch >= '0' && ch <= '7') { |
| iEscCode = iEscCode * 8 + FXSYS_toDecimalDigit(ch); |
| status = 3; |
| } else { |
| buf.AppendChar(iEscCode); |
| status = 0; |
| continue; |
| } |
| break; |
| case 3: |
| if (ch >= '0' && ch <= '7') { |
| iEscCode = iEscCode * 8 + FXSYS_toDecimalDigit(ch); |
| buf.AppendChar(iEscCode); |
| status = 0; |
| } else { |
| buf.AppendChar(iEscCode); |
| status = 0; |
| continue; |
| } |
| break; |
| case 4: |
| status = 0; |
| if (ch != '\n') { |
| continue; |
| } |
| break; |
| } |
| if (!GetNextChar(ch)) { |
| break; |
| } |
| } |
| GetNextChar(ch); |
| return buf.GetByteString(); |
| } |
| CFX_ByteString CPDF_SyntaxParser::ReadHexString() { |
| uint8_t ch; |
| if (!GetNextChar(ch)) |
| return CFX_ByteString(); |
| |
| CFX_BinaryBuf buf; |
| bool bFirst = true; |
| uint8_t code = 0; |
| while (1) { |
| if (ch == '>') |
| break; |
| |
| if (std::isxdigit(ch)) { |
| int val = FXSYS_toHexDigit(ch); |
| if (bFirst) { |
| code = val * 16; |
| } else { |
| code += val; |
| buf.AppendByte((uint8_t)code); |
| } |
| bFirst = !bFirst; |
| } |
| |
| if (!GetNextChar(ch)) |
| break; |
| } |
| if (!bFirst) |
| buf.AppendByte((uint8_t)code); |
| |
| return buf.GetByteString(); |
| } |
| void CPDF_SyntaxParser::ToNextLine() { |
| uint8_t ch; |
| while (GetNextChar(ch)) { |
| if (ch == '\n') { |
| break; |
| } |
| if (ch == '\r') { |
| GetNextChar(ch); |
| if (ch != '\n') { |
| --m_Pos; |
| } |
| break; |
| } |
| } |
| } |
| void CPDF_SyntaxParser::ToNextWord() { |
| uint8_t ch; |
| if (!GetNextChar(ch)) |
| return; |
| |
| while (1) { |
| while (PDFCharIsWhitespace(ch)) { |
| m_dwWordPos = m_Pos; |
| if (!GetNextChar(ch)) |
| return; |
| } |
| |
| if (ch != '%') |
| break; |
| |
| while (1) { |
| if (!GetNextChar(ch)) |
| return; |
| if (PDFCharIsLineEnding(ch)) |
| break; |
| } |
| } |
| m_Pos--; |
| } |
| |
| CFX_ByteString CPDF_SyntaxParser::GetNextWord(FX_BOOL& bIsNumber) { |
| GetNextWord(); |
| bIsNumber = m_bIsNumber; |
| return CFX_ByteString((const FX_CHAR*)m_WordBuffer, m_WordSize); |
| } |
| CFX_ByteString CPDF_SyntaxParser::GetKeyword() { |
| GetNextWord(); |
| return CFX_ByteString((const FX_CHAR*)m_WordBuffer, m_WordSize); |
| } |
| CPDF_Object* CPDF_SyntaxParser::GetObject(CPDF_IndirectObjects* pObjList, |
| FX_DWORD objnum, |
| FX_DWORD gennum, |
| PARSE_CONTEXT* pContext, |
| FX_BOOL bDecrypt) { |
| CFX_AutoRestorer<int> restorer(&s_CurrentRecursionDepth); |
| if (++s_CurrentRecursionDepth > kParserMaxRecursionDepth) { |
| return NULL; |
| } |
| FX_FILESIZE SavedPos = m_Pos; |
| FX_BOOL bTypeOnly = pContext && (pContext->m_Flags & PDFPARSE_TYPEONLY); |
| FX_BOOL bIsNumber; |
| CFX_ByteString word = GetNextWord(bIsNumber); |
| if (word.GetLength() == 0) { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_INVALID; |
| return NULL; |
| } |
| if (bIsNumber) { |
| FX_FILESIZE SavedPos = m_Pos; |
| CFX_ByteString nextword = GetNextWord(bIsNumber); |
| if (bIsNumber) { |
| CFX_ByteString nextword2 = GetNextWord(bIsNumber); |
| if (nextword2 == "R") { |
| FX_DWORD objnum = FXSYS_atoi(word); |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_REFERENCE; |
| return new CPDF_Reference(pObjList, objnum); |
| } |
| } |
| m_Pos = SavedPos; |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_NUMBER; |
| return new CPDF_Number(word); |
| } |
| if (word == "true" || word == "false") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_BOOLEAN; |
| return new CPDF_Boolean(word == "true"); |
| } |
| if (word == "null") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_NULL; |
| return new CPDF_Null; |
| } |
| if (word == "(") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_STRING; |
| CFX_ByteString str = ReadString(); |
| if (m_pCryptoHandler && bDecrypt) { |
| m_pCryptoHandler->Decrypt(objnum, gennum, str); |
| } |
| return new CPDF_String(str, FALSE); |
| } |
| if (word == "<") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_STRING; |
| CFX_ByteString str = ReadHexString(); |
| if (m_pCryptoHandler && bDecrypt) { |
| m_pCryptoHandler->Decrypt(objnum, gennum, str); |
| } |
| return new CPDF_String(str, TRUE); |
| } |
| if (word == "[") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_ARRAY; |
| CPDF_Array* pArray = new CPDF_Array; |
| while (CPDF_Object* pObj = GetObject(pObjList, objnum, gennum)) |
| pArray->Add(pObj); |
| |
| return pArray; |
| } |
| if (word[0] == '/') { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_NAME; |
| return new CPDF_Name( |
| PDF_NameDecode(CFX_ByteStringC(m_WordBuffer + 1, m_WordSize - 1))); |
| } |
| if (word == "<<") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_DICTIONARY; |
| |
| if (pContext) |
| pContext->m_DictStart = SavedPos; |
| |
| int32_t nKeys = 0; |
| FX_FILESIZE dwSignValuePos = 0; |
| nonstd::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>> pDict( |
| new CPDF_Dictionary); |
| while (1) { |
| FX_BOOL bIsNumber; |
| CFX_ByteString key = GetNextWord(bIsNumber); |
| if (key.IsEmpty()) |
| return nullptr; |
| |
| FX_FILESIZE SavedPos = m_Pos - key.GetLength(); |
| if (key == ">>") |
| break; |
| |
| if (key == "endobj") { |
| m_Pos = SavedPos; |
| break; |
| } |
| if (key[0] != '/') |
| continue; |
| |
| ++nKeys; |
| key = PDF_NameDecode(key); |
| if (key.IsEmpty()) |
| continue; |
| |
| if (key == "/Contents") |
| dwSignValuePos = m_Pos; |
| |
| CPDF_Object* pObj = GetObject(pObjList, objnum, gennum); |
| if (!pObj) |
| continue; |
| |
| CFX_ByteStringC keyNoSlash(key.c_str() + 1, key.GetLength() - 1); |
| // 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.get())) { |
| FX_FILESIZE dwSavePos = m_Pos; |
| m_Pos = dwSignValuePos; |
| CPDF_Object* pObj = GetObject(pObjList, objnum, gennum, NULL, FALSE); |
| pDict->SetAt("Contents", pObj); |
| m_Pos = dwSavePos; |
| } |
| if (pContext) { |
| pContext->m_DictEnd = m_Pos; |
| if (pContext->m_Flags & PDFPARSE_NOSTREAM) { |
| return pDict.release(); |
| } |
| } |
| FX_FILESIZE SavedPos = m_Pos; |
| FX_BOOL bIsNumber; |
| CFX_ByteString nextword = GetNextWord(bIsNumber); |
| if (nextword != "stream") { |
| m_Pos = SavedPos; |
| return pDict.release(); |
| } |
| |
| return ReadStream(pDict.release(), pContext, objnum, gennum); |
| } |
| if (word == ">>") { |
| m_Pos = SavedPos; |
| return nullptr; |
| } |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_INVALID; |
| |
| return nullptr; |
| } |
| |
| CPDF_Object* CPDF_SyntaxParser::GetObjectByStrict( |
| CPDF_IndirectObjects* pObjList, |
| FX_DWORD objnum, |
| FX_DWORD gennum, |
| struct PARSE_CONTEXT* pContext) { |
| CFX_AutoRestorer<int> restorer(&s_CurrentRecursionDepth); |
| if (++s_CurrentRecursionDepth > kParserMaxRecursionDepth) { |
| return NULL; |
| } |
| FX_FILESIZE SavedPos = m_Pos; |
| FX_BOOL bTypeOnly = pContext && (pContext->m_Flags & PDFPARSE_TYPEONLY); |
| FX_BOOL bIsNumber; |
| CFX_ByteString word = GetNextWord(bIsNumber); |
| if (word.GetLength() == 0) { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_INVALID; |
| return nullptr; |
| } |
| if (bIsNumber) { |
| FX_FILESIZE SavedPos = m_Pos; |
| CFX_ByteString nextword = GetNextWord(bIsNumber); |
| if (bIsNumber) { |
| CFX_ByteString nextword2 = GetNextWord(bIsNumber); |
| if (nextword2 == "R") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_REFERENCE; |
| FX_DWORD objnum = FXSYS_atoi(word); |
| return new CPDF_Reference(pObjList, objnum); |
| } |
| } |
| m_Pos = SavedPos; |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_NUMBER; |
| return new CPDF_Number(word); |
| } |
| if (word == "true" || word == "false") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_BOOLEAN; |
| return new CPDF_Boolean(word == "true"); |
| } |
| if (word == "null") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_NULL; |
| return new CPDF_Null; |
| } |
| if (word == "(") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_STRING; |
| CFX_ByteString str = ReadString(); |
| if (m_pCryptoHandler) |
| m_pCryptoHandler->Decrypt(objnum, gennum, str); |
| return new CPDF_String(str, FALSE); |
| } |
| if (word == "<") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_STRING; |
| CFX_ByteString str = ReadHexString(); |
| if (m_pCryptoHandler) |
| m_pCryptoHandler->Decrypt(objnum, gennum, str); |
| return new CPDF_String(str, TRUE); |
| } |
| if (word == "[") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_ARRAY; |
| nonstd::unique_ptr<CPDF_Array, ReleaseDeleter<CPDF_Array>> pArray( |
| new CPDF_Array); |
| while (CPDF_Object* pObj = GetObject(pObjList, objnum, gennum)) |
| pArray->Add(pObj); |
| return m_WordBuffer[0] == ']' ? pArray.release() : nullptr; |
| } |
| if (word[0] == '/') { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_NAME; |
| return new CPDF_Name( |
| PDF_NameDecode(CFX_ByteStringC(m_WordBuffer + 1, m_WordSize - 1))); |
| } |
| if (word == "<<") { |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_DICTIONARY; |
| if (pContext) |
| pContext->m_DictStart = SavedPos; |
| |
| nonstd::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>> pDict( |
| new CPDF_Dictionary); |
| while (1) { |
| FX_BOOL bIsNumber; |
| FX_FILESIZE SavedPos = m_Pos; |
| CFX_ByteString key = GetNextWord(bIsNumber); |
| if (key.IsEmpty()) |
| return nullptr; |
| |
| if (key == ">>") |
| break; |
| |
| if (key == "endobj") { |
| m_Pos = SavedPos; |
| break; |
| } |
| if (key[0] != '/') |
| continue; |
| |
| key = PDF_NameDecode(key); |
| nonstd::unique_ptr<CPDF_Object, ReleaseDeleter<CPDF_Object>> obj( |
| GetObject(pObjList, objnum, gennum)); |
| if (!obj) { |
| uint8_t ch; |
| while (GetNextChar(ch) && ch != 0x0A && ch != 0x0D) { |
| } |
| return nullptr; |
| } |
| if (key.GetLength() > 1) { |
| pDict->AddValue(CFX_ByteStringC(key.c_str() + 1, key.GetLength() - 1), |
| obj.release()); |
| } |
| } |
| if (pContext) { |
| pContext->m_DictEnd = m_Pos; |
| if (pContext->m_Flags & PDFPARSE_NOSTREAM) { |
| return pDict.release(); |
| } |
| } |
| FX_FILESIZE SavedPos = m_Pos; |
| FX_BOOL bIsNumber; |
| CFX_ByteString nextword = GetNextWord(bIsNumber); |
| if (nextword != "stream") { |
| m_Pos = SavedPos; |
| return pDict.release(); |
| } |
| |
| return ReadStream(pDict.release(), pContext, objnum, gennum); |
| } |
| if (word == ">>") { |
| m_Pos = SavedPos; |
| return nullptr; |
| } |
| if (bTypeOnly) |
| return (CPDF_Object*)PDFOBJ_INVALID; |
| 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("Length"); |
| 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; |
| if (pContext) { |
| pContext->m_DataStart = streamStartPos; |
| } |
| |
| const CFX_ByteStringC kEndStreamStr("endstream"); |
| const CFX_ByteStringC kEndObjStr("endobj"); |
| CPDF_CryptoHandler* pCryptoHandler = |
| 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, kEndStreamStr.GetLength() + 1); |
| GetNextWord(); |
| // Earlier version of PDF specification doesn't require EOL marker before |
| // 'endstream' keyword. If keyword 'endstream' follows the bytes in |
| // specified length, it signals the end of stream. |
| if (FXSYS_memcmp(m_WordBuffer, kEndStreamStr.GetPtr(), |
| kEndStreamStr.GetLength()) == 0) { |
| 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(kEndStreamStr, 0); |
| if (endStreamOffset < 0) { |
| // Can't find any "endstream". |
| break; |
| } |
| if (IsWholeWord(m_Pos - kEndStreamStr.GetLength(), m_FileLen, |
| kEndStreamStr, TRUE)) { |
| // Stop searching when the keyword "endstream" is found. |
| endStreamOffset = m_Pos - streamStartPos - kEndStreamStr.GetLength(); |
| break; |
| } |
| } |
| m_Pos = streamStartPos; |
| FX_FILESIZE endObjOffset = 0; |
| while (endObjOffset >= 0) { |
| endObjOffset = FindTag(kEndObjStr, 0); |
| if (endObjOffset < 0) { |
| // Can't find any "endobj". |
| break; |
| } |
| if (IsWholeWord(m_Pos - kEndObjStr.GetLength(), m_FileLen, kEndObjStr, |
| TRUE)) { |
| // Stop searching when the keyword "endobj" is found. |
| endObjOffset = m_Pos - streamStartPos - kEndObjStr.GetLength(); |
| break; |
| } |
| } |
| if (endStreamOffset < 0 && endObjOffset < 0) { |
| // Can't find "endstream" or "endobj". |
| pDict->Release(); |
| return nullptr; |
| } |
| if (endStreamOffset < 0 && endObjOffset >= 0) { |
| // Correct the position of end stream. |
| endStreamOffset = endObjOffset; |
| } else if (endStreamOffset >= 0 && endObjOffset < 0) { |
| // Correct the position of end obj. |
| endObjOffset = endStreamOffset; |
| } else if (endStreamOffset > endObjOffset) { |
| endStreamOffset = endObjOffset; |
| } |
| len = endStreamOffset; |
| int numMarkers = ReadEOLMarkers(streamStartPos + endStreamOffset - 2); |
| if (numMarkers == 2) { |
| len -= 2; |
| } else { |
| numMarkers = ReadEOLMarkers(streamStartPos + endStreamOffset - 1); |
| if (numMarkers == 1) { |
| len -= 1; |
| } |
| } |
| if (len < 0) { |
| pDict->Release(); |
| return nullptr; |
| } |
| pDict->SetAtInteger("Length", len); |
| } |
| m_Pos = streamStartPos; |
| } |
| if (len < 0) { |
| pDict->Release(); |
| 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; |
| FXSYS_memset(m_WordBuffer, 0, kEndObjStr.GetLength() + 1); |
| GetNextWord(); |
| int numMarkers = ReadEOLMarkers(m_Pos); |
| if (m_WordSize == kEndObjStr.GetLength() && numMarkers != 0 && |
| FXSYS_memcmp(m_WordBuffer, kEndObjStr.GetPtr(), kEndObjStr.GetLength()) == |
| 0) { |
| m_Pos = streamStartPos; |
| } |
| return pStream; |
| } |
| void CPDF_SyntaxParser::InitParser(IFX_FileRead* pFileAccess, |
| FX_DWORD HeaderOffset) { |
| FX_Free(m_pFileBuf); |
| m_pFileBuf = FX_Alloc(uint8_t, m_BufSize); |
| m_HeaderOffset = HeaderOffset; |
| m_FileLen = pFileAccess->GetSize(); |
| m_Pos = 0; |
| m_pFileAccess = pFileAccess; |
| m_BufOffset = 0; |
| pFileAccess->ReadBlock( |
| m_pFileBuf, 0, |
| (size_t)((FX_FILESIZE)m_BufSize > m_FileLen ? m_FileLen : m_BufSize)); |
| } |
| int32_t CPDF_SyntaxParser::GetDirectNum() { |
| GetNextWord(); |
| if (!m_bIsNumber) { |
| return 0; |
| } |
| m_WordBuffer[m_WordSize] = 0; |
| return FXSYS_atoi((const FX_CHAR*)m_WordBuffer); |
| } |
| |
| bool CPDF_SyntaxParser::IsWholeWord(FX_FILESIZE startpos, |
| FX_FILESIZE limit, |
| const CFX_ByteStringC& tag, |
| FX_BOOL checkKeyword) { |
| const FX_DWORD taglen = tag.GetLength(); |
| 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)) { |
| if (PDFCharIsNumeric(ch) || PDFCharIsOther(ch) || |
| (checkKeyword && PDFCharIsDelimiter(ch))) { |
| return false; |
| } |
| } |
| |
| if (bCheckLeft && startpos > 0 && GetCharAt(startpos - 1, ch)) { |
| if (PDFCharIsNumeric(ch) || PDFCharIsOther(ch) || |
| (checkKeyword && PDFCharIsDelimiter(ch))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| FX_BOOL CPDF_SyntaxParser::SearchWord(const CFX_ByteStringC& tag, |
| FX_BOOL bWholeWord, |
| FX_BOOL bForward, |
| FX_FILESIZE limit) { |
| int32_t taglen = tag.GetLength(); |
|