|  | // 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_stream.h" | 
|  |  | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "constants/stream_dict_common.h" | 
|  | #include "core/fpdfapi/parser/cpdf_dictionary.h" | 
|  | #include "core/fpdfapi/parser/cpdf_encryptor.h" | 
|  | #include "core/fpdfapi/parser/cpdf_flateencoder.h" | 
|  | #include "core/fpdfapi/parser/cpdf_number.h" | 
|  | #include "core/fpdfapi/parser/cpdf_stream_acc.h" | 
|  | #include "core/fpdfapi/parser/fpdf_parser_decode.h" | 
|  | #include "core/fxcrt/fx_stream.h" | 
|  | #include "third_party/base/numerics/safe_conversions.h" | 
|  | #include "third_party/base/stl_util.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool IsMetaDataStreamDictionary(const CPDF_Dictionary* dict) { | 
|  | return dict && dict->GetNameFor("Type") == "Metadata" && | 
|  | dict->GetNameFor("Subtype") == "XML"; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | CPDF_Stream::CPDF_Stream() {} | 
|  |  | 
|  | CPDF_Stream::CPDF_Stream(std::unique_ptr<uint8_t, FxFreeDeleter> pData, | 
|  | uint32_t size, | 
|  | RetainPtr<CPDF_Dictionary> pDict) | 
|  | : m_pDict(std::move(pDict)) { | 
|  | TakeData(std::move(pData), size); | 
|  | } | 
|  |  | 
|  | CPDF_Stream::~CPDF_Stream() { | 
|  | m_ObjNum = kInvalidObjNum; | 
|  | if (m_pDict && m_pDict->GetObjNum() == kInvalidObjNum) | 
|  | m_pDict.Leak();  // lowercase release, release ownership. | 
|  | } | 
|  |  | 
|  | CPDF_Object::Type CPDF_Stream::GetType() const { | 
|  | return kStream; | 
|  | } | 
|  |  | 
|  | CPDF_Dictionary* CPDF_Stream::GetDict() { | 
|  | return m_pDict.Get(); | 
|  | } | 
|  |  | 
|  | const CPDF_Dictionary* CPDF_Stream::GetDict() const { | 
|  | return m_pDict.Get(); | 
|  | } | 
|  |  | 
|  | bool CPDF_Stream::IsStream() const { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | CPDF_Stream* CPDF_Stream::AsStream() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | const CPDF_Stream* CPDF_Stream::AsStream() const { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | void CPDF_Stream::InitStream(pdfium::span<const uint8_t> pData, | 
|  | RetainPtr<CPDF_Dictionary> pDict) { | 
|  | m_pDict = std::move(pDict); | 
|  | SetData(pData); | 
|  | } | 
|  |  | 
|  | void CPDF_Stream::InitStreamFromFile( | 
|  | const RetainPtr<IFX_SeekableReadStream>& pFile, | 
|  | RetainPtr<CPDF_Dictionary> pDict) { | 
|  | m_bMemoryBased = false; | 
|  | m_pDataBuf.reset(); | 
|  | m_pFile = pFile; | 
|  | m_dwSize = pdfium::base::checked_cast<uint32_t>(pFile->GetSize()); | 
|  | m_pDict = std::move(pDict); | 
|  | m_pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(m_dwSize)); | 
|  | } | 
|  |  | 
|  | RetainPtr<CPDF_Object> CPDF_Stream::Clone() const { | 
|  | return CloneObjectNonCyclic(false); | 
|  | } | 
|  |  | 
|  | RetainPtr<CPDF_Object> CPDF_Stream::CloneNonCyclic( | 
|  | bool bDirect, | 
|  | std::set<const CPDF_Object*>* pVisited) const { | 
|  | pVisited->insert(this); | 
|  | auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(this); | 
|  | pAcc->LoadAllDataRaw(); | 
|  |  | 
|  | uint32_t streamSize = pAcc->GetSize(); | 
|  | const CPDF_Dictionary* pDict = GetDict(); | 
|  | RetainPtr<CPDF_Dictionary> pNewDict; | 
|  | if (pDict && !pdfium::Contains(*pVisited, pDict)) { | 
|  | pNewDict = | 
|  | ToDictionary(static_cast<const CPDF_Object*>(pDict)->CloneNonCyclic( | 
|  | bDirect, pVisited)); | 
|  | } | 
|  | return pdfium::MakeRetain<CPDF_Stream>(pAcc->DetachData(), streamSize, | 
|  | std::move(pNewDict)); | 
|  | } | 
|  |  | 
|  | void CPDF_Stream::SetDataAndRemoveFilter(pdfium::span<const uint8_t> pData) { | 
|  | SetData(pData); | 
|  | m_pDict->RemoveFor("Filter"); | 
|  | m_pDict->RemoveFor(pdfium::stream::kDecodeParms); | 
|  | } | 
|  |  | 
|  | void CPDF_Stream::SetDataFromStringstreamAndRemoveFilter( | 
|  | std::ostringstream* stream) { | 
|  | if (stream->tellp() <= 0) { | 
|  | SetDataAndRemoveFilter({}); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SetDataAndRemoveFilter( | 
|  | {reinterpret_cast<const uint8_t*>(stream->str().c_str()), | 
|  | static_cast<size_t>(stream->tellp())}); | 
|  | } | 
|  |  | 
|  | void CPDF_Stream::SetData(pdfium::span<const uint8_t> pData) { | 
|  | std::unique_ptr<uint8_t, FxFreeDeleter> data_copy; | 
|  | if (!pData.empty()) { | 
|  | data_copy.reset(FX_AllocUninit(uint8_t, pData.size())); | 
|  | memcpy(data_copy.get(), pData.data(), pData.size()); | 
|  | } | 
|  | TakeData(std::move(data_copy), pData.size()); | 
|  | } | 
|  |  | 
|  | void CPDF_Stream::TakeData(std::unique_ptr<uint8_t, FxFreeDeleter> pData, | 
|  | uint32_t size) { | 
|  | m_bMemoryBased = true; | 
|  | m_pFile = nullptr; | 
|  | m_pDataBuf = std::move(pData); | 
|  | m_dwSize = size; | 
|  | if (!m_pDict) | 
|  | m_pDict = pdfium::MakeRetain<CPDF_Dictionary>(); | 
|  | m_pDict->SetNewFor<CPDF_Number>("Length", static_cast<int>(size)); | 
|  | } | 
|  |  | 
|  | void CPDF_Stream::SetDataFromStringstream(std::ostringstream* stream) { | 
|  | if (stream->tellp() <= 0) { | 
|  | SetData({}); | 
|  | return; | 
|  | } | 
|  | SetData({reinterpret_cast<const uint8_t*>(stream->str().c_str()), | 
|  | static_cast<size_t>(stream->tellp())}); | 
|  | } | 
|  |  | 
|  | bool CPDF_Stream::ReadRawData(FX_FILESIZE offset, | 
|  | uint8_t* buf, | 
|  | uint32_t size) const { | 
|  | if (!m_bMemoryBased && m_pFile) | 
|  | return m_pFile->ReadBlockAtOffset(buf, offset, size); | 
|  |  | 
|  | if (m_pDataBuf) | 
|  | memcpy(buf, m_pDataBuf.get() + offset, size); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CPDF_Stream::HasFilter() const { | 
|  | return m_pDict && m_pDict->KeyExist("Filter"); | 
|  | } | 
|  |  | 
|  | WideString CPDF_Stream::GetUnicodeText() const { | 
|  | auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(this); | 
|  | pAcc->LoadAllDataFiltered(); | 
|  | return PDF_DecodeText(pAcc->GetSpan()); | 
|  | } | 
|  |  | 
|  | bool CPDF_Stream::WriteTo(IFX_ArchiveStream* archive, | 
|  | const CPDF_Encryptor* encryptor) const { | 
|  | const bool is_metadata = IsMetaDataStreamDictionary(GetDict()); | 
|  | CPDF_FlateEncoder encoder(this, !is_metadata); | 
|  |  | 
|  | std::vector<uint8_t, FxAllocAllocator<uint8_t>> encrypted_data; | 
|  | pdfium::span<const uint8_t> data = encoder.GetSpan(); | 
|  |  | 
|  | if (encryptor && !is_metadata) { | 
|  | encrypted_data = encryptor->Encrypt(data); | 
|  | data = encrypted_data; | 
|  | } | 
|  |  | 
|  | size_t size = data.size(); | 
|  | if (static_cast<size_t>(encoder.GetDict()->GetIntegerFor("Length")) != size) { | 
|  | encoder.CloneDict(); | 
|  | encoder.GetClonedDict()->SetNewFor<CPDF_Number>("Length", | 
|  | static_cast<int>(size)); | 
|  | } | 
|  |  | 
|  | if (!encoder.GetDict()->WriteTo(archive, encryptor)) | 
|  | return false; | 
|  |  | 
|  | if (!archive->WriteString("stream\r\n")) | 
|  | return false; | 
|  |  | 
|  | if (size && !archive->WriteBlock(data.data(), size)) | 
|  | return false; | 
|  |  | 
|  | if (!archive->WriteString("\r\nendstream")) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } |