blob: 47980318080acc7e7746a79e9f093306f4f0cd16 [file] [log] [blame]
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001
2#include "SkPdfNativeTokenizer.h"
edisonn@google.com571c70b2013-07-10 17:09:50 +00003#include "SkPdfObject.h"
4#include "SkPdfConfig.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +00005
edisonn@google.com571c70b2013-07-10 17:09:50 +00006#include "SkPdfStreamCommonDictionary_autogen.h"
edisonn@google.com78b38b12013-07-15 18:20:58 +00007#include "SkPdfImageDictionary_autogen.h"
8
9// TODO(edisonn): perf!!!
10// there could be 0s between start and end! but not in the needle.
11static char* strrstrk(char* hayStart, char* hayEnd, const char* needle) {
12 int needleLen = strlen(needle);
13 if ((isPdfWhiteSpaceOrPdfDelimiter(*(hayStart+needleLen)) || (hayStart+needleLen == hayEnd)) &&
14 strncmp(hayStart, needle, needleLen) == 0) {
15 return hayStart;
16 }
17
18 hayStart++;
19
20 while (hayStart < hayEnd) {
21 if (isPdfWhiteSpaceOrPdfDelimiter(*(hayStart-1)) &&
22 (isPdfWhiteSpaceOrPdfDelimiter(*(hayStart+needleLen)) || (hayStart+needleLen == hayEnd)) &&
23 strncmp(hayStart, needle, needleLen) == 0) {
24 return hayStart;
25 }
26 hayStart++;
27 }
28 return NULL;
29}
30
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000031#ifdef PDF_TRACE
32static void TRACE_INDENT(int level, const char* type) {
33 static int id = 0;
34 id++;
35 if (478613 == id) {
36 printf("break;\n");
37 }
38 // all types should have 2 letters, so the text is alligned nicely
39 printf("\n%10i %15s: ", id, type);
40 for (int i = 0 ; i < level; i++) {
41 printf(" ");
42 }
43}
edisonn@google.com3aac1f92013-07-02 22:42:53 +000044
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000045static void TRACE_COMMENT(char ch) {
46 printf("%c", ch);
47}
48
49static void TRACE_TK(char ch) {
50 printf("%c", ch);
51}
52
53static void TRACE_NAME(const unsigned char* start, const unsigned char* end) {
54 while (start < end) {
55 printf("%c", *start);
56 start++;
57 }
58 printf("\n");
59}
60
61static void TRACE_STRING(const unsigned char* start, const unsigned char* end) {
62 while (start < end) {
63 printf("%c", *start);
64 start++;
65 }
66 printf("\n");
67}
68
69static void TRACE_HEXSTRING(const unsigned char* start, const unsigned char* end) {
70 while (start < end) {
71 printf("%c", *start);
72 start++;
73 }
74 printf("\n");
75}
76
77#else
78#define TRACE_INDENT(level,type)
79#define TRACE_COMMENT(ch)
80#define TRACE_TK(ch)
81#define TRACE_NAME(start,end)
82#define TRACE_STRING(start,end)
83#define TRACE_HEXSTRING(start,end)
84#endif
85
86static const unsigned char* skipPdfWhiteSpaces(int level, const unsigned char* start, const unsigned char* end) {
87 TRACE_INDENT(level, "White Space");
edisonn@google.com571c70b2013-07-10 17:09:50 +000088 while (start < end && isPdfWhiteSpace(*start)) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000089 TRACE_COMMENT(*start);
edisonn@google.com571c70b2013-07-10 17:09:50 +000090 if (*start == kComment_PdfDelimiter) {
91 // skip the comment until end of line
92 while (start < end && !isPdfEOL(*start)) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000093 //*start = '\0';
edisonn@google.com571c70b2013-07-10 17:09:50 +000094 start++;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000095 TRACE_COMMENT(*start);
edisonn@google.com571c70b2013-07-10 17:09:50 +000096 }
97 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000098 //*start = '\0';
edisonn@google.com571c70b2013-07-10 17:09:50 +000099 start++;
100 }
101 }
102 return start;
103}
104
105// TODO(edisonn) '(' can be used, will it break the string a delimiter or space inside () ?
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000106static const unsigned char* endOfPdfToken(int level, const unsigned char* start, const unsigned char* end) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000107 //int opened brackets
108 //TODO(edisonn): what out for special chars, like \n, \032
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000109 TRACE_INDENT(level, "Token");
edisonn@google.com571c70b2013-07-10 17:09:50 +0000110
111 SkASSERT(!isPdfWhiteSpace(*start));
112
113 if (start < end && isPdfDelimiter(*start)) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000114 TRACE_TK(*start);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000115 start++;
116 return start;
117 }
118
119 while (start < end && !isPdfWhiteSpaceOrPdfDelimiter(*start)) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000120 TRACE_TK(*start);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000121 start++;
122 }
123 return start;
124}
125
edisonn@google.com571c70b2013-07-10 17:09:50 +0000126// last elem has to be ]
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000127static const unsigned char* readArray(int level, const unsigned char* start, const unsigned char* end, SkPdfObject* array, SkPdfAllocator* allocator, SkNativeParsedPDF* doc) {
128 TRACE_INDENT(level, "Array");
edisonn@google.com571c70b2013-07-10 17:09:50 +0000129 while (start < end) {
130 // skip white spaces
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000131 start = skipPdfWhiteSpaces(level + 1, start, end);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000132
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000133 const unsigned char* endOfToken = endOfPdfToken(level + 1, start, end);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000134
135 if (endOfToken == start) {
136 // TODO(edisonn): report error in pdf file (end of stream with ] for end of aray
137 return start;
138 }
139
140 if (endOfToken == start + 1 && *start == kClosedSquareBracket_PdfDelimiter) {
141 return endOfToken;
142 }
143
144 SkPdfObject* newObj = allocator->allocObject();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000145 start = nextObject(level + 1, start, end, newObj, allocator, doc);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000146 // TODO(edisonn): perf/memory: put the variables on the stack, and flush them on the array only when
147 // we are sure they are not references!
148 if (newObj->isKeywordReference() && array->size() >= 2 && array->objAtAIndex(array->size() - 1)->isInteger() && array->objAtAIndex(array->size() - 2)->isInteger()) {
149 SkPdfObject* gen = array->removeLastInArray();
150 SkPdfObject* id = array->removeLastInArray();
151 newObj->reset();
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000152 SkPdfObject::makeReference((unsigned int)id->intValue(), (unsigned int)gen->intValue(), newObj);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000153 }
154 array->appendInArray(newObj);
155 }
edisonn@google.com78b38b12013-07-15 18:20:58 +0000156 printf("break;\n"); // DO NOT SUBMIT!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000157 // TODO(edisonn): report not reached, we should never get here
edisonn@google.com8bad7372013-07-10 23:36:56 +0000158 // TODO(edisonn): there might be a bug here, enable an assert and run it on files
159 // or it might be that the files were actually corrupted
edisonn@google.com571c70b2013-07-10 17:09:50 +0000160 return start;
161}
162
163// When we read strings we will rewrite the string so we will reuse the memory
164// when we start to read the string, we already consumed the opened bracket
edisonn@google.com571c70b2013-07-10 17:09:50 +0000165
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000166// TODO(edisonn): space: add paramater, taht would report if we need to allocate new buffer, or we can reuse the one we have
167
168static const unsigned char* readString(int level, const unsigned char* start, const unsigned char* end, unsigned char* out) {
169 TRACE_INDENT(level, "String");
170 const unsigned char* in = start;
171 bool hasOut = (out != NULL);
172
173 int openRoundBrackets = 1;
174 while (in < end) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000175 openRoundBrackets += ((*in) == kOpenedRoundBracket_PdfDelimiter);
176 openRoundBrackets -= ((*in) == kClosedRoundBracket_PdfDelimiter);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000177 if (openRoundBrackets == 0) {
178 in++; // consumed )
179 break;
180 }
181
edisonn@google.com571c70b2013-07-10 17:09:50 +0000182 if (*in == kEscape_PdfSpecial) {
183 if (in + 1 < end) {
184 switch (in[1]) {
185 case 'n':
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000186 if (hasOut) { *out = kLF_PdfWhiteSpace; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000187 out++;
188 in += 2;
189 break;
190
191 case 'r':
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000192 if (hasOut) { *out = kCR_PdfWhiteSpace; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000193 out++;
194 in += 2;
195 break;
196
197 case 't':
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000198 if (hasOut) { *out = kHT_PdfWhiteSpace; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000199 out++;
200 in += 2;
201 break;
202
203 case 'b':
204 // TODO(edisonn): any special meaning to backspace?
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000205 if (hasOut) { *out = kBackspace_PdfSpecial; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000206 out++;
207 in += 2;
208 break;
209
210 case 'f':
211 *out = kFF_PdfWhiteSpace;
212 out++;
213 in += 2;
214 break;
215
216 case kOpenedRoundBracket_PdfDelimiter:
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000217 if (hasOut) { *out = kOpenedRoundBracket_PdfDelimiter; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000218 out++;
219 in += 2;
220 break;
221
222 case kClosedRoundBracket_PdfDelimiter:
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000223 if (hasOut) { *out = kClosedRoundBracket_PdfDelimiter; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000224 out++;
225 in += 2;
226 break;
227
228 case kEscape_PdfSpecial:
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000229 if (hasOut) { *out = kEscape_PdfSpecial; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000230 out++;
231 in += 2;
232 break;
233
234 case '0':
235 case '1':
236 case '2':
237 case '3':
238 case '4':
239 case '5':
240 case '6':
241 case '7': {
242 //read octals
243 in++; // consume backslash
244
245 int code = 0;
246 int i = 0;
247 while (in < end && *in >= '0' && *in < '8') {
248 code = (code << 3) + ((*in) - '0'); // code * 8 + d
249 i++;
250 in++;
251 if (i == 3) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000252 if (hasOut) { *out = code & 0xff; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000253 out++;
254 i = 0;
255 }
256 }
257 if (i > 0) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000258 if (hasOut) { *out = code & 0xff; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000259 out++;
260 }
261 }
262 break;
263
264 default:
265 // Per spec, backslash is ignored is escaped ch is unknown
266 in++;
267 break;
268 }
edisonn@google.com8bad7372013-07-10 23:36:56 +0000269 } else {
270 in++;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000271 }
272 } else {
273 // TODO(edisonn): perf, avoid copy into itself, maybe first do a simple scan until found backslash ?
274 // we could have one look that first just inc current, and when we find the backslash
275 // we go to this loop
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000276 if (hasOut) { *out = *in; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000277 in++;
278 out++;
279 }
280 }
281
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000282 if (hasOut) {
283 return in; // consumed already ) at the end of the string
284 } else {
285 return start + (out - (const unsigned char*)NULL); // return where the string would end if we reuse the string
286 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000287}
288
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000289static int readStringLength(int level, const unsigned char* start, const unsigned char* end) {
290 return readString(level, start, end, NULL) - start;
291}
292
293static const unsigned char* readString(int level, const unsigned char* start, const unsigned char* end, SkPdfObject* str, SkPdfAllocator* allocator) {
294 int outLength = readStringLength(level, start, end);
295 // TODO(edisonn): optimize the allocation, don't allocate new string, but put it in a preallocated buffer
296 unsigned char* out = (unsigned char*)allocator->alloc(outLength);
297 start = readString(level, start, end, out);
298 SkPdfObject::makeString(out, out + outLength, str);
299 TRACE_STRING(out, out + outLength);
300 return start; // consumed already ) at the end of the string
301}
302
303static const unsigned char* readHexString(int level, const unsigned char* start, const unsigned char* end, unsigned char* out) {
304 TRACE_INDENT(level, "HexString");
305 bool hasOut = (out != NULL);
306 const unsigned char* in = start;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000307
308 unsigned char code = 0;
309
310 while (in < end) {
311 while (in < end && isPdfWhiteSpace(*in)) {
312 in++;
313 }
314
315 if (*in == kClosedInequityBracket_PdfDelimiter) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000316 //*in = '\0';
317 in++; // consume >
edisonn@google.com571c70b2013-07-10 17:09:50 +0000318 // normal exit
319 break;
320 }
321
322 if (in >= end) {
323 // end too soon
324 break;
325 }
326
327 switch (*in) {
328 case '0':
329 case '1':
330 case '2':
331 case '3':
332 case '4':
333 case '5':
334 case '6':
335 case '7':
336 case '8':
337 case '9':
338 code = (*in - '0') << 4;
339 break;
340
341 case 'a':
342 case 'b':
343 case 'c':
344 case 'd':
345 case 'e':
346 case 'f':
347 code = (*in - 'a' + 10) << 4;
348 break;
349
350 case 'A':
351 case 'B':
352 case 'C':
353 case 'D':
354 case 'E':
355 case 'F':
356 code = (*in - 'A' + 10) << 4;
357 break;
358
359 // TODO(edisonn): spec does not say how to handle this error
360 default:
361 break;
362 }
363
364 in++; // advance
365
366 while (in < end && isPdfWhiteSpace(*in)) {
367 in++;
368 }
369
370 // TODO(edisonn): report error
371 if (in >= end) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000372 if (hasOut) { *out = code; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000373 out++;
374 break;
375 }
376
377 if (*in == kClosedInequityBracket_PdfDelimiter) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000378 if (hasOut) { *out = code; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000379 out++;
380 break;
381 }
382
383 switch (*in) {
384 case '0':
385 case '1':
386 case '2':
387 case '3':
388 case '4':
389 case '5':
390 case '6':
391 case '7':
392 case '8':
393 case '9':
394 code += (*in - '0');
395 break;
396
397 case 'a':
398 case 'b':
399 case 'c':
400 case 'd':
401 case 'e':
402 case 'f':
403 code += (*in - 'a' + 10);
404 break;
405
406 case 'A':
407 case 'B':
408 case 'C':
409 case 'D':
410 case 'E':
411 case 'F':
412 code += (*in - 'A' + 10);
413 break;
414
415 // TODO(edisonn): spec does not say how to handle this error
416 default:
417 break;
418 }
419
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000420 if (hasOut) { *out = code; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000421 out++;
422 in++;
423 }
424
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000425 if (hasOut) {
426 return in; // consumed already > at the end of the string
427 } else {
428 return start + (out - (const unsigned char*)NULL); // return where the string would end if we reuse the string
edisonn@google.com571c70b2013-07-10 17:09:50 +0000429 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000430}
edisonn@google.com571c70b2013-07-10 17:09:50 +0000431
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000432static int readHexStringLength(int level, const unsigned char* start, const unsigned char* end) {
433 return readHexString(level, start, end, NULL) - start;
434}
435
436static const unsigned char* readHexString(int level, const unsigned char* start, const unsigned char* end, SkPdfObject* str, SkPdfAllocator* allocator) {
437 int outLength = readHexStringLength(level, start, end);
438 // TODO(edisonn): optimize the allocation, don't allocate new string, but put it in a preallocated buffer
439 unsigned char* out = (unsigned char*)allocator->alloc(outLength);
440 start = readHexString(level, start, end, out);
441 SkPdfObject::makeHexString(out, out + outLength, str);
442 TRACE_HEXSTRING(out, out + outLength);
443 return start; // consumed already > at the end of the string
edisonn@google.com571c70b2013-07-10 17:09:50 +0000444}
445
446// TODO(edisonn): before PDF 1.2 name could not have special characters, add version parameter
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000447static const unsigned char* readName(int level, const unsigned char* start, const unsigned char* end, unsigned char* out) {
448 TRACE_INDENT(level, "Name");
449 bool hasOut = (out != NULL);
450 const unsigned char* in = start;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000451
452 unsigned char code = 0;
453
454 while (in < end) {
455 if (isPdfWhiteSpaceOrPdfDelimiter(*in)) {
456 break;
457 }
458
459 if (*in == '#' && in + 2 < end) {
460 in++;
461 switch (*in) {
462 case '0':
463 case '1':
464 case '2':
465 case '3':
466 case '4':
467 case '5':
468 case '6':
469 case '7':
470 case '8':
471 case '9':
472 code = (*in - '0') << 4;
473 break;
474
475 case 'a':
476 case 'b':
477 case 'c':
478 case 'd':
479 case 'e':
480 case 'f':
481 code = (*in - 'a' + 10) << 4;
482 break;
483
484 case 'A':
485 case 'B':
486 case 'C':
487 case 'D':
488 case 'E':
489 case 'F':
490 code = (*in - 'A' + 10) << 4;
491 break;
492
493 // TODO(edisonn): spec does not say how to handle this error
494 default:
495 break;
496 }
497
498 in++; // advance
499
500 switch (*in) {
501 case '0':
502 case '1':
503 case '2':
504 case '3':
505 case '4':
506 case '5':
507 case '6':
508 case '7':
509 case '8':
510 case '9':
511 code += (*in - '0');
512 break;
513
514 case 'a':
515 case 'b':
516 case 'c':
517 case 'd':
518 case 'e':
519 case 'f':
520 code += (*in - 'a' + 10);
521 break;
522
523 case 'A':
524 case 'B':
525 case 'C':
526 case 'D':
527 case 'E':
528 case 'F':
529 code += (*in - 'A' + 10);
530 break;
531
532 // TODO(edisonn): spec does not say how to handle this error
533 default:
534 break;
535 }
536
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000537 if (hasOut) { *out = code; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000538 out++;
539 in++;
540 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000541 if (hasOut) { *out = *in; }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000542 out++;
543 in++;
544 }
545 }
546
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000547 if (hasOut) {
548 return in;
549 } else {
550 return start + (out - (const unsigned char*)NULL); // return where the string would end if we reuse the string
551 }
552}
553
554static int readNameLength(int level, const unsigned char* start, const unsigned char* end) {
555 return readName(level, start, end, NULL) - start;
556}
557
558static const unsigned char* readName(int level, const unsigned char* start, const unsigned char* end, SkPdfObject* name, SkPdfAllocator* allocator) {
559 int outLength = readNameLength(level, start, end);
560 // TODO(edisonn): optimize the allocation, don't allocate new string, but put it in a preallocated buffer
561 unsigned char* out = (unsigned char*)allocator->alloc(outLength);
562 start = readName(level, start, end, out);
563 SkPdfObject::makeName(out, out + outLength, name);
564 TRACE_NAME(out, out + outLength);
565 return start;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000566}
567
568// TODO(edisonn): pdf spec let Length to be an indirect object define after the stream
569// that makes for an interesting scenario, where the stream itself contains endstream, together
570// with a reference object with the length, but the real length object would be somewhere else
571// it could confuse the parser
572/*example:
573
5747 0 obj
575<< /length 8 0 R>>
576stream
577...............
578endstream
5798 0 obj #we are in stream actually, not a real object
580<< 10 >> #we are in stream actually, not a real object
581endobj
582endstream
5838 0 obj #real obj
584<< 100 >> #real obj
585endobj
586and it could get worse, with multiple object like this
587*/
588
589// right now implement the silly algorithm that assumes endstream is finishing the stream
590
591
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000592static const unsigned char* readStream(int level, const unsigned char* start, const unsigned char* end, SkPdfObject* dict, SkNativeParsedPDF* doc) {
593 TRACE_INDENT(level, "Stream");
594 start = skipPdfWhiteSpaces(level, start, end);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000595 if (!(start[0] == 's' && start[1] == 't' && start[2] == 'r' && start[3] == 'e' && start[4] == 'a' && start[5] == 'm')) {
596 // no stream. return.
597 return start;
598 }
599
600 start += 6; // strlen("stream")
601 if (start[0] == kCR_PdfWhiteSpace && start[1] == kLF_PdfWhiteSpace) {
602 start += 2;
603 } else if (start[0] == kLF_PdfWhiteSpace) {
604 start += 1;
edisonn@google.com78b38b12013-07-15 18:20:58 +0000605 } else if (isPdfWhiteSpace(start[0])) {
606 start += 1;
607 } else {
608 // TODO(edisonn): warn it should be isPdfDelimiter(start[0])) ?
609 // TODO(edisonn): warning?
edisonn@google.com571c70b2013-07-10 17:09:50 +0000610 }
611
612 SkPdfStreamCommonDictionary* stream = (SkPdfStreamCommonDictionary*) dict;
613 // TODO(edisonn): load Length
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000614 int64_t length = -1;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000615
616 // TODO(edisonn): very basic implementation
edisonn@google.com951d6532013-07-10 23:17:31 +0000617 if (stream->has_Length() && stream->Length(doc) > 0) {
618 length = stream->Length(doc);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000619 }
620
621 // TODO(edisonn): laod external streams
622 // TODO(edisonn): look at the last filter, to determione how to deal with possible issue
623
624 if (length < 0) {
625 // scan the buffer, until we find first endstream
626 // TODO(edisonn): all buffers must have a 0 at the end now,
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000627 const unsigned char* endstream = (const unsigned char*)strrstrk((char*)start, (char*)end, "endstream");
edisonn@google.com571c70b2013-07-10 17:09:50 +0000628
629 if (endstream) {
630 length = endstream - start;
631 if (*(endstream-1) == kLF_PdfWhiteSpace) length--;
edisonn@google.com78b38b12013-07-15 18:20:58 +0000632 if (*(endstream-2) == kCR_PdfWhiteSpace) length--;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000633 }
634 }
635 if (length >= 0) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000636 const unsigned char* endstream = start + length;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000637
638 if (endstream[0] == kCR_PdfWhiteSpace && endstream[1] == kLF_PdfWhiteSpace) {
639 endstream += 2;
640 } else if (endstream[0] == kLF_PdfWhiteSpace) {
641 endstream += 1;
642 }
643
644 // TODO(edisonn): verify the next bytes are "endstream"
645
646 endstream += strlen("endstream");
647 // TODO(edisonn): Assert? report error/warning?
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000648 dict->addStream(start, (size_t)length);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000649 return endstream;
650 }
651 return start;
652}
653
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000654static const unsigned char* readInlineImageStream(int level, const unsigned char* start, const unsigned char* end, SkPdfImageDictionary* inlineImage, SkNativeParsedPDF* doc) {
655 TRACE_INDENT(level, "Inline Image");
edisonn@google.com78b38b12013-07-15 18:20:58 +0000656 // We already processed ID keyword, and we should be positioned immediately after it
657
658 // TODO(edisonn): security: read after end check, or make buffers with extra 2 bytes
659 if (start[0] == kCR_PdfWhiteSpace && start[1] == kLF_PdfWhiteSpace) {
660 start += 2;
661 } else if (start[0] == kLF_PdfWhiteSpace) {
662 start += 1;
663 } else if (isPdfWhiteSpace(start[0])) {
664 start += 1;
665 } else {
666 SkASSERT(isPdfDelimiter(start[0]));
667 // TODO(edisonn): warning?
668 }
669
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000670 const unsigned char* endstream = (const unsigned char*)strrstrk((char*)start, (char*)end, "EI");
671 const unsigned char* endEI = endstream ? endstream + 2 : NULL; // 2 == strlen("EI")
edisonn@google.com78b38b12013-07-15 18:20:58 +0000672
673 if (endstream) {
674 int length = endstream - start;
675 if (*(endstream-1) == kLF_PdfWhiteSpace) length--;
676 if (*(endstream-2) == kCR_PdfWhiteSpace) length--;
677 inlineImage->addStream(start, (size_t)length);
678 } else {
679 // TODO(edisonn): report error in inline image stream (ID-EI) section
680 // TODO(edisonn): based on filter, try to ignore a missing EI, and read data properly
681 return end;
682 }
683 return endEI;
684}
685
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000686static const unsigned char* readDictionary(int level, const unsigned char* start, const unsigned char* end, SkPdfObject* dict, SkPdfAllocator* allocator, SkNativeParsedPDF* doc) {
687 TRACE_INDENT(level, "Dictionary");
edisonn@google.com571c70b2013-07-10 17:09:50 +0000688 SkPdfObject::makeEmptyDictionary(dict);
689
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000690 start = skipPdfWhiteSpaces(level, start, end);
691 SkPdfAllocator tmpStorage; // keys will be stored in dict, we can free them immediately after set.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000692
693 while (start < end && *start == kNamed_PdfDelimiter) {
694 SkPdfObject key;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000695 //*start = '\0';
edisonn@google.com571c70b2013-07-10 17:09:50 +0000696 start++;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000697 start = readName(level + 1, start, end, &key, &tmpStorage);
698 start = skipPdfWhiteSpaces(level + 1, start, end);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000699
700 if (start < end) {
701 SkPdfObject* value = allocator->allocObject();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000702 start = nextObject(level + 1, start, end, value, allocator, doc);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000703
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000704 start = skipPdfWhiteSpaces(level + 1, start, end);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000705
706 if (start < end) {
707 // seems we have an indirect reference
708 if (isPdfDigit(*start)) {
709 SkPdfObject generation;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000710 start = nextObject(level + 1, start, end, &generation, allocator, doc);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000711
712 SkPdfObject keywordR;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000713 start = nextObject(level + 1, start, end, &keywordR, allocator, doc);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000714
715 if (value->isInteger() && generation.isInteger() && keywordR.isKeywordReference()) {
716 int64_t id = value->intValue();
717 value->reset();
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000718 SkPdfObject::makeReference((unsigned int)id, (unsigned int)generation.intValue(), value);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000719 dict->set(&key, value);
720 } else {
721 // error, ignore
722 dict->set(&key, value);
723 }
724 } else {
725 // next elem is not a digit, but it might not be / either!
726 dict->set(&key, value);
727 }
728 } else {
729 // /key >>
730 dict->set(&key, value);
731 return end;
732 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000733 start = skipPdfWhiteSpaces(level + 1, start, end);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000734 } else {
735 dict->set(&key, &SkPdfObject::kNull);
736 return end;
737 }
738 }
739
740 // TODO(edisonn): options to ignore these errors
741
742 // now we should expect >>
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000743 start = skipPdfWhiteSpaces(level, start, end);
edisonn@google.com78b38b12013-07-15 18:20:58 +0000744 if (*start != kClosedInequityBracket_PdfDelimiter) {
745 // TODO(edisonn): report/warning
746 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000747 //*start = '\0';
edisonn@google.com78b38b12013-07-15 18:20:58 +0000748 start++; // skip >
749 if (*start != kClosedInequityBracket_PdfDelimiter) {
750 // TODO(edisonn): report/warning
751 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000752 //*start = '\0';
edisonn@google.com78b38b12013-07-15 18:20:58 +0000753 start++; // skip >
edisonn@google.com571c70b2013-07-10 17:09:50 +0000754
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000755 start = readStream(level, start, end, dict, doc);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000756
757 return start;
758}
759
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000760const unsigned char* nextObject(int level, const unsigned char* start, const unsigned char* end, SkPdfObject* token, SkPdfAllocator* allocator, SkNativeParsedPDF* doc) {
761 const unsigned char* current;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000762
763 // skip white spaces
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000764 start = skipPdfWhiteSpaces(level, start, end);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000765
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000766 current = endOfPdfToken(level, start, end);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000767
768 // no token, len would be 0
769 if (current == start) {
770 return NULL;
771 }
772
773 int tokenLen = current - start;
774
775 if (tokenLen == 1) {
776 // start array
777 switch (*start) {
778 case kOpenedSquareBracket_PdfDelimiter:
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000779 //*start = '\0';
edisonn@google.com571c70b2013-07-10 17:09:50 +0000780 SkPdfObject::makeEmptyArray(token);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000781 return readArray(level + 1, current, end, token, allocator, doc);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000782
783 case kOpenedRoundBracket_PdfDelimiter:
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000784 //*start = '\0';
785 return readString(level, start + 1, end, token, allocator);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000786
787 case kOpenedInequityBracket_PdfDelimiter:
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000788 //*start = '\0';
edisonn@google.com571c70b2013-07-10 17:09:50 +0000789 if (end > start + 1 && start[1] == kOpenedInequityBracket_PdfDelimiter) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000790 //start[1] = '\0'; // optional
edisonn@google.com571c70b2013-07-10 17:09:50 +0000791 // TODO(edisonn): pass here the length somehow?
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000792 return readDictionary(level + 1, start + 2, end, token, allocator, doc); // skip <<
edisonn@google.com571c70b2013-07-10 17:09:50 +0000793 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000794 return readHexString(level, start + 1, end, token, allocator); // skip <
edisonn@google.com571c70b2013-07-10 17:09:50 +0000795 }
796
797 case kNamed_PdfDelimiter:
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000798 //*start = '\0';
799 return readName(level, start + 1, end, token, allocator);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000800
801 // TODO(edisonn): what to do curly brackets? read spec!
802 case kOpenedCurlyBracket_PdfDelimiter:
803 default:
804 break;
805 }
806
807 SkASSERT(!isPdfWhiteSpace(*start));
808 if (isPdfDelimiter(*start)) {
809 // TODO(edisonn): how stream ] } > ) will be handled?
810 // for now ignore, and it will become a keyword to be ignored
811 }
812 }
813
814 if (tokenLen == 4 && start[0] == 'n' && start[1] == 'u' && start[2] == 'l' && start[3] == 'l') {
815 SkPdfObject::makeNull(token);
816 return current;
817 }
818
819 if (tokenLen == 4 && start[0] == 't' && start[1] == 'r' && start[2] == 'u' && start[3] == 'e') {
820 SkPdfObject::makeBoolean(true, token);
821 return current;
822 }
823
824 if (tokenLen == 5 && start[0] == 'f' && start[1] == 'a' && start[2] == 'l' && start[3] == 's' && start[3] == 'e') {
825 SkPdfObject::makeBoolean(false, token);
826 return current;
827 }
828
829 if (isPdfNumeric(*start)) {
830 SkPdfObject::makeNumeric(start, current, token);
831 } else {
832 SkPdfObject::makeKeyword(start, current, token);
833 }
834 return current;
835}
836
837SkPdfObject* SkPdfAllocator::allocBlock() {
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000838 fSizeInBytes += BUFFER_SIZE * sizeof(SkPdfObject);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000839 return new SkPdfObject[BUFFER_SIZE];
840}
841
842SkPdfAllocator::~SkPdfAllocator() {
843 for (int i = 0 ; i < fHandles.count(); i++) {
844 free(fHandles[i]);
845 }
846 for (int i = 0 ; i < fHistory.count(); i++) {
edisonn@google.com222382b2013-07-10 22:33:10 +0000847 for (int j = 0 ; j < BUFFER_SIZE; j++) {
848 fHistory[i][j].reset();
849 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000850 delete[] fHistory[i];
851 }
edisonn@google.com222382b2013-07-10 22:33:10 +0000852 for (int j = 0 ; j < BUFFER_SIZE; j++) {
853 fCurrent[j].reset();
854 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000855 delete[] fCurrent;
856}
857
858SkPdfObject* SkPdfAllocator::allocObject() {
859 if (fCurrentUsed >= BUFFER_SIZE) {
860 fHistory.push(fCurrent);
861 fCurrent = allocBlock();
862 fCurrentUsed = 0;
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000863 fSizeInBytes += sizeof(SkPdfObject*);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000864 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000865 fCurrentUsed++;
866 return &fCurrent[fCurrentUsed - 1];
867}
868
869// TODO(edisonn): perf: do no copy the buffers, but use them, and mark cache the result, so there is no need of a second pass
edisonn@google.com951d6532013-07-10 23:17:31 +0000870SkPdfNativeTokenizer::SkPdfNativeTokenizer(SkPdfObject* objWithStream, const SkPdfMapper* mapper, SkPdfAllocator* allocator, SkNativeParsedPDF* doc) : fDoc(doc), fMapper(mapper), fAllocator(allocator), fUncompressedStream(NULL), fUncompressedStreamEnd(NULL), fEmpty(false), fHasPutBack(false) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000871 const unsigned char* buffer = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000872 size_t len = 0;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000873 objWithStream->GetFilteredStreamRef(&buffer, &len);
edisonn@google.com222382b2013-07-10 22:33:10 +0000874 // TODO(edisonn): hack, find end of object
edisonn@google.com78b38b12013-07-15 18:20:58 +0000875 char* endobj = strrstrk((char*)buffer, (char*)buffer + len, "endobj");
edisonn@google.com222382b2013-07-10 22:33:10 +0000876 if (endobj) {
877 len = endobj - (char*)buffer + strlen("endobj");
878 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000879 fUncompressedStreamStart = fUncompressedStream = buffer;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000880 fUncompressedStreamEnd = fUncompressedStream + len;
edisonn@google.com222382b2013-07-10 22:33:10 +0000881}
edisonn@google.com571c70b2013-07-10 17:09:50 +0000882
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000883SkPdfNativeTokenizer::SkPdfNativeTokenizer(const unsigned char* buffer, int len, const SkPdfMapper* mapper, SkPdfAllocator* allocator, SkNativeParsedPDF* doc) : fDoc(doc), fMapper(mapper), fAllocator(allocator), fEmpty(false), fHasPutBack(false) {
edisonn@google.com222382b2013-07-10 22:33:10 +0000884 // TODO(edisonn): hack, find end of object
edisonn@google.com78b38b12013-07-15 18:20:58 +0000885 char* endobj = strrstrk((char*)buffer, (char*)buffer + len, "endobj");
edisonn@google.com222382b2013-07-10 22:33:10 +0000886 if (endobj) {
887 len = endobj - (char*)buffer + strlen("endobj");
888 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000889 fUncompressedStreamStart = fUncompressedStream = buffer;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000890 fUncompressedStreamEnd = fUncompressedStream + len;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000891}
892
893SkPdfNativeTokenizer::~SkPdfNativeTokenizer() {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000894}
895
896bool SkPdfNativeTokenizer::readTokenCore(PdfToken* token) {
897 token->fKeyword = NULL;
898 token->fObject = NULL;
899
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000900 fUncompressedStream = skipPdfWhiteSpaces(0, fUncompressedStream, fUncompressedStreamEnd);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000901 if (fUncompressedStream >= fUncompressedStreamEnd) {
902 return false;
903 }
904
905 SkPdfObject obj;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000906 fUncompressedStream = nextObject(0, fUncompressedStream, fUncompressedStreamEnd, &obj, fAllocator, fDoc);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000907
908 // If it is a keyword, we will only get the pointer of the string
909 if (obj.type() == SkPdfObject::kKeyword_PdfObjectType) {
910 token->fKeyword = obj.c_str();
911 token->fKeywordLength = obj.len();
912 token->fType = kKeyword_TokenType;
913 } else {
914 SkPdfObject* pobj = fAllocator->allocObject();
915 *pobj = obj;
916 token->fObject = pobj;
917 token->fType = kObject_TokenType;
918 }
919
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000920#ifdef PDF_TRACE_READ_TOKEN
edisonn@google.com571c70b2013-07-10 17:09:50 +0000921 static int read_op = 0;
922 read_op++;
edisonn@google.com222382b2013-07-10 22:33:10 +0000923 if (548 == read_op) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000924 printf("break;\n");
925 }
926 printf("%i READ %s %s\n", read_op, token->fType == kKeyword_TokenType ? "Keyword" : "Object", token->fKeyword ? std::string(token->fKeyword, token->fKeywordLength).c_str() : token->fObject->toString().c_str());
927#endif
928
929 return true;
930}
931
932void SkPdfNativeTokenizer::PutBack(PdfToken token) {
933 SkASSERT(!fHasPutBack);
934 fHasPutBack = true;
935 fPutBack = token;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000936#ifdef PDF_TRACE_READ_TOKEN
edisonn@google.com571c70b2013-07-10 17:09:50 +0000937 printf("PUT_BACK %s %s\n", token.fType == kKeyword_TokenType ? "Keyword" : "Object", token.fKeyword ? std::string(token.fKeyword, token.fKeywordLength).c_str(): token.fObject->toString().c_str());
938#endif
939}
940
941bool SkPdfNativeTokenizer::readToken(PdfToken* token) {
942 if (fHasPutBack) {
943 *token = fPutBack;
944 fHasPutBack = false;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000945#ifdef PDF_TRACE_READ_TOKEN
edisonn@google.com571c70b2013-07-10 17:09:50 +0000946 printf("READ_BACK %s %s\n", token->fType == kKeyword_TokenType ? "Keyword" : "Object", token->fKeyword ? std::string(token->fKeyword, token->fKeywordLength).c_str() : token->fObject->toString().c_str());
947#endif
948 return true;
949 }
950
951 if (fEmpty) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000952#ifdef PDF_TRACE_READ_TOKEN
edisonn@google.com571c70b2013-07-10 17:09:50 +0000953 printf("EMPTY TOKENIZER\n");
954#endif
955 return false;
956 }
957
958 return readTokenCore(token);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000959}
edisonn@google.com78b38b12013-07-15 18:20:58 +0000960
961#define DECLARE_PDF_NAME(longName) SkPdfName longName((char*)#longName)
962
963// keys
964DECLARE_PDF_NAME(BitsPerComponent);
965DECLARE_PDF_NAME(ColorSpace);
966DECLARE_PDF_NAME(Decode);
967DECLARE_PDF_NAME(DecodeParms);
968DECLARE_PDF_NAME(Filter);
969DECLARE_PDF_NAME(Height);
970DECLARE_PDF_NAME(ImageMask);
971DECLARE_PDF_NAME(Intent); // PDF 1.1 - the key, or the abreviations?
972DECLARE_PDF_NAME(Interpolate);
973DECLARE_PDF_NAME(Width);
974
975// values
976DECLARE_PDF_NAME(DeviceGray);
977DECLARE_PDF_NAME(DeviceRGB);
978DECLARE_PDF_NAME(DeviceCMYK);
979DECLARE_PDF_NAME(Indexed);
980DECLARE_PDF_NAME(ASCIIHexDecode);
981DECLARE_PDF_NAME(ASCII85Decode);
982DECLARE_PDF_NAME(LZWDecode);
983DECLARE_PDF_NAME(FlateDecode); // PDF 1.2
984DECLARE_PDF_NAME(RunLengthDecode);
985DECLARE_PDF_NAME(CCITTFaxDecode);
986DECLARE_PDF_NAME(DCTDecode);
987
988#define HANDLE_NAME_ABBR(obj,longName,shortName) if (obj->isName(#shortName)) return &longName;
989
990
991static SkPdfObject* inlineImageKeyAbbreviationExpand(SkPdfObject* key) {
992 if (!key || !key->isName()) {
993 return key;
994 }
995
996 // TODO(edisonn): use autogenerated code!
997 HANDLE_NAME_ABBR(key, BitsPerComponent, BPC);
998 HANDLE_NAME_ABBR(key, ColorSpace, CS);
999 HANDLE_NAME_ABBR(key, Decode, D);
1000 HANDLE_NAME_ABBR(key, DecodeParms, DP);
1001 HANDLE_NAME_ABBR(key, Filter, F);
1002 HANDLE_NAME_ABBR(key, Height, H);
1003 HANDLE_NAME_ABBR(key, ImageMask, IM);
1004// HANDLE_NAME_ABBR(key, Intent, );
1005 HANDLE_NAME_ABBR(key, Interpolate, I);
1006 HANDLE_NAME_ABBR(key, Width, W);
1007
1008 return key;
1009}
1010
1011static SkPdfObject* inlineImageValueAbbreviationExpand(SkPdfObject* value) {
1012 if (!value || !value->isName()) {
1013 return value;
1014 }
1015
1016 // TODO(edisonn): use autogenerated code!
1017 HANDLE_NAME_ABBR(value, DeviceGray, G);
1018 HANDLE_NAME_ABBR(value, DeviceRGB, RGB);
1019 HANDLE_NAME_ABBR(value, DeviceCMYK, CMYK);
1020 HANDLE_NAME_ABBR(value, Indexed, I);
1021 HANDLE_NAME_ABBR(value, ASCIIHexDecode, AHx);
1022 HANDLE_NAME_ABBR(value, ASCII85Decode, A85);
1023 HANDLE_NAME_ABBR(value, LZWDecode, LZW);
1024 HANDLE_NAME_ABBR(value, FlateDecode, Fl); // (PDF 1.2)
1025 HANDLE_NAME_ABBR(value, RunLengthDecode, RL);
1026 HANDLE_NAME_ABBR(value, CCITTFaxDecode, CCF);
1027 HANDLE_NAME_ABBR(value, DCTDecode, DCT);
1028
1029 return value;
1030}
1031
1032SkPdfImageDictionary* SkPdfNativeTokenizer::readInlineImage() {
1033 // BI already processed
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001034 fUncompressedStream = skipPdfWhiteSpaces(0, fUncompressedStream, fUncompressedStreamEnd);
edisonn@google.com78b38b12013-07-15 18:20:58 +00001035 if (fUncompressedStream >= fUncompressedStreamEnd) {
1036 return NULL;
1037 }
1038
1039 SkPdfImageDictionary* inlineImage = (SkPdfImageDictionary*)fAllocator->allocObject();
1040 SkPdfObject::makeEmptyDictionary(inlineImage);
1041
1042 while (fUncompressedStream < fUncompressedStreamEnd) {
1043 SkPdfObject* key = fAllocator->allocObject();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001044 fUncompressedStream = nextObject(0, fUncompressedStream, fUncompressedStreamEnd, key, fAllocator, fDoc);
edisonn@google.com78b38b12013-07-15 18:20:58 +00001045
1046 if (key->isKeyword() && key->len() == 2 && key->c_str()[0] == 'I' && key->c_str()[1] == 'D') { // ID
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001047 fUncompressedStream = readInlineImageStream(0, fUncompressedStream, fUncompressedStreamEnd, inlineImage, fDoc);
edisonn@google.com78b38b12013-07-15 18:20:58 +00001048 return inlineImage;
1049 } else {
1050 SkPdfObject* obj = fAllocator->allocObject();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001051 fUncompressedStream = nextObject(0, fUncompressedStream, fUncompressedStreamEnd, obj, fAllocator, fDoc);
edisonn@google.com78b38b12013-07-15 18:20:58 +00001052 // TODO(edisonn): perf maybe we should not expand abreviation like this
1053 inlineImage->set(inlineImageKeyAbbreviationExpand(key),
1054 inlineImageValueAbbreviationExpand(obj));
1055 }
1056 }
1057 // TODO(edisonn): report end of data with inline image without an EI
1058 return inlineImage;
1059}