// Copyright 2016 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "core/fpdfapi/parser/cpdf_linearized_header.h" #include #include #include #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_number.h" #include "core/fpdfapi/parser/cpdf_syntax_parser.h" #include "third_party/base/ptr_util.h" namespace { constexpr FX_FILESIZE kLinearizedHeaderOffset = 9; constexpr size_t kMaxInt = static_cast(std::numeric_limits::max()); template bool IsValidNumericDictionaryValue(const CPDF_Dictionary* pDict, const char* key, T min_value, bool must_exist = true) { if (!pDict->KeyExist(key)) return !must_exist; const CPDF_Number* pNum = ToNumber(pDict->GetObjectFor(key)); if (!pNum || !pNum->IsInteger()) return false; const int raw_value = pNum->GetInteger(); if (!pdfium::base::IsValueInRangeForNumericType(raw_value)) return false; return static_cast(raw_value) >= min_value; } bool IsLinearizedHeaderValid(const CPDF_LinearizedHeader* header, FX_FILESIZE document_size) { ASSERT(header); return header->GetFileSize() == document_size && header->GetFirstPageNo() < kMaxInt && header->GetFirstPageNo() < header->GetPageCount() && header->GetMainXRefTableFirstEntryOffset() < document_size && header->GetFirstPageEndOffset() < document_size && header->GetLastXRefOffset() < document_size && header->GetHintStart() < document_size; } } // namespace // static std::unique_ptr CPDF_LinearizedHeader::Parse( CPDF_SyntaxParser* parser) { parser->SetPos(kLinearizedHeaderOffset); const auto pDict = ToDictionary( parser->GetIndirectObject(nullptr, CPDF_SyntaxParser::ParseType::kLoose)); if (!pDict || !pDict->KeyExist("Linearized") || !IsValidNumericDictionaryValue(pDict.get(), "L", 1) || !IsValidNumericDictionaryValue(pDict.get(), "P", 0, false) || !IsValidNumericDictionaryValue(pDict.get(), "T", 1) || !IsValidNumericDictionaryValue(pDict.get(), "N", 1) || !IsValidNumericDictionaryValue(pDict.get(), "E", 1) || !IsValidNumericDictionaryValue(pDict.get(), "O", 1)) { return nullptr; } // Move parser to the start of the xref table for the documents first page. // (skpping endobj keyword) if (parser->GetNextWord(nullptr) != "endobj") return nullptr; auto result = pdfium::WrapUnique( new CPDF_LinearizedHeader(pDict.get(), parser->GetPos())); if (!IsLinearizedHeaderValid(result.get(), parser->GetDocumentSize())) return nullptr; return result; } CPDF_LinearizedHeader::CPDF_LinearizedHeader(const CPDF_Dictionary* pDict, FX_FILESIZE szLastXRefOffset) : m_szFileSize(pDict->GetIntegerFor("L")), m_dwFirstPageNo(pDict->GetIntegerFor("P")), m_szMainXRefTableFirstEntryOffset(pDict->GetIntegerFor("T")), m_PageCount(pDict->GetIntegerFor("N")), m_szFirstPageEndOffset(pDict->GetIntegerFor("E")), m_FirstPageObjNum(pDict->GetIntegerFor("O")), m_szLastXRefOffset(szLastXRefOffset) { const CPDF_Array* pHintStreamRange = pDict->GetArrayFor("H"); const size_t nHintStreamSize = pHintStreamRange ? pHintStreamRange->GetCount() : 0; if (nHintStreamSize == 2 || nHintStreamSize == 4) { m_szHintStart = std::max(pHintStreamRange->GetIntegerAt(0), 0); const FX_SAFE_UINT32 safe_hint_length = pHintStreamRange->GetIntegerAt(1); if (safe_hint_length.IsValid()) m_HintLength = safe_hint_length.ValueOrDie(); } } CPDF_LinearizedHeader::~CPDF_LinearizedHeader() {} bool CPDF_LinearizedHeader::HasHintTable() const { return GetPageCount() > 1 && GetHintStart() > 0 && GetHintLength() > 0; }