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 "core/fpdfapi/fpdf_parser/include/cpdf_array.h" | |
8 #include "core/fpdfapi/fpdf_parser/include/cpdf_document.h" | |
9 #include "core/fpdfapi/fpdf_parser/include/fpdf_parser_decode.h" | |
10 #include "core/fpdfdoc/doc_utils.h" | |
11 #include "core/fpdfdoc/cpdf_pagelabel.h" | |
12 #include "core/fpdfdoc/include/cpdf_filespec.h" | |
13 #include "core/fpdfdoc/include/cpdf_nametree.h" | |
14 #include "core/fpdfdoc/include/ipdf_formnotify.h" | |
15 | |
16 namespace { | |
17 | |
18 const int nMaxRecursion = 32; | |
19 const FX_CHAR* const g_sZoomModes[] = {"XYZ", "Fit", "FitH", "FitV", "FitR", | |
20 "FitB", "FitBH", "FitBV", nullptr}; | |
21 | |
22 } // namespace | |
23 | |
24 int CPDF_Dest::GetPageIndex(CPDF_Document* pDoc) { | |
25 CPDF_Array* pArray = ToArray(m_pObj); | |
26 if (!pArray) | |
27 return 0; | |
28 | |
29 CPDF_Object* pPage = pArray->GetDirectObjectAt(0); | |
30 if (!pPage) | |
31 return 0; | |
32 if (pPage->IsNumber()) | |
33 return pPage->GetInteger(); | |
34 if (!pPage->IsDictionary()) | |
35 return 0; | |
36 return pDoc->GetPageIndex(pPage->GetObjNum()); | |
37 } | |
38 | |
39 uint32_t CPDF_Dest::GetPageObjNum() { | |
40 CPDF_Array* pArray = ToArray(m_pObj); | |
41 if (!pArray) | |
42 return 0; | |
43 | |
44 CPDF_Object* pPage = pArray->GetDirectObjectAt(0); | |
45 if (!pPage) | |
46 return 0; | |
47 if (pPage->IsNumber()) | |
48 return pPage->GetInteger(); | |
49 if (pPage->IsDictionary()) | |
50 return pPage->GetObjNum(); | |
51 return 0; | |
52 } | |
53 | |
54 int CPDF_Dest::GetZoomMode() { | |
55 CPDF_Array* pArray = ToArray(m_pObj); | |
56 if (!pArray) | |
57 return 0; | |
58 | |
59 CPDF_Object* pObj = pArray->GetDirectObjectAt(1); | |
60 if (!pObj) | |
61 return 0; | |
62 | |
63 CFX_ByteString mode = pObj->GetString(); | |
64 for (int i = 0; g_sZoomModes[i]; ++i) { | |
65 if (mode == g_sZoomModes[i]) | |
66 return i + 1; | |
67 } | |
68 | |
69 return 0; | |
70 } | |
71 | |
72 FX_FLOAT CPDF_Dest::GetParam(int index) { | |
73 CPDF_Array* pArray = ToArray(m_pObj); | |
74 return pArray ? pArray->GetNumberAt(2 + index) : 0; | |
75 } | |
76 | |
77 CFX_ByteString CPDF_Dest::GetRemoteName() { | |
78 return m_pObj ? m_pObj->GetString() : CFX_ByteString(); | |
79 } | |
80 | |
81 CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, | |
82 const CFX_ByteString& category) | |
83 : m_pRoot(nullptr) { | |
84 CPDF_Dictionary* pRoot = pDoc->GetRoot(); | |
85 if (!pRoot) | |
86 return; | |
87 | |
88 CPDF_Dictionary* pNames = pRoot->GetDictBy("Names"); | |
89 if (!pNames) | |
90 return; | |
91 | |
92 m_pRoot = pNames->GetDictBy(category); | |
93 } | |
94 | |
95 static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode, | |
96 const CFX_ByteString& csName, | |
97 size_t& nIndex, | |
98 CPDF_Array** ppFind, | |
99 int nLevel = 0) { | |
100 if (nLevel > nMaxRecursion) { | |
101 return nullptr; | |
102 } | |
103 CPDF_Array* pLimits = pNode->GetArrayBy("Limits"); | |
104 if (pLimits) { | |
105 CFX_ByteString csLeft = pLimits->GetStringAt(0); | |
106 CFX_ByteString csRight = pLimits->GetStringAt(1); | |
107 if (csLeft.Compare(csRight.AsStringC()) > 0) { | |
108 CFX_ByteString csTmp = csRight; | |
109 csRight = csLeft; | |
110 csLeft = csTmp; | |
111 } | |
112 if (csName.Compare(csLeft.AsStringC()) < 0 || | |
113 csName.Compare(csRight.AsStringC()) > 0) { | |
114 return nullptr; | |
115 } | |
116 } | |
117 CPDF_Array* pNames = pNode->GetArrayBy("Names"); | |
118 if (pNames) { | |
119 size_t dwCount = pNames->GetCount() / 2; | |
120 for (size_t i = 0; i < dwCount; i++) { | |
121 CFX_ByteString csValue = pNames->GetStringAt(i * 2); | |
122 int32_t iCompare = csValue.Compare(csName.AsStringC()); | |
123 if (iCompare <= 0) { | |
124 if (ppFind) { | |
125 *ppFind = pNames; | |
126 } | |
127 if (iCompare < 0) { | |
128 continue; | |
129 } | |
130 } else { | |
131 break; | |
132 } | |
133 nIndex += i; | |
134 return pNames->GetDirectObjectAt(i * 2 + 1); | |
135 } | |
136 nIndex += dwCount; | |
137 return nullptr; | |
138 } | |
139 CPDF_Array* pKids = pNode->GetArrayBy("Kids"); | |
140 if (!pKids) { | |
141 return nullptr; | |
142 } | |
143 for (size_t i = 0; i < pKids->GetCount(); i++) { | |
144 CPDF_Dictionary* pKid = pKids->GetDictAt(i); | |
145 if (!pKid) { | |
146 continue; | |
147 } | |
148 CPDF_Object* pFound = | |
149 SearchNameNode(pKid, csName, nIndex, ppFind, nLevel + 1); | |
150 if (pFound) { | |
151 return pFound; | |
152 } | |
153 } | |
154 return nullptr; | |
155 } | |
156 | |
157 static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode, | |
158 size_t nIndex, | |
159 size_t& nCurIndex, | |
160 CFX_ByteString& csName, | |
161 CPDF_Array** ppFind, | |
162 int nLevel = 0) { | |
163 if (nLevel > nMaxRecursion) | |
164 return nullptr; | |
165 | |
166 CPDF_Array* pNames = pNode->GetArrayBy("Names"); | |
167 if (pNames) { | |
168 size_t nCount = pNames->GetCount() / 2; | |
169 if (nIndex >= nCurIndex + nCount) { | |
170 nCurIndex += nCount; | |
171 return nullptr; | |
172 } | |
173 if (ppFind) | |
174 *ppFind = pNames; | |
175 csName = pNames->GetStringAt((nIndex - nCurIndex) * 2); | |
176 return pNames->GetDirectObjectAt((nIndex - nCurIndex) * 2 + 1); | |
177 } | |
178 CPDF_Array* pKids = pNode->GetArrayBy("Kids"); | |
179 if (!pKids) | |
180 return nullptr; | |
181 for (size_t i = 0; i < pKids->GetCount(); i++) { | |
182 CPDF_Dictionary* pKid = pKids->GetDictAt(i); | |
183 if (!pKid) | |
184 continue; | |
185 CPDF_Object* pFound = | |
186 SearchNameNode(pKid, nIndex, nCurIndex, csName, ppFind, nLevel + 1); | |
187 if (pFound) | |
188 return pFound; | |
189 } | |
190 return nullptr; | |
191 } | |
192 | |
193 static size_t CountNames(CPDF_Dictionary* pNode, int nLevel = 0) { | |
194 if (nLevel > nMaxRecursion) { | |
195 return 0; | |
196 } | |
197 CPDF_Array* pNames = pNode->GetArrayBy("Names"); | |
198 if (pNames) { | |
199 return pNames->GetCount() / 2; | |
200 } | |
201 CPDF_Array* pKids = pNode->GetArrayBy("Kids"); | |
202 if (!pKids) { | |
203 return 0; | |
204 } | |
205 size_t nCount = 0; | |
206 for (size_t i = 0; i < pKids->GetCount(); i++) { | |
207 CPDF_Dictionary* pKid = pKids->GetDictAt(i); | |
208 if (!pKid) { | |
209 continue; | |
210 } | |
211 nCount += CountNames(pKid, nLevel + 1); | |
212 } | |
213 return nCount; | |
214 } | |
215 | |
216 size_t CPDF_NameTree::GetCount() const { | |
217 if (!m_pRoot) { | |
218 return 0; | |
219 } | |
220 return ::CountNames(m_pRoot); | |
221 } | |
222 | |
223 int CPDF_NameTree::GetIndex(const CFX_ByteString& csName) const { | |
224 if (!m_pRoot) { | |
225 return -1; | |
226 } | |
227 size_t nIndex = 0; | |
228 if (!SearchNameNode(m_pRoot, csName, nIndex, nullptr)) { | |
229 return -1; | |
230 } | |
231 return nIndex; | |
232 } | |
233 | |
234 CPDF_Object* CPDF_NameTree::LookupValue(int nIndex, | |
235 CFX_ByteString& csName) const { | |
236 if (!m_pRoot) { | |
237 return nullptr; | |
238 } | |
239 size_t nCurIndex = 0; | |
240 return SearchNameNode(m_pRoot, nIndex, nCurIndex, csName, nullptr); | |
241 } | |
242 | |
243 CPDF_Object* CPDF_NameTree::LookupValue(const CFX_ByteString& csName) const { | |
244 if (!m_pRoot) { | |
245 return nullptr; | |
246 } | |
247 size_t nIndex = 0; | |
248 return SearchNameNode(m_pRoot, csName, nIndex, nullptr); | |
249 } | |
250 | |
251 CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc, | |
252 const CFX_ByteString& sName) { | |
253 CPDF_Object* pValue = LookupValue(sName); | |
254 if (!pValue) { | |
255 CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDictBy("Dests"); | |
256 if (!pDests) | |
257 return nullptr; | |
258 pValue = pDests->GetDirectObjectBy(sName); | |
259 } | |
260 if (!pValue) | |
261 return nullptr; | |
262 if (CPDF_Array* pArray = pValue->AsArray()) | |
263 return pArray; | |
264 if (CPDF_Dictionary* pDict = pValue->AsDictionary()) | |
265 return pDict->GetArrayBy("D"); | |
266 return nullptr; | |
267 } | |
268 | |
269 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ || \ | |
270 _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ | |
271 static CFX_WideString ChangeSlashToPlatform(const FX_WCHAR* str) { | |
272 CFX_WideString result; | |
273 while (*str) { | |
274 if (*str == '/') { | |
275 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ | |
276 result += ':'; | |
277 #else | |
278 result += '\\'; | |
279 #endif | |
280 } else { | |
281 result += *str; | |
282 } | |
283 str++; | |
284 } | |
285 return result; | |
286 } | |
287 | |
288 static CFX_WideString ChangeSlashToPDF(const FX_WCHAR* str) { | |
289 CFX_WideString result; | |
290 while (*str) { | |
291 if (*str == '\\' || *str == ':') { | |
292 result += '/'; | |
293 } else { | |
294 result += *str; | |
295 } | |
296 str++; | |
297 } | |
298 return result; | |
299 } | |
300 #endif // _FXM_PLATFORM_APPLE_ || _FXM_PLATFORM_WINDOWS_ | |
301 | |
302 CFX_WideString CPDF_FileSpec::DecodeFileName(const CFX_WideStringC& filepath) { | |
303 if (filepath.GetLength() <= 1) | |
304 return CFX_WideString(); | |
305 | |
306 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ | |
307 if (filepath.Left(sizeof("/Mac") - 1) == CFX_WideStringC(L"/Mac")) | |
308 return ChangeSlashToPlatform(filepath.c_str() + 1); | |
309 return ChangeSlashToPlatform(filepath.c_str()); | |
310 #elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ | |
311 if (filepath.GetAt(0) != '/') | |
312 return ChangeSlashToPlatform(filepath.c_str()); | |
313 if (filepath.GetAt(1) == '/') | |
314 return ChangeSlashToPlatform(filepath.c_str() + 1); | |
315 if (filepath.GetAt(2) == '/') { | |
316 CFX_WideString result; | |
317 result += filepath.GetAt(1); | |
318 result += ':'; | |
319 result += ChangeSlashToPlatform(filepath.c_str() + 2); | |
320 return result; | |
321 } | |
322 CFX_WideString result; | |
323 result += '\\'; | |
324 result += ChangeSlashToPlatform(filepath.c_str()); | |
325 return result; | |
326 #else | |
327 return CFX_WideString(filepath); | |
328 #endif | |
329 } | |
330 | |
331 bool CPDF_FileSpec::GetFileName(CFX_WideString* csFileName) const { | |
332 if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) { | |
333 *csFileName = pDict->GetUnicodeTextBy("UF"); | |
334 if (csFileName->IsEmpty()) { | |
335 *csFileName = | |
336 CFX_WideString::FromLocal(pDict->GetStringBy("F").AsStringC()); | |
337 } | |
338 if (pDict->GetStringBy("FS") == "URL") | |
339 return true; | |
340 if (csFileName->IsEmpty()) { | |
341 if (pDict->KeyExist("DOS")) { | |
342 *csFileName = | |
343 CFX_WideString::FromLocal(pDict->GetStringBy("DOS").AsStringC()); | |
344 } else if (pDict->KeyExist("Mac")) { | |
345 *csFileName = | |
346 CFX_WideString::FromLocal(pDict->GetStringBy("Mac").AsStringC()); | |
347 } else if (pDict->KeyExist("Unix")) { | |
348 *csFileName = | |
349 CFX_WideString::FromLocal(pDict->GetStringBy("Unix").AsStringC()); | |
350 } else { | |
351 return false; | |
352 } | |
353 } | |
354 } else if (m_pObj->IsString()) { | |
355 *csFileName = CFX_WideString::FromLocal(m_pObj->GetString().AsStringC()); | |
356 } else { | |
357 return false; | |
358 } | |
359 *csFileName = DecodeFileName(csFileName->AsStringC()); | |
360 return true; | |
361 } | |
362 | |
363 CPDF_FileSpec::CPDF_FileSpec() { | |
364 m_pObj = new CPDF_Dictionary; | |
365 m_pObj->AsDictionary()->SetAtName("Type", "Filespec"); | |
366 } | |
367 | |
368 CFX_WideString CPDF_FileSpec::EncodeFileName(const CFX_WideStringC& filepath) { | |
369 if (filepath.GetLength() <= 1) { | |
370 return CFX_WideString(); | |
371 } | |
372 #if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ | |
373 if (filepath.GetAt(1) == ':') { | |
374 CFX_WideString result; | |
375 result = '/'; | |
376 result += filepath.GetAt(0); | |
377 if (filepath.GetAt(2) != '\\') { | |
378 result += '/'; | |
379 } | |
380 result += ChangeSlashToPDF(filepath.c_str() + 2); | |
381 return result; | |
382 } | |
383 if (filepath.GetAt(0) == '\\' && filepath.GetAt(1) == '\\') { | |
384 return ChangeSlashToPDF(filepath.c_str() + 1); | |
385 } | |
386 if (filepath.GetAt(0) == '\\') { | |
387 CFX_WideString result; | |
388 result = '/'; | |
389 result += ChangeSlashToPDF(filepath.c_str()); | |
390 return result; | |
391 } | |
392 return ChangeSlashToPDF(filepath.c_str()); | |
393 #elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ | |
394 if (filepath.Left(sizeof("Mac") - 1) == FX_WSTRC(L"Mac")) { | |
395 CFX_WideString result; | |
396 result = '/'; | |
397 result += ChangeSlashToPDF(filepath.c_str()); | |
398 return result; | |
399 } | |
400 return ChangeSlashToPDF(filepath.c_str()); | |
401 #else | |
402 return CFX_WideString(filepath); | |
403 #endif | |
404 } | |
405 | |
406 void CPDF_FileSpec::SetFileName(const CFX_WideStringC& wsFileName) { | |
407 if (!m_pObj) | |
408 return; | |
409 | |
410 CFX_WideString wsStr = EncodeFileName(wsFileName); | |
411 if (m_pObj->IsString()) { | |
412 m_pObj->SetString(CFX_ByteString::FromUnicode(wsStr)); | |
413 } else if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) { | |
414 pDict->SetAtString("F", CFX_ByteString::FromUnicode(wsStr)); | |
415 pDict->SetAtString("UF", PDF_EncodeText(wsStr)); | |
416 } | |
417 } | |
418 | |
419 static CFX_WideString _MakeRoman(int num) { | |
420 const int arabic[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; | |
421 const CFX_WideString roman[] = {L"m", L"cm", L"d", L"cd", L"c", | |
422 L"xc", L"l", L"xl", L"x", L"ix", | |
423 L"v", L"iv", L"i"}; | |
424 const int nMaxNum = 1000000; | |
425 num %= nMaxNum; | |
426 int i = 0; | |
427 CFX_WideString wsRomanNumber; | |
428 while (num > 0) { | |
429 while (num >= arabic[i]) { | |
430 num = num - arabic[i]; | |
431 wsRomanNumber += roman[i]; | |
432 } | |
433 i = i + 1; | |
434 } | |
435 return wsRomanNumber; | |
436 } | |
437 | |
438 static CFX_WideString _MakeLetters(int num) { | |
439 if (num == 0) { | |
440 return CFX_WideString(); | |
441 } | |
442 CFX_WideString wsLetters; | |
443 const int nMaxCount = 1000; | |
444 const int nLetterCount = 26; | |
445 num -= 1; | |
446 int count = num / nLetterCount + 1; | |
447 count %= nMaxCount; | |
448 FX_WCHAR ch = L'a' + num % nLetterCount; | |
449 for (int i = 0; i < count; i++) { | |
450 wsLetters += ch; | |
451 } | |
452 return wsLetters; | |
453 } | |
454 | |
455 static CFX_WideString _GetLabelNumPortion(int num, | |
456 const CFX_ByteString& bsStyle) { | |
457 CFX_WideString wsNumPortion; | |
458 if (bsStyle.IsEmpty()) { | |
459 return wsNumPortion; | |
460 } | |
461 if (bsStyle == "D") { | |
462 wsNumPortion.Format(L"%d", num); | |
463 } else if (bsStyle == "R") { | |
464 wsNumPortion = _MakeRoman(num); | |
465 wsNumPortion.MakeUpper(); | |
466 } else if (bsStyle == "r") { | |
467 wsNumPortion = _MakeRoman(num); | |
468 } else if (bsStyle == "A") { | |
469 wsNumPortion = _MakeLetters(num); | |
470 wsNumPortion.MakeUpper(); | |
471 } else if (bsStyle == "a") { | |
472 wsNumPortion = _MakeLetters(num); | |
473 } | |
474 return wsNumPortion; | |
475 } | |
476 | |
477 IPDF_FormNotify::~IPDF_FormNotify() {} | |
478 | |
479 int IPDF_FormNotify::BeforeValueChange(CPDF_FormField* pField, | |
480 const CFX_WideString& csValue) { | |
481 return 0; | |
482 } | |
483 | |
484 void IPDF_FormNotify::AfterValueChange(CPDF_FormField* pField) {} | |
485 | |
486 int IPDF_FormNotify::BeforeSelectionChange(CPDF_FormField* pField, | |
487 const CFX_WideString& csValue) { | |
488 return 0; | |
489 } | |
490 | |
491 void IPDF_FormNotify::AfterSelectionChange(CPDF_FormField* pField) {} | |
492 | |
493 void IPDF_FormNotify::AfterCheckedStatusChange(CPDF_FormField* pField) {} | |
494 | |
495 int IPDF_FormNotify::BeforeFormReset(CPDF_InterForm* pForm) { | |
496 return 0; | |
497 } | |
498 | |
499 void IPDF_FormNotify::AfterFormReset(CPDF_InterForm* pForm) {} | |
500 | |
501 int IPDF_FormNotify::BeforeFormImportData(CPDF_InterForm* pForm) { | |
502 return 0; | |
503 } | |
504 | |
505 void IPDF_FormNotify::AfterFormImportData(CPDF_InterForm* pForm) {} | |
506 | |
507 CPDF_PageLabel::CPDF_PageLabel(CPDF_Document* pDocument) | |
508 : m_pDocument(pDocument) {} | |
509 | |
510 CFX_WideString CPDF_PageLabel::GetLabel(int nPage) const { | |
511 CFX_WideString wsLabel; | |
512 if (!m_pDocument) { | |
513 return wsLabel; | |
514 } | |
515 CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot(); | |
516 if (!pPDFRoot) { | |
517 return wsLabel; | |
518 } | |
519 CPDF_Dictionary* pLabels = pPDFRoot->GetDictBy("PageLabels"); | |
520 CPDF_NumberTree numberTree(pLabels); | |
521 CPDF_Object* pValue = nullptr; | |
522 int n = nPage; | |
523 while (n >= 0) { | |
524 pValue = numberTree.LookupValue(n); | |
525 if (pValue) { | |
526 break; | |
527 } | |
528 n--; | |
529 } | |
530 if (pValue) { | |
531 pValue = pValue->GetDirect(); | |
532 if (CPDF_Dictionary* pLabel = pValue->AsDictionary()) { | |
533 if (pLabel->KeyExist("P")) { | |
534 wsLabel += pLabel->GetUnicodeTextBy("P"); | |
535 } | |
536 CFX_ByteString bsNumberingStyle = pLabel->GetStringBy("S", ""); | |
537 int nLabelNum = nPage - n + pLabel->GetIntegerBy("St", 1); | |
538 CFX_WideString wsNumPortion = | |
539 _GetLabelNumPortion(nLabelNum, bsNumberingStyle); | |
540 wsLabel += wsNumPortion; | |
541 return wsLabel; | |
542 } | |
543 } | |
544 wsLabel.Format(L"%d", nPage + 1); | |
545 return wsLabel; | |
546 } | |
547 | |
548 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_ByteStringC& bsLabel) const { | |
549 if (!m_pDocument) | |
550 return -1; | |
551 | |
552 CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot(); | |
553 if (!pPDFRoot) | |
554 return -1; | |
555 | |
556 int nPages = m_pDocument->GetPageCount(); | |
557 for (int i = 0; i < nPages; i++) { | |
558 if (PDF_EncodeText(GetLabel(i)).Compare(bsLabel)) | |
559 return i; | |
560 } | |
561 | |
562 int nPage = FXSYS_atoi(CFX_ByteString(bsLabel).c_str()); // NUL terminate. | |
563 return nPage > 0 && nPage <= nPages ? nPage : -1; | |
564 } | |
565 | |
566 int32_t CPDF_PageLabel::GetPageByLabel(const CFX_WideStringC& wsLabel) const { | |
567 return GetPageByLabel(PDF_EncodeText(wsLabel.c_str()).AsStringC()); | |
568 } | |
OLD | NEW |