OLD | NEW |
1 | 1 |
2 #include "SkPdfNativeTokenizer.h" | 2 #include "SkPdfNativeTokenizer.h" |
3 | 3 #include "SkPdfObject.h" |
4 SkPdfNativeTokenizer::SkPdfNativeTokenizer() { | 4 #include "SkPdfConfig.h" |
5 // TODO(edisonn): Auto-generated constructor stub | 5 |
6 | 6 #include "SkPdfStreamCommonDictionary_autogen.h" |
| 7 |
| 8 unsigned char* skipPdfWhiteSpaces(unsigned char* start, unsigned char* end) { |
| 9 while (start < end && isPdfWhiteSpace(*start)) { |
| 10 if (*start == kComment_PdfDelimiter) { |
| 11 // skip the comment until end of line |
| 12 while (start < end && !isPdfEOL(*start)) { |
| 13 *start = '\0'; |
| 14 start++; |
| 15 } |
| 16 } else { |
| 17 *start = '\0'; |
| 18 start++; |
| 19 } |
| 20 } |
| 21 return start; |
| 22 } |
| 23 |
| 24 // TODO(edisonn) '(' can be used, will it break the string a delimiter or space
inside () ? |
| 25 unsigned char* endOfPdfToken(unsigned char* start, unsigned char* end) { |
| 26 //int opened brackets |
| 27 //TODO(edisonn): what out for special chars, like \n, \032 |
| 28 |
| 29 SkASSERT(!isPdfWhiteSpace(*start)); |
| 30 |
| 31 if (start < end && isPdfDelimiter(*start)) { |
| 32 start++; |
| 33 return start; |
| 34 } |
| 35 |
| 36 while (start < end && !isPdfWhiteSpaceOrPdfDelimiter(*start)) { |
| 37 start++; |
| 38 } |
| 39 return start; |
| 40 } |
| 41 |
| 42 unsigned char* skipPdfComment(unsigned char* start, unsigned char* end) { |
| 43 SkASSERT(start == end || *start == kComment_PdfDelimiter); |
| 44 while (start < end && isPdfEOL(*start)) { |
| 45 *start = '\0'; |
| 46 start++; |
| 47 } |
| 48 return start; |
| 49 } |
| 50 |
| 51 // last elem has to be ] |
| 52 unsigned char* readArray(unsigned char* start, unsigned char* end, SkPdfObject*
array, SkPdfAllocator* allocator) { |
| 53 while (start < end) { |
| 54 // skip white spaces |
| 55 start = skipPdfWhiteSpaces(start, end); |
| 56 |
| 57 unsigned char* endOfToken = endOfPdfToken(start, end); |
| 58 |
| 59 if (endOfToken == start) { |
| 60 // TODO(edisonn): report error in pdf file (end of stream with ] for
end of aray |
| 61 return start; |
| 62 } |
| 63 |
| 64 if (endOfToken == start + 1 && *start == kClosedSquareBracket_PdfDelimit
er) { |
| 65 return endOfToken; |
| 66 } |
| 67 |
| 68 SkPdfObject* newObj = allocator->allocObject(); |
| 69 start = nextObject(start, end, newObj, allocator); |
| 70 // TODO(edisonn): perf/memory: put the variables on the stack, and flush
them on the array only when |
| 71 // we are sure they are not references! |
| 72 if (newObj->isKeywordReference() && array->size() >= 2 && array->objAtAI
ndex(array->size() - 1)->isInteger() && array->objAtAIndex(array->size() - 2)->i
sInteger()) { |
| 73 SkPdfObject* gen = array->removeLastInArray(); |
| 74 SkPdfObject* id = array->removeLastInArray(); |
| 75 newObj->reset(); |
| 76 SkPdfObject::makeReference(id->intValue(), gen->intValue(), newObj); |
| 77 } |
| 78 array->appendInArray(newObj); |
| 79 } |
| 80 // TODO(edisonn): report not reached, we should never get here |
| 81 SkASSERT(false); |
| 82 return start; |
| 83 } |
| 84 |
| 85 // When we read strings we will rewrite the string so we will reuse the memory |
| 86 // when we start to read the string, we already consumed the opened bracket |
| 87 unsigned char* readString(unsigned char* start, unsigned char* end, SkPdfObject*
str) { |
| 88 unsigned char* out = start; |
| 89 unsigned char* in = start; |
| 90 |
| 91 int openRoundBrackets = 0; |
| 92 while (in < end && (*in != kClosedRoundBracket_PdfDelimiter || openRoundBrac
kets > 0)) { |
| 93 openRoundBrackets += ((*in) == kOpenedRoundBracket_PdfDelimiter); |
| 94 openRoundBrackets -= ((*in) == kClosedRoundBracket_PdfDelimiter); |
| 95 if (*in == kEscape_PdfSpecial) { |
| 96 if (in + 1 < end) { |
| 97 switch (in[1]) { |
| 98 case 'n': |
| 99 *out = kLF_PdfWhiteSpace; |
| 100 out++; |
| 101 in += 2; |
| 102 break; |
| 103 |
| 104 case 'r': |
| 105 *out = kCR_PdfWhiteSpace; |
| 106 out++; |
| 107 in += 2; |
| 108 break; |
| 109 |
| 110 case 't': |
| 111 *out = kHT_PdfWhiteSpace; |
| 112 out++; |
| 113 in += 2; |
| 114 break; |
| 115 |
| 116 case 'b': |
| 117 // TODO(edisonn): any special meaning to backspace? |
| 118 *out = kBackspace_PdfSpecial; |
| 119 out++; |
| 120 in += 2; |
| 121 break; |
| 122 |
| 123 case 'f': |
| 124 *out = kFF_PdfWhiteSpace; |
| 125 out++; |
| 126 in += 2; |
| 127 break; |
| 128 |
| 129 case kOpenedRoundBracket_PdfDelimiter: |
| 130 *out = kOpenedRoundBracket_PdfDelimiter; |
| 131 out++; |
| 132 in += 2; |
| 133 break; |
| 134 |
| 135 case kClosedRoundBracket_PdfDelimiter: |
| 136 *out = kClosedRoundBracket_PdfDelimiter; |
| 137 out++; |
| 138 in += 2; |
| 139 break; |
| 140 |
| 141 case kEscape_PdfSpecial: |
| 142 *out = kEscape_PdfSpecial; |
| 143 out++; |
| 144 in += 2; |
| 145 break; |
| 146 |
| 147 case '0': |
| 148 case '1': |
| 149 case '2': |
| 150 case '3': |
| 151 case '4': |
| 152 case '5': |
| 153 case '6': |
| 154 case '7': { |
| 155 //read octals |
| 156 in++; // consume backslash |
| 157 |
| 158 int code = 0; |
| 159 int i = 0; |
| 160 while (in < end && *in >= '0' && *in < '8') { |
| 161 code = (code << 3) + ((*in) - '0'); // code * 8
+ d |
| 162 i++; |
| 163 in++; |
| 164 if (i == 3) { |
| 165 *out = code & 0xff; |
| 166 out++; |
| 167 i = 0; |
| 168 } |
| 169 } |
| 170 if (i > 0) { |
| 171 *out = code & 0xff; |
| 172 out++; |
| 173 } |
| 174 } |
| 175 break; |
| 176 |
| 177 default: |
| 178 // Per spec, backslash is ignored is escaped ch is unkno
wn |
| 179 in++; |
| 180 break; |
| 181 } |
| 182 } |
| 183 } else { |
| 184 // TODO(edisonn): perf, avoid copy into itself, maybe first do a sim
ple scan until found backslash ? |
| 185 // we could have one look that first just inc current, and when we f
ind the backslash |
| 186 // we go to this loop |
| 187 *in = *out; |
| 188 in++; |
| 189 out++; |
| 190 } |
| 191 } |
| 192 |
| 193 |
| 194 SkPdfObject::makeString(start, out, str); |
| 195 return in + 1; // consume ) at the end of the string |
| 196 } |
| 197 |
| 198 unsigned char* readHexString(unsigned char* start, unsigned char* end, SkPdfObje
ct* str) { |
| 199 unsigned char* out = start; |
| 200 unsigned char* in = start; |
| 201 |
| 202 unsigned char code = 0; |
| 203 |
| 204 while (in < end) { |
| 205 while (in < end && isPdfWhiteSpace(*in)) { |
| 206 in++; |
| 207 } |
| 208 |
| 209 if (*in == kClosedInequityBracket_PdfDelimiter) { |
| 210 *in = '\0'; |
| 211 in++; |
| 212 // normal exit |
| 213 break; |
| 214 } |
| 215 |
| 216 if (in >= end) { |
| 217 // end too soon |
| 218 break; |
| 219 } |
| 220 |
| 221 switch (*in) { |
| 222 case '0': |
| 223 case '1': |
| 224 case '2': |
| 225 case '3': |
| 226 case '4': |
| 227 case '5': |
| 228 case '6': |
| 229 case '7': |
| 230 case '8': |
| 231 case '9': |
| 232 code = (*in - '0') << 4; |
| 233 break; |
| 234 |
| 235 case 'a': |
| 236 case 'b': |
| 237 case 'c': |
| 238 case 'd': |
| 239 case 'e': |
| 240 case 'f': |
| 241 code = (*in - 'a' + 10) << 4; |
| 242 break; |
| 243 |
| 244 case 'A': |
| 245 case 'B': |
| 246 case 'C': |
| 247 case 'D': |
| 248 case 'E': |
| 249 case 'F': |
| 250 code = (*in - 'A' + 10) << 4; |
| 251 break; |
| 252 |
| 253 // TODO(edisonn): spec does not say how to handle this error |
| 254 default: |
| 255 break; |
| 256 } |
| 257 |
| 258 in++; // advance |
| 259 |
| 260 while (in < end && isPdfWhiteSpace(*in)) { |
| 261 in++; |
| 262 } |
| 263 |
| 264 // TODO(edisonn): report error |
| 265 if (in >= end) { |
| 266 *out = code; |
| 267 out++; |
| 268 break; |
| 269 } |
| 270 |
| 271 if (*in == kClosedInequityBracket_PdfDelimiter) { |
| 272 *out = code; |
| 273 out++; |
| 274 break; |
| 275 } |
| 276 |
| 277 switch (*in) { |
| 278 case '0': |
| 279 case '1': |
| 280 case '2': |
| 281 case '3': |
| 282 case '4': |
| 283 case '5': |
| 284 case '6': |
| 285 case '7': |
| 286 case '8': |
| 287 case '9': |
| 288 code += (*in - '0'); |
| 289 break; |
| 290 |
| 291 case 'a': |
| 292 case 'b': |
| 293 case 'c': |
| 294 case 'd': |
| 295 case 'e': |
| 296 case 'f': |
| 297 code += (*in - 'a' + 10); |
| 298 break; |
| 299 |
| 300 case 'A': |
| 301 case 'B': |
| 302 case 'C': |
| 303 case 'D': |
| 304 case 'E': |
| 305 case 'F': |
| 306 code += (*in - 'A' + 10); |
| 307 break; |
| 308 |
| 309 // TODO(edisonn): spec does not say how to handle this error |
| 310 default: |
| 311 break; |
| 312 } |
| 313 |
| 314 *out = code; |
| 315 out++; |
| 316 in++; |
| 317 } |
| 318 |
| 319 if (out < in) { |
| 320 *out = '\0'; |
| 321 } |
| 322 |
| 323 SkPdfObject::makeHexString(start, out, str); |
| 324 return in; // consume > at the end of the string |
| 325 } |
| 326 |
| 327 // TODO(edisonn): before PDF 1.2 name could not have special characters, add ver
sion parameter |
| 328 unsigned char* readName(unsigned char* start, unsigned char* end, SkPdfObject* n
ame) { |
| 329 unsigned char* out = start; |
| 330 unsigned char* in = start; |
| 331 |
| 332 unsigned char code = 0; |
| 333 |
| 334 while (in < end) { |
| 335 if (isPdfWhiteSpaceOrPdfDelimiter(*in)) { |
| 336 break; |
| 337 } |
| 338 |
| 339 if (*in == '#' && in + 2 < end) { |
| 340 in++; |
| 341 switch (*in) { |
| 342 case '0': |
| 343 case '1': |
| 344 case '2': |
| 345 case '3': |
| 346 case '4': |
| 347 case '5': |
| 348 case '6': |
| 349 case '7': |
| 350 case '8': |
| 351 case '9': |
| 352 code = (*in - '0') << 4; |
| 353 break; |
| 354 |
| 355 case 'a': |
| 356 case 'b': |
| 357 case 'c': |
| 358 case 'd': |
| 359 case 'e': |
| 360 case 'f': |
| 361 code = (*in - 'a' + 10) << 4; |
| 362 break; |
| 363 |
| 364 case 'A': |
| 365 case 'B': |
| 366 case 'C': |
| 367 case 'D': |
| 368 case 'E': |
| 369 case 'F': |
| 370 code = (*in - 'A' + 10) << 4; |
| 371 break; |
| 372 |
| 373 // TODO(edisonn): spec does not say how to handle this error |
| 374 default: |
| 375 break; |
| 376 } |
| 377 |
| 378 in++; // advance |
| 379 |
| 380 switch (*in) { |
| 381 case '0': |
| 382 case '1': |
| 383 case '2': |
| 384 case '3': |
| 385 case '4': |
| 386 case '5': |
| 387 case '6': |
| 388 case '7': |
| 389 case '8': |
| 390 case '9': |
| 391 code += (*in - '0'); |
| 392 break; |
| 393 |
| 394 case 'a': |
| 395 case 'b': |
| 396 case 'c': |
| 397 case 'd': |
| 398 case 'e': |
| 399 case 'f': |
| 400 code += (*in - 'a' + 10); |
| 401 break; |
| 402 |
| 403 case 'A': |
| 404 case 'B': |
| 405 case 'C': |
| 406 case 'D': |
| 407 case 'E': |
| 408 case 'F': |
| 409 code += (*in - 'A' + 10); |
| 410 break; |
| 411 |
| 412 // TODO(edisonn): spec does not say how to handle this error |
| 413 default: |
| 414 break; |
| 415 } |
| 416 |
| 417 *out = code; |
| 418 out++; |
| 419 in++; |
| 420 } else { |
| 421 *out = *in; |
| 422 out++; |
| 423 in++; |
| 424 } |
| 425 } |
| 426 |
| 427 SkPdfObject::makeName(start, out, name); |
| 428 return in; |
| 429 } |
| 430 |
| 431 // TODO(edisonn): pdf spec let Length to be an indirect object define after the
stream |
| 432 // that makes for an interesting scenario, where the stream itself contains ends
tream, together |
| 433 // with a reference object with the length, but the real length object would be
somewhere else |
| 434 // it could confuse the parser |
| 435 /*example: |
| 436 |
| 437 7 0 obj |
| 438 << /length 8 0 R>> |
| 439 stream |
| 440 ............... |
| 441 endstream |
| 442 8 0 obj #we are in stream actually, not a real object |
| 443 << 10 >> #we are in stream actually, not a real object |
| 444 endobj |
| 445 endstream |
| 446 8 0 obj #real obj |
| 447 << 100 >> #real obj |
| 448 endobj |
| 449 and it could get worse, with multiple object like this |
| 450 */ |
| 451 |
| 452 // right now implement the silly algorithm that assumes endstream is finishing t
he stream |
| 453 |
| 454 |
| 455 unsigned char* readStream(unsigned char* start, unsigned char* end, SkPdfObject*
dict) { |
| 456 start = skipPdfWhiteSpaces(start, end); |
| 457 if (!(start[0] == 's' && start[1] == 't' && start[2] == 'r' && start[3] == '
e' && start[4] == 'a' && start[5] == 'm')) { |
| 458 // no stream. return. |
| 459 return start; |
| 460 } |
| 461 |
| 462 start += 6; // strlen("stream") |
| 463 if (start[0] == kCR_PdfWhiteSpace && start[1] == kLF_PdfWhiteSpace) { |
| 464 start += 2; |
| 465 } else if (start[0] == kLF_PdfWhiteSpace) { |
| 466 start += 1; |
| 467 } |
| 468 |
| 469 SkPdfStreamCommonDictionary* stream = (SkPdfStreamCommonDictionary*) dict; |
| 470 // TODO(edisonn): load Length |
| 471 int length = -1; |
| 472 |
| 473 // TODO(edisonn): very basic implementation |
| 474 if (stream->has_Length() && stream->Length(NULL) > 0) { |
| 475 length = stream->Length(NULL); |
| 476 } |
| 477 |
| 478 // TODO(edisonn): laod external streams |
| 479 // TODO(edisonn): look at the last filter, to determione how to deal with po
ssible issue |
| 480 |
| 481 if (length < 0) { |
| 482 // scan the buffer, until we find first endstream |
| 483 // TODO(edisonn): all buffers must have a 0 at the end now, |
| 484 // TODO(edisonn): hack (mark end of content with 0) |
| 485 unsigned char lastCh = *end; |
| 486 *end = '\0'; |
| 487 //SkASSERT(*end == '\0'); |
| 488 unsigned char* endstream = (unsigned char*)strstr((const char*)start, "e
ndstream"); |
| 489 *end = lastCh; |
| 490 |
| 491 if (endstream) { |
| 492 length = endstream - start; |
| 493 if (*(endstream-1) == kLF_PdfWhiteSpace) length--; |
| 494 if (*(endstream-1) == kCR_PdfWhiteSpace) length--; |
| 495 } |
| 496 } |
| 497 if (length >= 0) { |
| 498 unsigned char* endstream = start + length; |
| 499 |
| 500 if (endstream[0] == kCR_PdfWhiteSpace && endstream[1] == kLF_PdfWhiteSpa
ce) { |
| 501 endstream += 2; |
| 502 } else if (endstream[0] == kLF_PdfWhiteSpace) { |
| 503 endstream += 1; |
| 504 } |
| 505 |
| 506 // TODO(edisonn): verify the next bytes are "endstream" |
| 507 |
| 508 endstream += strlen("endstream"); |
| 509 // TODO(edisonn): Assert? report error/warning? |
| 510 dict->addStream(start, length); |
| 511 return endstream; |
| 512 } |
| 513 return start; |
| 514 } |
| 515 |
| 516 unsigned char* readDictionary(unsigned char* start, unsigned char* end, SkPdfObj
ect* dict, SkPdfAllocator* allocator) { |
| 517 SkPdfObject::makeEmptyDictionary(dict); |
| 518 |
| 519 start = skipPdfWhiteSpaces(start, end); |
| 520 |
| 521 while (start < end && *start == kNamed_PdfDelimiter) { |
| 522 SkPdfObject key; |
| 523 *start = '\0'; |
| 524 start++; |
| 525 start = readName(start, end, &key); |
| 526 start = skipPdfWhiteSpaces(start, end); |
| 527 |
| 528 if (start < end) { |
| 529 SkPdfObject* value = allocator->allocObject(); |
| 530 start = nextObject(start, end, value, allocator); |
| 531 |
| 532 start = skipPdfWhiteSpaces(start, end); |
| 533 |
| 534 if (start < end) { |
| 535 // seems we have an indirect reference |
| 536 if (isPdfDigit(*start)) { |
| 537 SkPdfObject generation; |
| 538 start = nextObject(start, end, &generation, allocator); |
| 539 |
| 540 SkPdfObject keywordR; |
| 541 start = nextObject(start, end, &keywordR, allocator); |
| 542 |
| 543 if (value->isInteger() && generation.isInteger() && keywordR
.isKeywordReference()) { |
| 544 int64_t id = value->intValue(); |
| 545 value->reset(); |
| 546 SkPdfObject::makeReference(id, generation.intValue(), va
lue); |
| 547 dict->set(&key, value); |
| 548 } else { |
| 549 // error, ignore |
| 550 dict->set(&key, value); |
| 551 } |
| 552 } else { |
| 553 // next elem is not a digit, but it might not be / either! |
| 554 dict->set(&key, value); |
| 555 } |
| 556 } else { |
| 557 // /key >> |
| 558 dict->set(&key, value); |
| 559 return end; |
| 560 } |
| 561 start = skipPdfWhiteSpaces(start, end); |
| 562 } else { |
| 563 dict->set(&key, &SkPdfObject::kNull); |
| 564 return end; |
| 565 } |
| 566 } |
| 567 |
| 568 // TODO(edisonn): options to ignore these errors |
| 569 |
| 570 // now we should expect >> |
| 571 start = skipPdfWhiteSpaces(start, end); |
| 572 start = endOfPdfToken(start, end); // > |
| 573 start = endOfPdfToken(start, end); // > |
| 574 |
| 575 // TODO(edisonn): read stream ... put dict and stream in a struct, and have
a pointer to struct ... |
| 576 // or alocate 2 objects, and if there is no stream, free it to be used by so
meone else? or just leave it ? |
| 577 |
| 578 start = readStream(start, end, dict); |
| 579 |
| 580 return start; |
| 581 } |
| 582 |
| 583 unsigned char* nextObject(unsigned char* start, unsigned char* end, SkPdfObject*
token, SkPdfAllocator* allocator) { |
| 584 unsigned char* current; |
| 585 |
| 586 // skip white spaces |
| 587 start = skipPdfWhiteSpaces(start, end); |
| 588 |
| 589 current = endOfPdfToken(start, end); |
| 590 |
| 591 // no token, len would be 0 |
| 592 if (current == start) { |
| 593 return NULL; |
| 594 } |
| 595 |
| 596 int tokenLen = current - start; |
| 597 |
| 598 if (tokenLen == 1) { |
| 599 // start array |
| 600 switch (*start) { |
| 601 case kOpenedSquareBracket_PdfDelimiter: |
| 602 *start = '\0'; |
| 603 SkPdfObject::makeEmptyArray(token); |
| 604 return readArray(current, end, token, allocator); |
| 605 |
| 606 case kOpenedRoundBracket_PdfDelimiter: |
| 607 *start = '\0'; |
| 608 return readString(start, end, token); |
| 609 |
| 610 case kOpenedInequityBracket_PdfDelimiter: |
| 611 *start = '\0'; |
| 612 if (end > start + 1 && start[1] == kOpenedInequityBracket_PdfDel
imiter) { |
| 613 // TODO(edisonn): pass here the length somehow? |
| 614 return readDictionary(start + 2, end, token, allocator); //
skip << |
| 615 } else { |
| 616 return readHexString(start + 1, end, token); // skip < |
| 617 } |
| 618 |
| 619 case kNamed_PdfDelimiter: |
| 620 *start = '\0'; |
| 621 return readName(start + 1, end, token); |
| 622 |
| 623 // TODO(edisonn): what to do curly brackets? read spec! |
| 624 case kOpenedCurlyBracket_PdfDelimiter: |
| 625 default: |
| 626 break; |
| 627 } |
| 628 |
| 629 SkASSERT(!isPdfWhiteSpace(*start)); |
| 630 if (isPdfDelimiter(*start)) { |
| 631 // TODO(edisonn): how stream ] } > ) will be handled? |
| 632 // for now ignore, and it will become a keyword to be ignored |
| 633 } |
| 634 } |
| 635 |
| 636 if (tokenLen == 4 && start[0] == 'n' && start[1] == 'u' && start[2] == 'l' &
& start[3] == 'l') { |
| 637 SkPdfObject::makeNull(token); |
| 638 return current; |
| 639 } |
| 640 |
| 641 if (tokenLen == 4 && start[0] == 't' && start[1] == 'r' && start[2] == 'u' &
& start[3] == 'e') { |
| 642 SkPdfObject::makeBoolean(true, token); |
| 643 return current; |
| 644 } |
| 645 |
| 646 if (tokenLen == 5 && start[0] == 'f' && start[1] == 'a' && start[2] == 'l' &
& start[3] == 's' && start[3] == 'e') { |
| 647 SkPdfObject::makeBoolean(false, token); |
| 648 return current; |
| 649 } |
| 650 |
| 651 if (isPdfNumeric(*start)) { |
| 652 SkPdfObject::makeNumeric(start, current, token); |
| 653 } else { |
| 654 SkPdfObject::makeKeyword(start, current, token); |
| 655 } |
| 656 return current; |
| 657 } |
| 658 |
| 659 SkPdfObject* SkPdfAllocator::allocBlock() { |
| 660 return new SkPdfObject[BUFFER_SIZE]; |
| 661 } |
| 662 |
| 663 SkPdfAllocator::~SkPdfAllocator() { |
| 664 for (int i = 0 ; i < fHandles.count(); i++) { |
| 665 free(fHandles[i]); |
| 666 } |
| 667 for (int i = 0 ; i < fHistory.count(); i++) { |
| 668 delete[] fHistory[i]; |
| 669 } |
| 670 delete[] fCurrent; |
| 671 } |
| 672 |
| 673 SkPdfObject* SkPdfAllocator::allocObject() { |
| 674 if (fCurrentUsed >= BUFFER_SIZE) { |
| 675 fHistory.push(fCurrent); |
| 676 fCurrent = allocBlock(); |
| 677 fCurrentUsed = 0; |
| 678 } |
| 679 |
| 680 fCurrentUsed++; |
| 681 return &fCurrent[fCurrentUsed - 1]; |
| 682 } |
| 683 |
| 684 // 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 |
| 685 SkPdfNativeTokenizer::SkPdfNativeTokenizer(SkPdfObject* objWithStream, const SkP
dfMapper* mapper, SkPdfAllocator* allocator) : fMapper(mapper), fAllocator(alloc
ator), fUncompressedStream(NULL), fUncompressedStreamEnd(NULL), fEmpty(false), f
HasPutBack(false) { |
| 686 unsigned char* buffer = NULL; |
| 687 size_t len = 0; |
| 688 objWithStream->GetFilteredStreamRef(&buffer, &len, fAllocator); |
| 689 fUncompressedStreamStart = fUncompressedStream = (unsigned char*)fAllocator-
>alloc(len); |
| 690 fUncompressedStreamEnd = fUncompressedStream + len; |
| 691 memcpy(fUncompressedStream, buffer, len);} |
| 692 |
| 693 SkPdfNativeTokenizer::SkPdfNativeTokenizer(unsigned char* buffer, int len, const
SkPdfMapper* mapper, SkPdfAllocator* allocator) : fMapper(mapper), fAllocator(a
llocator), fEmpty(false), fHasPutBack(false) { |
| 694 fUncompressedStreamStart = fUncompressedStream = (unsigned char*)fAllocator-
>alloc(len); |
| 695 fUncompressedStreamEnd = fUncompressedStream + len; |
| 696 memcpy(fUncompressedStream, buffer, len); |
7 } | 697 } |
8 | 698 |
9 SkPdfNativeTokenizer::~SkPdfNativeTokenizer() { | 699 SkPdfNativeTokenizer::~SkPdfNativeTokenizer() { |
10 // TODO(edisonn): Auto-generated destructor stub | 700 // free the unparsed stream, we don't need it. |
11 } | 701 // the parsed one is locked as it contains the strings and keywords referenc
ed in objects |
12 | 702 if (fUncompressedStream) { |
| 703 realloc(fUncompressedStreamStart, fUncompressedStream - fUncompressedStr
eamStart); |
| 704 } else { |
| 705 SkASSERT(false); |
| 706 } |
| 707 } |
| 708 |
| 709 bool SkPdfNativeTokenizer::readTokenCore(PdfToken* token) { |
| 710 token->fKeyword = NULL; |
| 711 token->fObject = NULL; |
| 712 |
| 713 fUncompressedStream = skipPdfWhiteSpaces(fUncompressedStream, fUncompressedS
treamEnd); |
| 714 if (fUncompressedStream >= fUncompressedStreamEnd) { |
| 715 return false; |
| 716 } |
| 717 |
| 718 SkPdfObject obj; |
| 719 fUncompressedStream = nextObject(fUncompressedStream, fUncompressedStreamEnd
, &obj, fAllocator); |
| 720 |
| 721 // If it is a keyword, we will only get the pointer of the string |
| 722 if (obj.type() == SkPdfObject::kKeyword_PdfObjectType) { |
| 723 token->fKeyword = obj.c_str(); |
| 724 token->fKeywordLength = obj.len(); |
| 725 token->fType = kKeyword_TokenType; |
| 726 } else { |
| 727 SkPdfObject* pobj = fAllocator->allocObject(); |
| 728 *pobj = obj; |
| 729 token->fObject = pobj; |
| 730 token->fType = kObject_TokenType; |
| 731 } |
| 732 |
| 733 #ifdef PDF_TRACE |
| 734 static int read_op = 0; |
| 735 read_op++; |
| 736 if (182749 == read_op) { |
| 737 printf("break;\n"); |
| 738 } |
| 739 printf("%i READ %s %s\n", read_op, token->fType == kKeyword_TokenType ? "Key
word" : "Object", token->fKeyword ? std::string(token->fKeyword, token->fKeyword
Length).c_str() : token->fObject->toString().c_str()); |
| 740 #endif |
| 741 |
| 742 return true; |
| 743 } |
| 744 |
| 745 void SkPdfNativeTokenizer::PutBack(PdfToken token) { |
| 746 SkASSERT(!fHasPutBack); |
| 747 fHasPutBack = true; |
| 748 fPutBack = token; |
| 749 #ifdef PDF_TRACE |
| 750 printf("PUT_BACK %s %s\n", token.fType == kKeyword_TokenType ? "Keyword" : "
Object", token.fKeyword ? std::string(token.fKeyword, token.fKeywordLength).c_st
r(): token.fObject->toString().c_str()); |
| 751 #endif |
| 752 } |
| 753 |
| 754 bool SkPdfNativeTokenizer::readToken(PdfToken* token) { |
| 755 if (fHasPutBack) { |
| 756 *token = fPutBack; |
| 757 fHasPutBack = false; |
| 758 #ifdef PDF_TRACE |
| 759 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()); |
| 760 #endif |
| 761 return true; |
| 762 } |
| 763 |
| 764 if (fEmpty) { |
| 765 #ifdef PDF_TRACE |
| 766 printf("EMPTY TOKENIZER\n"); |
| 767 #endif |
| 768 return false; |
| 769 } |
| 770 |
| 771 return readTokenCore(token); |
| 772 } |
OLD | NEW |