Cleanup some numeric code.
[pdfium.git] / core / src / fpdfapi / fpdf_parser / fpdf_parser_utility.cpp
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 "../../../include/fpdfapi/fpdf_parser.h"
8 #include "../../../include/fxcrt/fx_ext.h"
9
10 // Indexed by 8-bit character code, contains either:
11 //   'W' - for whitespace: NUL, TAB, CR, LF, FF, 0x80, 0xff
12 //   'N' - for numeric: 0123456789+-.
13 //   'D' - for delimiter: %()/<>[]{}
14 //   'R' - otherwise.
15 const char PDF_CharType[256] = {
16     // NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL  BS   HT   LF   VT   FF   CR   SO
17     // SI
18     'W', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'W', 'W', 'R', 'W', 'W', 'R',
19     'R',
20
21     // DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB  CAN  EM   SUB  ESC  FS   GS   RS
22     // US
23     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
24     'R',
25
26     // SP    !    "    #    $    %    &    ยด    (    )    *    +    ,    -    .
27     // /
28     'W', 'R', 'R', 'R', 'R', 'D', 'R', 'R', 'D', 'D', 'R', 'N', 'R', 'N', 'N',
29     'D',
30
31     // 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    > ?
32     'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'R', 'R', 'D', 'R', 'D',
33     'R',
34
35     // @    A    B    C    D    E    F    G    H    I    J    K    L    M    N O
36     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
37     'R',
38
39     // P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^ _
40     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'D', 'R', 'D', 'R',
41     'R',
42
43     // `    a    b    c    d    e    f    g    h    i    j    k    l    m    n o
44     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
45     'R',
46
47     // p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
48     // DEL
49     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'D', 'R', 'D', 'R',
50     'R',
51
52     'W', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
53     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
54     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
55     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
56     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
57     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
58     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
59     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
60     'R', 'R', 'R', 'R', 'R', 'R', 'R', 'W'};
61
62 #ifndef MAX_PATH
63 #define MAX_PATH 4096
64 #endif
65 CPDF_SimpleParser::CPDF_SimpleParser(const uint8_t* pData, FX_DWORD dwSize) {
66   m_pData = pData;
67   m_dwSize = dwSize;
68   m_dwCurPos = 0;
69 }
70 CPDF_SimpleParser::CPDF_SimpleParser(const CFX_ByteStringC& str) {
71   m_pData = str.GetPtr();
72   m_dwSize = str.GetLength();
73   m_dwCurPos = 0;
74 }
75 void CPDF_SimpleParser::ParseWord(const uint8_t*& pStart,
76                                   FX_DWORD& dwSize,
77                                   int& type) {
78   pStart = NULL;
79   dwSize = 0;
80   type = PDFWORD_EOF;
81   uint8_t ch;
82   while (1) {
83     if (m_dwSize <= m_dwCurPos)
84       return;
85     ch = m_pData[m_dwCurPos++];
86     while (PDFCharIsWhitespace(ch)) {
87       if (m_dwSize <= m_dwCurPos)
88         return;
89       ch = m_pData[m_dwCurPos++];
90     }
91
92     if (ch != '%')
93       break;
94
95     while (1) {
96       if (m_dwSize <= m_dwCurPos)
97         return;
98       ch = m_pData[m_dwCurPos++];
99       if (ch == '\r' || ch == '\n')
100         break;
101     }
102   }
103
104   FX_DWORD start_pos = m_dwCurPos - 1;
105   pStart = m_pData + start_pos;
106   if (PDFCharIsDelimiter(ch)) {
107     if (ch == '/') {
108       while (1) {
109         if (m_dwSize <= m_dwCurPos)
110           return;
111         ch = m_pData[m_dwCurPos++];
112         if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) {
113           m_dwCurPos--;
114           dwSize = m_dwCurPos - start_pos;
115           type = PDFWORD_NAME;
116           return;
117         }
118       }
119     } else {
120       type = PDFWORD_DELIMITER;
121       dwSize = 1;
122       if (ch == '<') {
123         if (m_dwSize <= m_dwCurPos)
124           return;
125         ch = m_pData[m_dwCurPos++];
126         if (ch == '<')
127           dwSize = 2;
128         else
129           m_dwCurPos--;
130       } else if (ch == '>') {
131         if (m_dwSize <= m_dwCurPos)
132           return;
133         ch = m_pData[m_dwCurPos++];
134         if (ch == '>')
135           dwSize = 2;
136         else
137           m_dwCurPos--;
138       }
139     }
140     return;
141   }
142
143   type = PDFWORD_NUMBER;
144   dwSize = 1;
145   while (1) {
146     if (!PDFCharIsNumeric(ch))
147       type = PDFWORD_TEXT;
148     if (m_dwSize <= m_dwCurPos)
149       return;
150     ch = m_pData[m_dwCurPos++];
151
152     if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) {
153       m_dwCurPos--;
154       break;
155     }
156     dwSize++;
157   }
158 }
159 CFX_ByteStringC CPDF_SimpleParser::GetWord() {
160   const uint8_t* pStart;
161   FX_DWORD dwSize;
162   int type;
163   ParseWord(pStart, dwSize, type);
164   if (dwSize == 1 && pStart[0] == '<') {
165     while (m_dwCurPos < m_dwSize && m_pData[m_dwCurPos] != '>') {
166       m_dwCurPos++;
167     }
168     if (m_dwCurPos < m_dwSize) {
169       m_dwCurPos++;
170     }
171     return CFX_ByteStringC(pStart,
172                            (FX_STRSIZE)(m_dwCurPos - (pStart - m_pData)));
173   }
174   if (dwSize == 1 && pStart[0] == '(') {
175     int level = 1;
176     while (m_dwCurPos < m_dwSize) {
177       if (m_pData[m_dwCurPos] == ')') {
178         level--;
179         if (level == 0) {
180           break;
181         }
182       }
183       if (m_pData[m_dwCurPos] == '\\') {
184         if (m_dwSize <= m_dwCurPos) {
185           break;
186         }
187         m_dwCurPos++;
188       } else if (m_pData[m_dwCurPos] == '(') {
189         level++;
190       }
191       if (m_dwSize <= m_dwCurPos) {
192         break;
193       }
194       m_dwCurPos++;
195     }
196     if (m_dwCurPos < m_dwSize) {
197       m_dwCurPos++;
198     }
199     return CFX_ByteStringC(pStart,
200                            (FX_STRSIZE)(m_dwCurPos - (pStart - m_pData)));
201   }
202   return CFX_ByteStringC(pStart, dwSize);
203 }
204 FX_BOOL CPDF_SimpleParser::SearchToken(const CFX_ByteStringC& token) {
205   int token_len = token.GetLength();
206   while (m_dwCurPos < m_dwSize - token_len) {
207     if (FXSYS_memcmp(m_pData + m_dwCurPos, token.GetPtr(), token_len) == 0) {
208       break;
209     }
210     m_dwCurPos++;
211   }
212   if (m_dwCurPos == m_dwSize - token_len) {
213     return FALSE;
214   }
215   m_dwCurPos += token_len;
216   return TRUE;
217 }
218 FX_BOOL CPDF_SimpleParser::SkipWord(const CFX_ByteStringC& token) {
219   while (1) {
220     CFX_ByteStringC word = GetWord();
221     if (word.IsEmpty()) {
222       return FALSE;
223     }
224     if (word == token) {
225       return TRUE;
226     }
227   }
228   return FALSE;
229 }
230 FX_BOOL CPDF_SimpleParser::FindTagPair(const CFX_ByteStringC& start_token,
231                                        const CFX_ByteStringC& end_token,
232                                        FX_DWORD& start_pos,
233                                        FX_DWORD& end_pos) {
234   if (!start_token.IsEmpty()) {
235     if (!SkipWord(start_token)) {
236       return FALSE;
237     }
238     start_pos = m_dwCurPos;
239   }
240   while (1) {
241     end_pos = m_dwCurPos;
242     CFX_ByteStringC word = GetWord();
243     if (word.IsEmpty()) {
244       return FALSE;
245     }
246     if (word == end_token) {
247       return TRUE;
248     }
249   }
250   return FALSE;
251 }
252 FX_BOOL CPDF_SimpleParser::FindTagParam(const CFX_ByteStringC& token,
253                                         int nParams) {
254   nParams++;
255   FX_DWORD* pBuf = FX_Alloc(FX_DWORD, nParams);
256   int buf_index = 0;
257   int buf_count = 0;
258   while (1) {
259     pBuf[buf_index++] = m_dwCurPos;
260     if (buf_index == nParams) {
261       buf_index = 0;
262     }
263     buf_count++;
264     if (buf_count > nParams) {
265       buf_count = nParams;
266     }
267     CFX_ByteStringC word = GetWord();
268     if (word.IsEmpty()) {
269       FX_Free(pBuf);
270       return FALSE;
271     }
272     if (word == token) {
273       if (buf_count < nParams) {
274         continue;
275       }
276       m_dwCurPos = pBuf[buf_index];
277       FX_Free(pBuf);
278       return TRUE;
279     }
280   }
281   return FALSE;
282 }
283
284 CFX_ByteString PDF_NameDecode(const CFX_ByteStringC& bstr) {
285   int size = bstr.GetLength();
286   const FX_CHAR* pSrc = bstr.GetCStr();
287   if (FXSYS_memchr(pSrc, '#', size) == NULL) {
288     return bstr;
289   }
290   CFX_ByteString result;
291   FX_CHAR* pDestStart = result.GetBuffer(size);
292   FX_CHAR* pDest = pDestStart;
293   for (int i = 0; i < size; i++) {
294     if (pSrc[i] == '#' && i < size - 2) {
295       *pDest++ = HexCharToDigit(pSrc[i + 1]) * 16 + HexCharToDigit(pSrc[i + 2]);
296       i += 2;
297     } else {
298       *pDest++ = pSrc[i];
299     }
300   }
301   result.ReleaseBuffer((FX_STRSIZE)(pDest - pDestStart));
302   return result;
303 }
304 CFX_ByteString PDF_NameDecode(const CFX_ByteString& orig) {
305   if (FXSYS_memchr(orig.c_str(), '#', orig.GetLength()) == NULL) {
306     return orig;
307   }
308   return PDF_NameDecode(CFX_ByteStringC(orig));
309 }
310 CFX_ByteString PDF_NameEncode(const CFX_ByteString& orig) {
311   uint8_t* src_buf = (uint8_t*)orig.c_str();
312   int src_len = orig.GetLength();
313   int dest_len = 0;
314   int i;
315   for (i = 0; i < src_len; i++) {
316     uint8_t ch = src_buf[i];
317     if (ch >= 0x80 || PDFCharIsWhitespace(ch) || ch == '#' ||
318         PDFCharIsDelimiter(ch)) {
319       dest_len += 3;
320     } else {
321       dest_len++;
322     }
323   }
324   if (dest_len == src_len)
325     return orig;
326
327   CFX_ByteString res;
328   FX_CHAR* dest_buf = res.GetBuffer(dest_len);
329   dest_len = 0;
330   for (i = 0; i < src_len; i++) {
331     uint8_t ch = src_buf[i];
332     if (ch >= 0x80 || PDFCharIsWhitespace(ch) || ch == '#' ||
333         PDFCharIsDelimiter(ch)) {
334       dest_buf[dest_len++] = '#';
335       dest_buf[dest_len++] = "0123456789ABCDEF"[ch / 16];
336       dest_buf[dest_len++] = "0123456789ABCDEF"[ch % 16];
337     } else {
338       dest_buf[dest_len++] = ch;
339     }
340   }
341   dest_buf[dest_len] = 0;
342   res.ReleaseBuffer();
343   return res;
344 }
345 CFX_ByteTextBuf& operator<<(CFX_ByteTextBuf& buf, const CPDF_Object* pObj) {
346   if (pObj == NULL) {
347     buf << FX_BSTRC(" null");
348     return buf;
349   }
350   switch (pObj->GetType()) {
351     case PDFOBJ_NULL:
352       buf << FX_BSTRC(" null");
353       break;
354     case PDFOBJ_BOOLEAN:
355     case PDFOBJ_NUMBER:
356       buf << " " << pObj->GetString();
357       break;
358     case PDFOBJ_STRING:
359       buf << PDF_EncodeString(pObj->GetString(), pObj->AsString()->IsHex());
360       break;
361     case PDFOBJ_NAME: {
362       CFX_ByteString str = pObj->GetString();
363       buf << FX_BSTRC("/") << PDF_NameEncode(str);
364       break;
365     }
366     case PDFOBJ_REFERENCE: {
367       buf << " " << pObj->AsReference()->GetRefObjNum() << FX_BSTRC(" 0 R ");
368       break;
369     }
370     case PDFOBJ_ARRAY: {
371       const CPDF_Array* p = pObj->AsArray();
372       buf << FX_BSTRC("[");
373       for (FX_DWORD i = 0; i < p->GetCount(); i++) {
374         CPDF_Object* pElement = p->GetElement(i);
375         if (pElement->GetObjNum()) {
376           buf << " " << pElement->GetObjNum() << FX_BSTRC(" 0 R");
377         } else {
378           buf << pElement;
379         }
380       }
381       buf << FX_BSTRC("]");
382       break;
383     }
384     case PDFOBJ_DICTIONARY: {
385       const CPDF_Dictionary* p = pObj->AsDictionary();
386       buf << FX_BSTRC("<<");
387       FX_POSITION pos = p->GetStartPos();
388       while (pos) {
389         CFX_ByteString key;
390         CPDF_Object* pValue = p->GetNextElement(pos, key);
391         buf << FX_BSTRC("/") << PDF_NameEncode(key);
392         if (pValue && pValue->GetObjNum()) {
393           buf << " " << pValue->GetObjNum() << FX_BSTRC(" 0 R ");
394         } else {
395           buf << pValue;
396         }
397       }
398       buf << FX_BSTRC(">>");
399       break;
400     }
401     case PDFOBJ_STREAM: {
402       const CPDF_Stream* p = pObj->AsStream();
403       buf << p->GetDict() << FX_BSTRC("stream\r\n");
404       CPDF_StreamAcc acc;
405       acc.LoadAllData(p, TRUE);
406       buf.AppendBlock(acc.GetData(), acc.GetSize());
407       buf << FX_BSTRC("\r\nendstream");
408       break;
409     }
410     default:
411       ASSERT(FALSE);
412       break;
413   }
414   return buf;
415 }
416 FX_FLOAT PDF_ClipFloat(FX_FLOAT f) {
417   if (f < 0) {
418     return 0;
419   }
420   if (f > 1.0f) {
421     return 1.0f;
422   }
423   return f;
424 }
425 static CPDF_Object* SearchNumberNode(CPDF_Dictionary* pNode, int num) {
426   CPDF_Array* pLimits = pNode->GetArray("Limits");
427   if (pLimits &&
428       (num < pLimits->GetInteger(0) || num > pLimits->GetInteger(1))) {
429     return NULL;
430   }
431   CPDF_Array* pNumbers = pNode->GetArray("Nums");
432   if (pNumbers) {
433     FX_DWORD dwCount = pNumbers->GetCount() / 2;
434     for (FX_DWORD i = 0; i < dwCount; i++) {
435       int index = pNumbers->GetInteger(i * 2);
436       if (num == index) {
437         return pNumbers->GetElementValue(i * 2 + 1);
438       }
439       if (index > num) {
440         break;
441       }
442     }
443     return NULL;
444   }
445   CPDF_Array* pKids = pNode->GetArray("Kids");
446   if (pKids == NULL) {
447     return NULL;
448   }
449   for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
450     CPDF_Dictionary* pKid = pKids->GetDict(i);
451     if (pKid == NULL) {
452       continue;
453     }
454     CPDF_Object* pFound = SearchNumberNode(pKid, num);
455     if (pFound) {
456       return pFound;
457     }
458   }
459   return NULL;
460 }
461 CPDF_Object* CPDF_NumberTree::LookupValue(int num) {
462   return SearchNumberNode(m_pRoot, num);
463 }