OLD | NEW |
| (Empty) |
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 "public/fpdf_ppo.h" | |
8 | |
9 #include <map> | |
10 #include <memory> | |
11 #include <vector> | |
12 | |
13 #include "core/include/fpdfapi/cpdf_array.h" | |
14 #include "core/include/fpdfapi/cpdf_document.h" | |
15 #include "core/include/fpdfapi/cpdf_name.h" | |
16 #include "core/include/fpdfapi/cpdf_number.h" | |
17 #include "core/include/fpdfapi/cpdf_reference.h" | |
18 #include "core/include/fpdfapi/cpdf_string.h" | |
19 #include "fpdfsdk/include/fsdk_define.h" | |
20 #include "third_party/base/stl_util.h" | |
21 | |
22 class CPDF_PageOrganizer { | |
23 public: | |
24 using ObjectNumberMap = std::map<FX_DWORD, FX_DWORD>; | |
25 CPDF_PageOrganizer(); | |
26 ~CPDF_PageOrganizer(); | |
27 | |
28 FX_BOOL PDFDocInit(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc); | |
29 FX_BOOL ExportPage(CPDF_Document* pSrcPDFDoc, | |
30 std::vector<FX_WORD>* pPageNums, | |
31 CPDF_Document* pDestPDFDoc, | |
32 int nIndex); | |
33 CPDF_Object* PageDictGetInheritableTag(CPDF_Dictionary* pDict, | |
34 CFX_ByteString nSrctag); | |
35 FX_BOOL UpdateReference(CPDF_Object* pObj, | |
36 CPDF_Document* pDoc, | |
37 ObjectNumberMap* pObjNumberMap); | |
38 FX_DWORD GetNewObjId(CPDF_Document* pDoc, | |
39 ObjectNumberMap* pObjNumberMap, | |
40 CPDF_Reference* pRef); | |
41 }; | |
42 | |
43 CPDF_PageOrganizer::CPDF_PageOrganizer() {} | |
44 | |
45 CPDF_PageOrganizer::~CPDF_PageOrganizer() {} | |
46 | |
47 FX_BOOL CPDF_PageOrganizer::PDFDocInit(CPDF_Document* pDestPDFDoc, | |
48 CPDF_Document* pSrcPDFDoc) { | |
49 if (!pDestPDFDoc || !pSrcPDFDoc) | |
50 return FALSE; | |
51 | |
52 CPDF_Dictionary* pNewRoot = pDestPDFDoc->GetRoot(); | |
53 if (!pNewRoot) | |
54 return FALSE; | |
55 | |
56 // Set the document information | |
57 CPDF_Dictionary* DInfoDict = pDestPDFDoc->GetInfo(); | |
58 if (!DInfoDict) | |
59 return FALSE; | |
60 | |
61 CFX_ByteString producerstr; | |
62 producerstr.Format("PDFium"); | |
63 DInfoDict->SetAt("Producer", new CPDF_String(producerstr, FALSE)); | |
64 | |
65 // Set type | |
66 CFX_ByteString cbRootType = pNewRoot->GetStringBy("Type", ""); | |
67 if (cbRootType.Equal("")) { | |
68 pNewRoot->SetAt("Type", new CPDF_Name("Catalog")); | |
69 } | |
70 | |
71 CPDF_Object* pElement = pNewRoot->GetElement("Pages"); | |
72 CPDF_Dictionary* pNewPages = | |
73 pElement ? ToDictionary(pElement->GetDirect()) : nullptr; | |
74 if (!pNewPages) { | |
75 pNewPages = new CPDF_Dictionary; | |
76 FX_DWORD NewPagesON = pDestPDFDoc->AddIndirectObject(pNewPages); | |
77 pNewRoot->SetAt("Pages", new CPDF_Reference(pDestPDFDoc, NewPagesON)); | |
78 } | |
79 | |
80 CFX_ByteString cbPageType = pNewPages->GetStringBy("Type", ""); | |
81 if (cbPageType.Equal("")) { | |
82 pNewPages->SetAt("Type", new CPDF_Name("Pages")); | |
83 } | |
84 | |
85 CPDF_Array* pKeysArray = pNewPages->GetArrayBy("Kids"); | |
86 if (!pKeysArray) { | |
87 CPDF_Array* pNewKids = new CPDF_Array; | |
88 FX_DWORD Kidsobjnum = -1; | |
89 Kidsobjnum = pDestPDFDoc->AddIndirectObject(pNewKids); | |
90 | |
91 pNewPages->SetAt("Kids", new CPDF_Reference(pDestPDFDoc, Kidsobjnum)); | |
92 pNewPages->SetAt("Count", new CPDF_Number(0)); | |
93 } | |
94 | |
95 return TRUE; | |
96 } | |
97 | |
98 FX_BOOL CPDF_PageOrganizer::ExportPage(CPDF_Document* pSrcPDFDoc, | |
99 std::vector<FX_WORD>* pPageNums, | |
100 CPDF_Document* pDestPDFDoc, | |
101 int nIndex) { | |
102 int curpage = nIndex; | |
103 std::unique_ptr<ObjectNumberMap> pObjNumberMap(new ObjectNumberMap); | |
104 int nSize = pdfium::CollectionSize<int>(*pPageNums); | |
105 for (int i = 0; i < nSize; ++i) { | |
106 CPDF_Dictionary* pCurPageDict = pDestPDFDoc->CreateNewPage(curpage); | |
107 CPDF_Dictionary* pSrcPageDict = pSrcPDFDoc->GetPage(pPageNums->at(i) - 1); | |
108 if (!pSrcPageDict || !pCurPageDict) | |
109 return FALSE; | |
110 | |
111 // Clone the page dictionary | |
112 for (const auto& it : *pSrcPageDict) { | |
113 const CFX_ByteString& cbSrcKeyStr = it.first; | |
114 CPDF_Object* pObj = it.second; | |
115 if (cbSrcKeyStr.Compare(("Type")) && cbSrcKeyStr.Compare(("Parent"))) { | |
116 if (pCurPageDict->KeyExist(cbSrcKeyStr)) | |
117 pCurPageDict->RemoveAt(cbSrcKeyStr); | |
118 pCurPageDict->SetAt(cbSrcKeyStr, pObj->Clone()); | |
119 } | |
120 } | |
121 | |
122 // inheritable item | |
123 CPDF_Object* pInheritable = nullptr; | |
124 // 1 MediaBox //required | |
125 if (!pCurPageDict->KeyExist("MediaBox")) { | |
126 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "MediaBox"); | |
127 if (!pInheritable) { | |
128 // Search the "CropBox" from source page dictionary, | |
129 // if not exists,we take the letter size. | |
130 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox"); | |
131 if (pInheritable) { | |
132 pCurPageDict->SetAt("MediaBox", pInheritable->Clone()); | |
133 } else { | |
134 // Make the default size to be letter size (8.5'x11') | |
135 CPDF_Array* pArray = new CPDF_Array; | |
136 pArray->AddNumber(0); | |
137 pArray->AddNumber(0); | |
138 pArray->AddNumber(612); | |
139 pArray->AddNumber(792); | |
140 pCurPageDict->SetAt("MediaBox", pArray); | |
141 } | |
142 } else { | |
143 pCurPageDict->SetAt("MediaBox", pInheritable->Clone()); | |
144 } | |
145 } | |
146 // 2 Resources //required | |
147 if (!pCurPageDict->KeyExist("Resources")) { | |
148 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Resources"); | |
149 if (!pInheritable) | |
150 return FALSE; | |
151 pCurPageDict->SetAt("Resources", pInheritable->Clone()); | |
152 } | |
153 // 3 CropBox //Optional | |
154 if (!pCurPageDict->KeyExist("CropBox")) { | |
155 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox"); | |
156 if (pInheritable) | |
157 pCurPageDict->SetAt("CropBox", pInheritable->Clone()); | |
158 } | |
159 // 4 Rotate //Optional | |
160 if (!pCurPageDict->KeyExist("Rotate")) { | |
161 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Rotate"); | |
162 if (pInheritable) | |
163 pCurPageDict->SetAt("Rotate", pInheritable->Clone()); | |
164 } | |
165 | |
166 // Update the reference | |
167 FX_DWORD dwOldPageObj = pSrcPageDict->GetObjNum(); | |
168 FX_DWORD dwNewPageObj = pCurPageDict->GetObjNum(); | |
169 | |
170 (*pObjNumberMap)[dwOldPageObj] = dwNewPageObj; | |
171 | |
172 UpdateReference(pCurPageDict, pDestPDFDoc, pObjNumberMap.get()); | |
173 ++curpage; | |
174 } | |
175 | |
176 return TRUE; | |
177 } | |
178 | |
179 CPDF_Object* CPDF_PageOrganizer::PageDictGetInheritableTag( | |
180 CPDF_Dictionary* pDict, | |
181 CFX_ByteString nSrctag) { | |
182 if (!pDict || nSrctag.IsEmpty()) | |
183 return nullptr; | |
184 if (!pDict->KeyExist("Parent") || !pDict->KeyExist("Type")) | |
185 return nullptr; | |
186 | |
187 CPDF_Object* pType = pDict->GetElement("Type")->GetDirect(); | |
188 if (!ToName(pType)) | |
189 return nullptr; | |
190 if (pType->GetString().Compare("Page")) | |
191 return nullptr; | |
192 | |
193 CPDF_Dictionary* pp = ToDictionary(pDict->GetElement("Parent")->GetDirect()); | |
194 if (!pp) | |
195 return nullptr; | |
196 | |
197 if (pDict->KeyExist((const char*)nSrctag)) | |
198 return pDict->GetElement((const char*)nSrctag); | |
199 | |
200 while (pp) { | |
201 if (pp->KeyExist((const char*)nSrctag)) | |
202 return pp->GetElement((const char*)nSrctag); | |
203 if (!pp->KeyExist("Parent")) | |
204 break; | |
205 pp = ToDictionary(pp->GetElement("Parent")->GetDirect()); | |
206 } | |
207 return nullptr; | |
208 } | |
209 | |
210 FX_BOOL CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj, | |
211 CPDF_Document* pDoc, | |
212 ObjectNumberMap* pObjNumberMap) { | |
213 switch (pObj->GetType()) { | |
214 case CPDF_Object::REFERENCE: { | |
215 CPDF_Reference* pReference = pObj->AsReference(); | |
216 FX_DWORD newobjnum = GetNewObjId(pDoc, pObjNumberMap, pReference); | |
217 if (newobjnum == 0) | |
218 return FALSE; | |
219 pReference->SetRef(pDoc, newobjnum); | |
220 break; | |
221 } | |
222 case CPDF_Object::DICTIONARY: { | |
223 CPDF_Dictionary* pDict = pObj->AsDictionary(); | |
224 auto it = pDict->begin(); | |
225 while (it != pDict->end()) { | |
226 const CFX_ByteString& key = it->first; | |
227 CPDF_Object* pNextObj = it->second; | |
228 ++it; | |
229 if (!FXSYS_strcmp(key, "Parent") || !FXSYS_strcmp(key, "Prev") || | |
230 !FXSYS_strcmp(key, "First")) { | |
231 continue; | |
232 } | |
233 if (pNextObj) { | |
234 if (!UpdateReference(pNextObj, pDoc, pObjNumberMap)) | |
235 pDict->RemoveAt(key); | |
236 } else { | |
237 return FALSE; | |
238 } | |
239 } | |
240 break; | |
241 } | |
242 case CPDF_Object::ARRAY: { | |
243 CPDF_Array* pArray = pObj->AsArray(); | |
244 FX_DWORD count = pArray->GetCount(); | |
245 for (FX_DWORD i = 0; i < count; ++i) { | |
246 CPDF_Object* pNextObj = pArray->GetElement(i); | |
247 if (!pNextObj) | |
248 return FALSE; | |
249 if (!UpdateReference(pNextObj, pDoc, pObjNumberMap)) | |
250 return FALSE; | |
251 } | |
252 break; | |
253 } | |
254 case CPDF_Object::STREAM: { | |
255 CPDF_Stream* pStream = pObj->AsStream(); | |
256 CPDF_Dictionary* pDict = pStream->GetDict(); | |
257 if (pDict) { | |
258 if (!UpdateReference(pDict, pDoc, pObjNumberMap)) | |
259 return FALSE; | |
260 } else { | |
261 return FALSE; | |
262 } | |
263 break; | |
264 } | |
265 default: | |
266 break; | |
267 } | |
268 | |
269 return TRUE; | |
270 } | |
271 | |
272 FX_DWORD CPDF_PageOrganizer::GetNewObjId(CPDF_Document* pDoc, | |
273 ObjectNumberMap* pObjNumberMap, | |
274 CPDF_Reference* pRef) { | |
275 if (!pRef) | |
276 return 0; | |
277 | |
278 FX_DWORD dwObjnum = pRef->GetRefObjNum(); | |
279 FX_DWORD dwNewObjNum = 0; | |
280 const auto it = pObjNumberMap->find(dwObjnum); | |
281 if (it != pObjNumberMap->end()) | |
282 dwNewObjNum = it->second; | |
283 if (dwNewObjNum) | |
284 return dwNewObjNum; | |
285 | |
286 CPDF_Object* pDirect = pRef->GetDirect(); | |
287 if (!pDirect) | |
288 return 0; | |
289 | |
290 CPDF_Object* pClone = pDirect->Clone(); | |
291 if (!pClone) | |
292 return 0; | |
293 | |
294 if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) { | |
295 if (pDictClone->KeyExist("Type")) { | |
296 CFX_ByteString strType = pDictClone->GetStringBy("Type"); | |
297 if (!FXSYS_stricmp(strType, "Pages")) { | |
298 pDictClone->Release(); | |
299 return 4; | |
300 } | |
301 if (!FXSYS_stricmp(strType, "Page")) { | |
302 pDictClone->Release(); | |
303 return 0; | |
304 } | |
305 } | |
306 } | |
307 dwNewObjNum = pDoc->AddIndirectObject(pClone); | |
308 (*pObjNumberMap)[dwObjnum] = dwNewObjNum; | |
309 if (!UpdateReference(pClone, pDoc, pObjNumberMap)) { | |
310 pClone->Release(); | |
311 return 0; | |
312 } | |
313 return dwNewObjNum; | |
314 } | |
315 | |
316 FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring, | |
317 std::vector<FX_WORD>* pageArray, | |
318 int nCount) { | |
319 if (rangstring.GetLength() != 0) { | |
320 rangstring.Remove(' '); | |
321 int nLength = rangstring.GetLength(); | |
322 CFX_ByteString cbCompareString("0123456789-,"); | |
323 for (int i = 0; i < nLength; ++i) { | |
324 if (cbCompareString.Find(rangstring[i]) == -1) | |
325 return FALSE; | |
326 } | |
327 CFX_ByteString cbMidRange; | |
328 int nStringFrom = 0; | |
329 int nStringTo = 0; | |
330 while (nStringTo < nLength) { | |
331 nStringTo = rangstring.Find(',', nStringFrom); | |
332 if (nStringTo == -1) | |
333 nStringTo = nLength; | |
334 cbMidRange = rangstring.Mid(nStringFrom, nStringTo - nStringFrom); | |
335 int nMid = cbMidRange.Find('-'); | |
336 if (nMid == -1) { | |
337 long lPageNum = atol(cbMidRange); | |
338 if (lPageNum <= 0 || lPageNum > nCount) | |
339 return FALSE; | |
340 pageArray->push_back((FX_WORD)lPageNum); | |
341 } else { | |
342 int nStartPageNum = atol(cbMidRange.Mid(0, nMid)); | |
343 if (nStartPageNum == 0) | |
344 return FALSE; | |
345 | |
346 ++nMid; | |
347 int nEnd = cbMidRange.GetLength() - nMid; | |
348 if (nEnd == 0) | |
349 return FALSE; | |
350 | |
351 int nEndPageNum = atol(cbMidRange.Mid(nMid, nEnd)); | |
352 if (nStartPageNum < 0 || nStartPageNum > nEndPageNum || | |
353 nEndPageNum > nCount) { | |
354 return FALSE; | |
355 } | |
356 for (int i = nStartPageNum; i <= nEndPageNum; ++i) { | |
357 pageArray->push_back(i); | |
358 } | |
359 } | |
360 nStringFrom = nStringTo + 1; | |
361 } | |
362 } | |
363 return TRUE; | |
364 } | |
365 | |
366 DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc, | |
367 FPDF_DOCUMENT src_doc, | |
368 FPDF_BYTESTRING pagerange, | |
369 int index) { | |
370 CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(dest_doc); | |
371 if (!dest_doc) | |
372 return FALSE; | |
373 | |
374 CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc); | |
375 if (!pSrcDoc) | |
376 return FALSE; | |
377 | |
378 std::vector<FX_WORD> pageArray; | |
379 int nCount = pSrcDoc->GetPageCount(); | |
380 if (pagerange) { | |
381 if (!ParserPageRangeString(pagerange, &pageArray, nCount)) | |
382 return FALSE; | |
383 } else { | |
384 for (int i = 1; i <= nCount; ++i) { | |
385 pageArray.push_back(i); | |
386 } | |
387 } | |
388 | |
389 CPDF_PageOrganizer pageOrg; | |
390 pageOrg.PDFDocInit(pDestDoc, pSrcDoc); | |
391 return pageOrg.ExportPage(pSrcDoc, &pageArray, pDestDoc, index); | |
392 } | |
393 | |
394 DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, | |
395 FPDF_DOCUMENT src_doc) { | |
396 CPDF_Document* pDstDoc = CPDFDocumentFromFPDFDocument(dest_doc); | |
397 if (!pDstDoc) | |
398 return FALSE; | |
399 | |
400 CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc); | |
401 if (!pSrcDoc) | |
402 return FALSE; | |
403 | |
404 CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot(); | |
405 pSrcDict = pSrcDict->GetDictBy("ViewerPreferences"); | |
406 if (!pSrcDict) | |
407 return FALSE; | |
408 | |
409 CPDF_Dictionary* pDstDict = pDstDoc->GetRoot(); | |
410 if (!pDstDict) | |
411 return FALSE; | |
412 | |
413 pDstDict->SetAt("ViewerPreferences", pSrcDict->Clone(TRUE)); | |
414 return TRUE; | |
415 } | |
OLD | NEW |