1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "core/include/fpdfapi/fpdf_module.h"
8 #include "core/include/fpdfapi/fpdf_page.h"
9 #include "core/include/fpdfapi/fpdf_serial.h"
10 #include "core/src/fpdfapi/fpdf_page/pageint.h"
11 
operator <<(CFX_ByteTextBuf & ar,CFX_Matrix & matrix)12 CFX_ByteTextBuf& operator<<(CFX_ByteTextBuf& ar, CFX_Matrix& matrix) {
13   ar << matrix.a << " " << matrix.b << " " << matrix.c << " " << matrix.d << " "
14      << matrix.e << " " << matrix.f;
15   return ar;
16 }
CPDF_PageContentGenerate(CPDF_Page * pPage)17 CPDF_PageContentGenerate::CPDF_PageContentGenerate(CPDF_Page* pPage)
18     : m_pPage(pPage) {
19   m_pDocument = NULL;
20   if (m_pPage) {
21     m_pDocument = m_pPage->m_pDocument;
22   }
23   FX_POSITION pos = pPage->GetFirstObjectPosition();
24   while (pos) {
25     InsertPageObject(pPage->GetNextObject(pos));
26   }
27 }
~CPDF_PageContentGenerate()28 CPDF_PageContentGenerate::~CPDF_PageContentGenerate() {}
InsertPageObject(CPDF_PageObject * pPageObject)29 FX_BOOL CPDF_PageContentGenerate::InsertPageObject(
30     CPDF_PageObject* pPageObject) {
31   if (!pPageObject) {
32     return FALSE;
33   }
34   return m_pageObjects.Add(pPageObject);
35 }
GenerateContent()36 void CPDF_PageContentGenerate::GenerateContent() {
37   CFX_ByteTextBuf buf;
38   CPDF_Dictionary* pPageDict = m_pPage->m_pFormDict;
39   for (int i = 0; i < m_pageObjects.GetSize(); ++i) {
40     CPDF_PageObject* pPageObj = m_pageObjects[i];
41     if (!pPageObj || pPageObj->m_Type != PDFPAGE_IMAGE) {
42       continue;
43     }
44     ProcessImage(buf, (CPDF_ImageObject*)pPageObj);
45   }
46   CPDF_Object* pContent =
47       pPageDict ? pPageDict->GetElementValue("Contents") : NULL;
48   if (pContent) {
49     pPageDict->RemoveAt("Contents");
50   }
51   CPDF_Stream* pStream = new CPDF_Stream(NULL, 0, NULL);
52   pStream->SetData(buf.GetBuffer(), buf.GetLength(), FALSE, FALSE);
53   m_pDocument->AddIndirectObject(pStream);
54   pPageDict->SetAtReference("Contents", m_pDocument, pStream->GetObjNum());
55 }
RealizeResource(CPDF_Object * pResourceObj,const FX_CHAR * szType)56 CFX_ByteString CPDF_PageContentGenerate::RealizeResource(
57     CPDF_Object* pResourceObj,
58     const FX_CHAR* szType) {
59   if (!m_pPage->m_pResources) {
60     m_pPage->m_pResources = new CPDF_Dictionary;
61     int objnum = m_pDocument->AddIndirectObject(m_pPage->m_pResources);
62     m_pPage->m_pFormDict->SetAtReference("Resources", m_pDocument, objnum);
63   }
64   CPDF_Dictionary* pResList = m_pPage->m_pResources->GetDict(szType);
65   if (!pResList) {
66     pResList = new CPDF_Dictionary;
67     m_pPage->m_pResources->SetAt(szType, pResList);
68   }
69   m_pDocument->AddIndirectObject(pResourceObj);
70   CFX_ByteString name;
71   int idnum = 1;
72   while (1) {
73     name.Format("FX%c%d", szType[0], idnum);
74     if (!pResList->KeyExist(name)) {
75       break;
76     }
77     idnum++;
78   }
79   pResList->AddReference(name, m_pDocument, pResourceObj->GetObjNum());
80   return name;
81 }
ProcessImage(CFX_ByteTextBuf & buf,CPDF_ImageObject * pImageObj)82 void CPDF_PageContentGenerate::ProcessImage(CFX_ByteTextBuf& buf,
83                                             CPDF_ImageObject* pImageObj) {
84   if ((pImageObj->m_Matrix.a == 0 && pImageObj->m_Matrix.b == 0) ||
85       (pImageObj->m_Matrix.c == 0 && pImageObj->m_Matrix.d == 0)) {
86     return;
87   }
88   buf << "q " << pImageObj->m_Matrix << " cm ";
89   if (!pImageObj->m_pImage->IsInline()) {
90     CPDF_Stream* pStream = pImageObj->m_pImage->GetStream();
91     FX_DWORD dwSavedObjNum = pStream->GetObjNum();
92     CFX_ByteString name = RealizeResource(pStream, "XObject");
93     if (dwSavedObjNum == 0) {
94       if (pImageObj->m_pImage)
95         pImageObj->m_pImage->Release();
96       pImageObj->m_pImage = m_pDocument->GetPageData()->GetImage(pStream);
97     }
98     buf << "/" << PDF_NameEncode(name) << " Do Q\n";
99   }
100 }
ProcessForm(CFX_ByteTextBuf & buf,const uint8_t * data,FX_DWORD size,CFX_Matrix & matrix)101 void CPDF_PageContentGenerate::ProcessForm(CFX_ByteTextBuf& buf,
102                                            const uint8_t* data,
103                                            FX_DWORD size,
104                                            CFX_Matrix& matrix) {
105   if (!data || !size) {
106     return;
107   }
108   CPDF_Stream* pStream = new CPDF_Stream(NULL, 0, NULL);
109   CPDF_Dictionary* pFormDict = new CPDF_Dictionary;
110   pFormDict->SetAtName("Type", "XObject");
111   pFormDict->SetAtName("Subtype", "Form");
112   CFX_FloatRect bbox = m_pPage->GetPageBBox();
113   matrix.TransformRect(bbox);
114   pFormDict->SetAtRect("BBox", bbox);
115   pStream->InitStream((uint8_t*)data, size, pFormDict);
116   buf << "q " << matrix << " cm ";
117   CFX_ByteString name = RealizeResource(pStream, "XObject");
118   buf << "/" << PDF_NameEncode(name) << " Do Q\n";
119 }
TransformContent(CFX_Matrix & matrix)120 void CPDF_PageContentGenerate::TransformContent(CFX_Matrix& matrix) {
121   CPDF_Dictionary* pDict = m_pPage->m_pFormDict;
122   CPDF_Object* pContent = pDict ? pDict->GetElementValue("Contents") : NULL;
123   if (!pContent)
124     return;
125 
126   CFX_ByteTextBuf buf;
127   if (CPDF_Array* pArray = pContent->AsArray()) {
128     int iCount = pArray->GetCount();
129     CPDF_StreamAcc** pContentArray = FX_Alloc(CPDF_StreamAcc*, iCount);
130     int size = 0;
131     int i = 0;
132     for (i = 0; i < iCount; ++i) {
133       pContent = pArray->GetElement(i);
134       CPDF_Stream* pStream = ToStream(pContent);
135       if (!pStream)
136         continue;
137 
138       CPDF_StreamAcc* pStreamAcc = new CPDF_StreamAcc();
139       pStreamAcc->LoadAllData(pStream);
140       pContentArray[i] = pStreamAcc;
141       size += pContentArray[i]->GetSize() + 1;
142     }
143     int pos = 0;
144     uint8_t* pBuf = FX_Alloc(uint8_t, size);
145     for (i = 0; i < iCount; ++i) {
146       FXSYS_memcpy(pBuf + pos, pContentArray[i]->GetData(),
147                    pContentArray[i]->GetSize());
148       pos += pContentArray[i]->GetSize() + 1;
149       pBuf[pos - 1] = ' ';
150       delete pContentArray[i];
151     }
152     ProcessForm(buf, pBuf, size, matrix);
153     FX_Free(pBuf);
154     FX_Free(pContentArray);
155   } else if (CPDF_Stream* pStream = pContent->AsStream()) {
156     CPDF_StreamAcc contentStream;
157     contentStream.LoadAllData(pStream);
158     ProcessForm(buf, contentStream.GetData(), contentStream.GetSize(), matrix);
159   }
160   CPDF_Stream* pStream = new CPDF_Stream(NULL, 0, NULL);
161   pStream->SetData(buf.GetBuffer(), buf.GetLength(), FALSE, FALSE);
162   m_pDocument->AddIndirectObject(pStream);
163   m_pPage->m_pFormDict->SetAtReference("Contents", m_pDocument,
164                                        pStream->GetObjNum());
165 }
166