summaryrefslogtreecommitdiff
path: root/core/fpdfapi/edit/cpdf_xrefstream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/fpdfapi/edit/cpdf_xrefstream.cpp')
-rw-r--r--core/fpdfapi/edit/cpdf_xrefstream.cpp412
1 files changed, 412 insertions, 0 deletions
diff --git a/core/fpdfapi/edit/cpdf_xrefstream.cpp b/core/fpdfapi/edit/cpdf_xrefstream.cpp
new file mode 100644
index 0000000000..9ec27f291c
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_xrefstream.cpp
@@ -0,0 +1,412 @@
+// Copyright 2017 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/edit/cpdf_xrefstream.h"
+
+#include "core/fpdfapi/edit/cpdf_creator.h"
+#include "core/fpdfapi/edit/cpdf_flateencoder.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+
+namespace {
+
+int32_t WriteTrailer(CPDF_Document* pDocument,
+ CFX_FileBufferArchive* pFile,
+ CPDF_Array* pIDArray) {
+ FX_FILESIZE offset = 0;
+ int32_t len = 0;
+ CPDF_Parser* pParser = pDocument->GetParser();
+ if (pParser) {
+ CPDF_Dictionary* p = pParser->GetTrailer();
+ for (const auto& it : *p) {
+ const CFX_ByteString& key = it.first;
+ CPDF_Object* pValue = it.second.get();
+ if (key == "Encrypt" || key == "Size" || key == "Filter" ||
+ key == "Index" || key == "Length" || key == "Prev" || key == "W" ||
+ key == "XRefStm" || key == "Type" || key == "ID") {
+ continue;
+ }
+ if (key == "DecodeParms")
+ continue;
+ if (pFile->AppendString(("/")) < 0)
+ return -1;
+ if ((len = pFile->AppendString(PDF_NameEncode(key).AsStringC())) < 0)
+ return -1;
+
+ offset += len + 1;
+ if (!pValue->IsInline()) {
+ if (pFile->AppendString(" ") < 0)
+ return -1;
+ if ((len = pFile->AppendDWord(pValue->GetObjNum())) < 0)
+ return -1;
+ if (pFile->AppendString(" 0 R ") < 0)
+ return -1;
+
+ offset += len + 6;
+ } else {
+ if (!pValue->WriteTo(pFile, &offset))
+ return -1;
+ }
+ }
+ if (pIDArray) {
+ if (pFile->AppendString(("/ID")) < 0)
+ return -1;
+
+ offset += 3;
+ if (!pIDArray->WriteTo(pFile, &offset))
+ return -1;
+ }
+ return offset;
+ }
+ if (pFile->AppendString("\r\n/Root ") < 0)
+ return -1;
+ if ((len = pFile->AppendDWord(pDocument->GetRoot()->GetObjNum())) < 0)
+ return -1;
+ if (pFile->AppendString(" 0 R\r\n") < 0)
+ return -1;
+
+ offset += len + 14;
+ if (pDocument->GetInfo()) {
+ if (pFile->AppendString("/Info ") < 0)
+ return -1;
+ if ((len = pFile->AppendDWord(pDocument->GetInfo()->GetObjNum())) < 0)
+ return -1;
+ if (pFile->AppendString(" 0 R\r\n") < 0)
+ return -1;
+
+ offset += len + 12;
+ }
+ if (pIDArray) {
+ if (pFile->AppendString(("/ID")) < 0)
+ return -1;
+
+ offset += 3;
+ if (!pIDArray->WriteTo(pFile, &offset))
+ return -1;
+ }
+ return offset;
+}
+
+int32_t WriteEncryptDictObjectReference(uint32_t dwObjNum,
+ CFX_FileBufferArchive* pFile) {
+ ASSERT(pFile);
+
+ FX_FILESIZE offset = 0;
+ int32_t len = 0;
+ if (pFile->AppendString("/Encrypt") < 0)
+ return -1;
+
+ offset += 8;
+ if (pFile->AppendString(" ") < 0)
+ return -1;
+ if ((len = pFile->AppendDWord(dwObjNum)) < 0)
+ return -1;
+ if (pFile->AppendString(" 0 R ") < 0)
+ return -1;
+
+ offset += len + 6;
+ return offset;
+}
+
+void AppendIndex0(CFX_ByteTextBuf& buffer, bool bFirstObject) {
+ buffer.AppendByte(0);
+ buffer.AppendByte(0);
+ buffer.AppendByte(0);
+ buffer.AppendByte(0);
+ buffer.AppendByte(0);
+
+ const uint8_t byte = bFirstObject ? 0xFF : 0;
+ buffer.AppendByte(byte);
+ buffer.AppendByte(byte);
+}
+
+void AppendIndex1(CFX_ByteTextBuf& buffer, FX_FILESIZE offset) {
+ buffer.AppendByte(1);
+ buffer.AppendByte(static_cast<uint8_t>(offset >> 24));
+ buffer.AppendByte(static_cast<uint8_t>(offset >> 16));
+ buffer.AppendByte(static_cast<uint8_t>(offset >> 8));
+ buffer.AppendByte(static_cast<uint8_t>(offset));
+ buffer.AppendByte(0);
+ buffer.AppendByte(0);
+}
+
+void AppendIndex2(CFX_ByteTextBuf& buffer, uint32_t objnum, int32_t index) {
+ buffer.AppendByte(2);
+ buffer.AppendByte(static_cast<uint8_t>(objnum >> 24));
+ buffer.AppendByte(static_cast<uint8_t>(objnum >> 16));
+ buffer.AppendByte(static_cast<uint8_t>(objnum >> 8));
+ buffer.AppendByte(static_cast<uint8_t>(objnum));
+ buffer.AppendByte(static_cast<uint8_t>(index >> 8));
+ buffer.AppendByte(static_cast<uint8_t>(index));
+}
+
+} // namespace
+
+CPDF_XRefStream::CPDF_XRefStream()
+ : m_PrevOffset(0), m_dwTempObjNum(0), m_iSeg(0) {}
+
+CPDF_XRefStream::~CPDF_XRefStream() {}
+
+bool CPDF_XRefStream::Start() {
+ m_IndexArray.clear();
+ m_Buffer.Clear();
+ m_iSeg = 0;
+ return true;
+}
+
+int32_t CPDF_XRefStream::CompressIndirectObject(uint32_t dwObjNum,
+ const CPDF_Object* pObj,
+ CPDF_Creator* pCreator) {
+ ASSERT(pCreator);
+
+ m_ObjStream.CompressIndirectObject(dwObjNum, pObj);
+ if (m_ObjStream.ItemCount() < pCreator->GetObjectStreamSize() &&
+ m_ObjStream.IsNotFull()) {
+ return 1;
+ }
+ return EndObjectStream(pCreator, true);
+}
+
+int32_t CPDF_XRefStream::CompressIndirectObject(uint32_t dwObjNum,
+ const uint8_t* pBuffer,
+ uint32_t dwSize,
+ CPDF_Creator* pCreator) {
+ ASSERT(pCreator);
+
+ m_ObjStream.CompressIndirectObject(dwObjNum, pBuffer, dwSize);
+ if (m_ObjStream.ItemCount() < pCreator->GetObjectStreamSize() &&
+ m_ObjStream.IsNotFull()) {
+ return 1;
+ }
+ return EndObjectStream(pCreator, true);
+}
+
+int32_t CPDF_XRefStream::EndObjectStream(CPDF_Creator* pCreator, bool bEOF) {
+ FX_FILESIZE objOffset = 0;
+ if (bEOF) {
+ objOffset = m_ObjStream.End(pCreator);
+ if (objOffset < 0)
+ return -1;
+ }
+
+ if (!m_ObjStream.GetObjectNumber())
+ m_ObjStream.SetObjectNumber(pCreator->GetNextObjectNumber());
+
+ int32_t iSize = m_ObjStream.ItemCount();
+ size_t iSeg = m_IndexArray.size();
+ if (!pCreator->IsIncremental()) {
+ if (m_dwTempObjNum == 0) {
+ AppendIndex0(m_Buffer, true);
+ m_dwTempObjNum++;
+ }
+ uint32_t end_num = m_IndexArray.back().objnum + m_IndexArray.back().count;
+ int index = 0;
+ for (; m_dwTempObjNum < end_num; m_dwTempObjNum++) {
+ if (pCreator->HasObjectNumber(m_dwTempObjNum)) {
+ if (index >= iSize ||
+ m_dwTempObjNum != m_ObjStream.GetObjectNumberForItem(index)) {
+ AppendIndex1(m_Buffer, pCreator->GetObjectOffset(m_dwTempObjNum));
+ } else {
+ AppendIndex2(m_Buffer, m_ObjStream.GetObjectNumber(), index++);
+ }
+ } else {
+ AppendIndex0(m_Buffer, false);
+ }
+ }
+ if (iSize > 0 && bEOF)
+ pCreator->SetObjectOffset(m_ObjStream.GetObjectNumber(), objOffset);
+
+ m_iSeg = iSeg;
+ if (bEOF)
+ m_ObjStream.Start();
+
+ return 1;
+ }
+ for (auto it = m_IndexArray.begin() + m_iSeg; it != m_IndexArray.end();
+ ++it) {
+ for (uint32_t m = it->objnum; m < it->objnum + it->count; ++m) {
+ if (m_ObjStream.GetIndex() >= iSize ||
+ m != m_ObjStream.GetObjectNumberForItem(it - m_IndexArray.begin())) {
+ AppendIndex1(m_Buffer, pCreator->GetObjectOffset(m));
+ } else {
+ AppendIndex2(m_Buffer, m_ObjStream.GetObjectNumber(),
+ m_ObjStream.GetIndex());
+ m_ObjStream.IncrementIndex();
+ }
+ }
+ }
+ if (iSize > 0 && bEOF) {
+ AppendIndex1(m_Buffer, objOffset);
+ m_IndexArray.push_back({m_ObjStream.GetObjectNumber(), 1});
+ iSeg += 1;
+ }
+ m_iSeg = iSeg;
+ if (bEOF)
+ m_ObjStream.Start();
+
+ return 1;
+}
+
+bool CPDF_XRefStream::GenerateXRefStream(CPDF_Creator* pCreator, bool bEOF) {
+ FX_FILESIZE offset_tmp = pCreator->GetOffset();
+ uint32_t objnum = pCreator->GetNextObjectNumber();
+ CFX_FileBufferArchive* pFile = pCreator->GetFile();
+ if (pCreator->IsIncremental()) {
+ AddObjectNumberToIndexArray(objnum);
+ } else {
+ for (; m_dwTempObjNum < pCreator->GetLastObjectNumber(); m_dwTempObjNum++) {
+ if (pCreator->HasObjectNumber(m_dwTempObjNum))
+ AppendIndex1(m_Buffer, pCreator->GetObjectOffset(m_dwTempObjNum));
+ else
+ AppendIndex0(m_Buffer, false);
+ }
+ }
+
+ AppendIndex1(m_Buffer, offset_tmp);
+
+ int32_t len = pFile->AppendDWord(objnum);
+ if (len < 0)
+ return false;
+
+ pCreator->IncrementOffset(len);
+ if ((len = pFile->AppendString(" 0 obj\r\n<</Type /XRef/W[1 4 2]/Index[")) <
+ 0) {
+ return false;
+ }
+ pCreator->IncrementOffset(len);
+ if (!pCreator->IsIncremental()) {
+ if ((len = pFile->AppendDWord(0)) < 0)
+ return false;
+ if ((len = pFile->AppendString(" ")) < 0)
+ return false;
+
+ pCreator->IncrementOffset(len + 1);
+ if ((len = pFile->AppendDWord(objnum + 1)) < 0)
+ return false;
+
+ pCreator->IncrementOffset(len);
+ } else {
+ for (const auto& pair : m_IndexArray) {
+ if ((len = pFile->AppendDWord(pair.objnum)) < 0)
+ return false;
+ if (pFile->AppendString(" ") < 0)
+ return false;
+
+ pCreator->IncrementOffset(len + 1);
+ if ((len = pFile->AppendDWord(pair.count)) < 0)
+ return false;
+ if (pFile->AppendString(" ") < 0)
+ return false;
+
+ pCreator->IncrementOffset(len + 1);
+ }
+ }
+ if (pFile->AppendString("]/Size ") < 0)
+ return false;
+ if ((len = pFile->AppendDWord(objnum + 1)) < 0)
+ return false;
+
+ pCreator->IncrementOffset(len + 7);
+ if (m_PrevOffset > 0) {
+ if (pFile->AppendString("/Prev ") < 0)
+ return false;
+
+ char offset_buf[20];
+ memset(offset_buf, 0, sizeof(offset_buf));
+ FXSYS_i64toa(m_PrevOffset, offset_buf, 10);
+ int32_t offset_len = (int32_t)FXSYS_strlen(offset_buf);
+ if (pFile->AppendBlock(offset_buf, offset_len) < 0)
+ return false;
+
+ pCreator->IncrementOffset(offset_len + 6);
+ }
+
+ CPDF_FlateEncoder encoder(m_Buffer.GetBuffer(), m_Buffer.GetLength(), true,
+ true);
+ if (pFile->AppendString("/Filter /FlateDecode") < 0)
+ return false;
+
+ pCreator->IncrementOffset(20);
+ if ((len = pFile->AppendString("/DecodeParms<</Columns 7/Predictor 12>>")) <
+ 0) {
+ return false;
+ }
+
+ pCreator->IncrementOffset(len);
+ if (pFile->AppendString("/Length ") < 0)
+ return false;
+ if ((len = pFile->AppendDWord(encoder.GetSize())) < 0)
+ return false;
+
+ pCreator->IncrementOffset(len + 8);
+ if (bEOF) {
+ if ((len = WriteTrailer(pCreator->GetDocument(), pFile,
+ pCreator->GetIDArray())) < 0) {
+ return false;
+ }
+ pCreator->IncrementOffset(len);
+
+ if (CPDF_Dictionary* encryptDict = pCreator->GetEncryptDict()) {
+ uint32_t dwEncryptObjNum = encryptDict->GetObjNum();
+ if (dwEncryptObjNum == 0)
+ dwEncryptObjNum = pCreator->GetEncryptObjectNumber();
+ if ((len = WriteEncryptDictObjectReference(dwEncryptObjNum, pFile)) < 0)
+ return false;
+ pCreator->IncrementOffset(len);
+ }
+ }
+ if ((len = pFile->AppendString(">>stream\r\n")) < 0)
+ return false;
+
+ pCreator->IncrementOffset(len);
+ if (pFile->AppendBlock(encoder.GetData(), encoder.GetSize()) < 0)
+ return false;
+ if ((len = pFile->AppendString("\r\nendstream\r\nendobj\r\n")) < 0)
+ return false;
+
+ pCreator->IncrementOffset(encoder.GetSize() + len);
+ m_PrevOffset = offset_tmp;
+ return true;
+}
+
+bool CPDF_XRefStream::End(CPDF_Creator* pCreator, bool bEOF) {
+ if (EndObjectStream(pCreator, bEOF) < 0)
+ return false;
+ return GenerateXRefStream(pCreator, bEOF);
+}
+
+bool CPDF_XRefStream::EndXRefStream(CPDF_Creator* pCreator) {
+ if (!pCreator->IsIncremental()) {
+ AppendIndex0(m_Buffer, true);
+ for (uint32_t i = 1; i < pCreator->GetLastObjectNumber() + 1; i++) {
+ if (pCreator->HasObjectNumber(i))
+ AppendIndex1(m_Buffer, pCreator->GetObjectOffset(i));
+ else
+ AppendIndex0(m_Buffer, false);
+ }
+ } else {
+ for (const auto& pair : m_IndexArray) {
+ for (uint32_t j = pair.objnum; j < pair.objnum + pair.count; ++j)
+ AppendIndex1(m_Buffer, pCreator->GetObjectOffset(j));
+ }
+ }
+ return GenerateXRefStream(pCreator, false);
+}
+
+void CPDF_XRefStream::AddObjectNumberToIndexArray(uint32_t objnum) {
+ if (m_IndexArray.empty()) {
+ m_IndexArray.push_back({objnum, 1});
+ return;
+ }
+
+ uint32_t next_objnum = m_IndexArray.back().objnum + m_IndexArray.back().count;
+ if (objnum == next_objnum)
+ m_IndexArray.back().count += 1;
+ else
+ m_IndexArray.push_back({objnum, 1});
+}