blob: 6d5ccae4472566ec214095477b559600d9c9416c [file] [log] [blame]
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkCanvas.h"
9#include "SkDevice.h"
10#include "SkForceLinking.h"
11#include "SkGraphics.h"
12#include "SkImageDecoder.h"
13#include "SkImageEncoder.h"
14#include "SkOSFile.h"
15#include "SkPicture.h"
16#include "SkStream.h"
17#include "SkTypeface.h"
18#include "SkTArray.h"
19#include "picture_utils.h"
20
21#include <iostream>
22#include <cstdio>
23#include <stack>
24
25#include "podofo.h"
26using namespace PoDoFo;
27
28
29__SK_FORCE_IMAGE_DECODER_LINKING;
30
31// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
32// keep for each object pos in file
33// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
34
35// TODO(edisonn): security - validate all the user input, all pdf!
36
37
38#include "SkPdfHeaders_autogen.h"
39#include "SkPdfPodofoMapper_autogen.h"
40#include "SkPdfParser.h"
41
42#include "SkPdfBasics.h"
43#include "SkPdfUtils.h"
44
45#include "SkPdfFont.h"
46
47bool skpdfmap(const PdfMemDocument& podofoDoc, const PdfObject& podofoObj, SkPdfObject** out) {
48 return mapObject(podofoDoc, podofoObj, out);
49}
50
51
52/*
53 * TODO(edisonn):
54 * - all font types and all ppdf font features
55 * - word spacing
56 * - load font for baidu.pdf
57 * - load font for youtube.pdf
58 * - parser for pdf from the definition already available in pdfspec_autogen.py
59 * - all docs from ~/work
60 * - encapsulate podofo in the pdf api so the skpdf does not know anything about podofo ... in progress
61 * - load gs/ especially smask and already known prop (skp) ... in progress
62 * - wrapper on classes for customizations? e.g.
63 * SkPdfPageObjectVanila - has only the basic loaders/getters
64 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
65 * need to find a nice object model for all this with constructors and factories
66 * - deal with inheritable automatically ?
67 * - deal with specific type in spec directly, add all dictionary types to known types
68*/
69
70using namespace std;
71using namespace PoDoFo;
72
73// Utilities
74static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
75 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
76
77 bitmap->allocPixels();
78 bitmap->eraseColor(color);
79}
80
81// TODO(edisonn): synonyms? DeviceRGB and RGB ...
82int GetColorSpaceComponents(const std::string& colorSpace) {
83 if (colorSpace == "DeviceCMYK") {
84 return 4;
85 } else if (colorSpace == "DeviceGray" ||
86 colorSpace == "CalGray" ||
87 colorSpace == "Indexed") {
88 return 1;
89 } else if (colorSpace == "DeviceRGB" ||
90 colorSpace == "CalRGB" ||
91 colorSpace == "Lab") {
92 return 3;
93 } else {
94 return 0;
95 }
96}
97
98const PdfObject* resolveReferenceObject(const PdfMemDocument* pdfDoc,
99 const PdfObject* obj,
100 bool resolveOneElementArrays) {
101 while (obj && (obj->IsReference() || (resolveOneElementArrays &&
102 obj->IsArray() &&
103 obj->GetArray().GetSize() == 1))) {
104 if (obj->IsReference()) {
105 // We need to force the non const, the only update we will do is for recurssion checks.
106 PdfReference& ref = (PdfReference&)obj->GetReference();
107 obj = pdfDoc->GetObjects().GetObject(ref);
108 } else {
109 obj = &obj->GetArray()[0];
110 }
111 }
112
113 return obj;
114}
115
116static SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
117 SkMatrix matrix;
118 matrix.setAll(SkDoubleToScalar(array[0]),
119 SkDoubleToScalar(array[2]),
120 SkDoubleToScalar(array[4]),
121 SkDoubleToScalar(array[1]),
122 SkDoubleToScalar(array[3]),
123 SkDoubleToScalar(array[5]),
124 SkDoubleToScalar(0),
125 SkDoubleToScalar(0),
126 SkDoubleToScalar(1));
127
128 return matrix;
129}
130
131SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
132 double array[6];
133
134 // TODO(edisonn): security issue, ret if size() != 6
135 for (int i = 0; i < 6; i++) {
136 const PdfObject* elem = resolveReferenceObject(pdfArray->doc(), (*pdfArray)[i]->podofo());
137 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
138 return SkMatrix::I(); // TODO(edisonn): report issue
139 }
140 array[i] = elem->GetReal();
141 }
142
143 return SkMatrixFromPdfMatrix(array);
144}
145
146PdfContext* gPdfContext = NULL;
147SkBitmap* gDumpBitmap = NULL;
148SkCanvas* gDumpCanvas = NULL;
149char gLastKeyword[100] = "";
150int gLastOpKeyword = -1;
151char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
152int gReadOp = 0;
153
154
155
156bool hasVisualEffect(const char* pdfOp) {
157 return true;
158 if (*pdfOp == '\0') return false;
159
160 char markedPdfOp[100] = ",";
161 strcat(markedPdfOp, pdfOp);
162 strcat(markedPdfOp, ",");
163
164 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
165}
166
167// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
168static bool readToken(SkPdfTokenizer* fTokenizer, PdfToken* token) {
169 bool ret = fTokenizer->readToken(token);
170
171 gReadOp++;
172
173#ifdef PDF_TRACE_DIFF_IN_PNG
174 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
175 // the numbar and name of last operation, so the file name will reflect op that changed.
176 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
177 gDumpCanvas->flush();
178
179 SkBitmap bitmap;
180 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
181
182 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
183
184 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
185 SkCanvas canvas(device);
186
187 // draw context stuff here
188 SkPaint blueBorder;
189 blueBorder.setColor(SK_ColorBLUE);
190 blueBorder.setStyle(SkPaint::kStroke_Style);
191 blueBorder.setTextSize(SkDoubleToScalar(20));
192
193 SkString str;
194
195 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
196 if (clipStack) {
197 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
198 const SkClipStack::Element* elem;
199 double y = 0;
200 int total = 0;
201 while (elem = iter.next()) {
202 total++;
203 y += 30;
204
205 switch (elem->getType()) {
206 case SkClipStack::Element::kRect_Type:
207 canvas.drawRect(elem->getRect(), blueBorder);
208 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
209 break;
210 case SkClipStack::Element::kPath_Type:
211 canvas.drawPath(elem->getPath(), blueBorder);
212 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
213 break;
214 case SkClipStack::Element::kEmpty_Type:
215 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
216 break;
217 default:
218 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
219 break;
220 }
221 }
222
223 y += 30;
224 str.printf("Number of clips in stack: %i", total);
225 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
226 }
227
228 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
229 SkPath clipPath;
230 if (clipRegion.getBoundaryPath(&clipPath)) {
231 SkPaint redBorder;
232 redBorder.setColor(SK_ColorRED);
233 redBorder.setStyle(SkPaint::kStroke_Style);
234 canvas.drawPath(clipPath, redBorder);
235 }
236
237 canvas.flush();
238
239 SkString out;
240
241 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
242 // ... and other properties, to be able to debug th code easily
243
244 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
245 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
246 }
247
248 if (token->fType == kKeyword_TokenType) {
249 strcpy(gLastKeyword, token->fKeyword);
250 gLastOpKeyword = gReadOp;
251 } else {
252 strcpy(gLastKeyword, "");
253 }
254#endif
255
256 return ret;
257}
258
259
260
261typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
262
263map<std::string, PdfOperatorRenderer> gPdfOps;
264
265map<std::string, int> gRenderStats[kCount_PdfResult];
266
267char* gRenderStatsNames[kCount_PdfResult] = {
268 "Success",
269 "Partially implemented",
270 "Not yet implemented",
271 "Ignore Error",
272 "Error",
273 "Unsupported/Unknown"
274};
275
276static SkTypeface* SkTypefaceFromPdfFont(PdfFont* font) {
277 if (font == NULL) {
278 return SkTypeface::CreateFromName("Times New Roman", SkTypeface::kNormal);
279 }
280
281 PdfObject* fontObject = font->GetObject();
282
283 PdfObject* pBaseFont = NULL;
284 // TODO(edisonn): warning, PoDoFo has a bug in PdfFont constructor, does not call InitVars()
285 // for now fixed locally.
286 pBaseFont = fontObject->GetIndirectKey( "BaseFont" );
287 const char* pszBaseFontName = pBaseFont->GetName().GetName().c_str();
288
289#ifdef PDF_TRACE
290 std::string str;
291 fontObject->ToString(str);
292 printf("Base Font Name: %s\n", pszBaseFontName);
293 printf("Font Object Data: %s\n", str.c_str());
294#endif
295
296 SkTypeface* typeface = SkTypefaceFromPdfStandardFont(pszBaseFontName, font->IsBold(), font->IsItalic());
297
298 if (typeface != NULL) {
299 return typeface;
300 }
301
302 char name[1000];
303 // HACK
304 strncpy(name, pszBaseFontName, 1000);
305 char* comma = strstr(name, ",");
306 char* dash = strstr(name, "-");
307 if (comma) *comma = '\0';
308 if (dash) *dash = '\0';
309
310 typeface = SkTypeface::CreateFromName(
311 name,
312 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
313 (font->IsItalic() ? SkTypeface::kItalic : 0)));
314
315 if (typeface != NULL) {
316#ifdef PDF_TRACE
317 printf("HACKED FONT found %s\n", name);
318#endif
319 return typeface;
320 }
321
322#ifdef PDF_TRACE
323 printf("FONT_NOT_FOUND %s\n", pszBaseFontName);
324#endif
325
326 // TODO(edisonn): Report Warning, NYI
327 return SkTypeface::CreateFromName(
328 "Times New Roman",
329 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
330 (font->IsItalic() ? SkTypeface::kItalic : 0)));
331}
332
333PdfResult DrawText(PdfContext* pdfContext,
334 const SkPdfObject* str,
335 SkCanvas* canvas)
336{
337
338 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
339 if (skfont == NULL) {
340 skfont = SkPdfFont::Default();
341 }
342
343 SkUnencodedText binary(str);
344
345 SkDecodedText decoded;
346
347 if (skfont->encoding() == NULL) {
348 // TODO(edisonn): report warning
349 return kNYI_PdfResult;
350 }
351
352 skfont->encoding()->decodeText(binary, &decoded);
353
354 SkPaint paint;
355 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
356 // Or maybe just not call setTextSize at all?
357 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
358 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
359 }
360
361// if (fCurFont && fCurFont->GetFontScale() != 0) {
362// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
363// }
364
365 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
366
367 canvas->save();
368
369#if 1
370 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
371
372 SkPoint point1;
373 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
374
375 SkMatrix mirror;
376 mirror.setTranslate(0, -point1.y());
377 // TODO(edisonn): fix rotated text, and skewed too
378 mirror.postScale(SK_Scalar1, -SK_Scalar1);
379 // TODO(edisonn): post rotate, skew
380 mirror.postTranslate(0, point1.y());
381
382 matrix.postConcat(mirror);
383
384 canvas->setMatrix(matrix);
385
386 SkTraceMatrix(matrix, "mirrored");
387#endif
388
389 skfont->drawText(decoded, &paint, pdfContext, canvas, &pdfContext->fGraphicsState.fMatrixTm);
390 canvas->restore();
391
392 return kPartial_PdfResult;
393}
394
395// TODO(edisonn): create header files with declarations!
396PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
397PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
398PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
399PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
400
401// TODO(edisonn): deal with synonyms (/BPC == /BitsPerComponent), here or in GetKey?
402// Always pass long form in key, and have a map of long -> short key
403bool LongFromDictionary(const PdfMemDocument* pdfDoc,
404 const PdfDictionary& dict,
405 const char* key,
406 long* data) {
407 const PdfObject* value = resolveReferenceObject(pdfDoc,
408 dict.GetKey(PdfName(key)));
409
410 if (value == NULL || !value->IsNumber()) {
411 return false;
412 }
413 if (data == NULL) {
414 return true;
415 }
416
417 *data = value->GetNumber();
418 return true;
419}
420
421bool LongFromDictionary(const PdfMemDocument* pdfDoc,
422 const PdfDictionary& dict,
423 const char* key,
424 const char* abr,
425 long* data) {
426 if (LongFromDictionary(pdfDoc, dict, key, data)) return true;
427 if (abr == NULL || *abr == '\0') return false;
428 return LongFromDictionary(pdfDoc, dict, abr, data);
429}
430
431bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
432 const PdfDictionary& dict,
433 const char* key,
434 double* data) {
435 const PdfObject* value = resolveReferenceObject(pdfDoc,
436 dict.GetKey(PdfName(key)));
437
438 if (value == NULL || (!value->IsReal() && !value->IsNumber())) {
439 return false;
440 }
441 if (data == NULL) {
442 return true;
443 }
444
445 *data = value->GetReal();
446 return true;
447}
448
449bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
450 const PdfDictionary& dict,
451 const char* key,
452 const char* abr,
453 double* data) {
454 if (DoubleFromDictionary(pdfDoc, dict, key, data)) return true;
455 if (abr == NULL || *abr == '\0') return false;
456 return DoubleFromDictionary(pdfDoc, dict, abr, data);
457}
458
459
460bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
461 const PdfDictionary& dict,
462 const char* key,
463 bool* data) {
464 const PdfObject* value = resolveReferenceObject(pdfDoc,
465 dict.GetKey(PdfName(key)));
466
467 if (value == NULL || !value->IsBool()) {
468 return false;
469 }
470 if (data == NULL) {
471 return true;
472 }
473
474 *data = value->GetBool();
475 return true;
476}
477
478bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
479 const PdfDictionary& dict,
480 const char* key,
481 const char* abr,
482 bool* data) {
483 if (BoolFromDictionary(pdfDoc, dict, key, data)) return true;
484 if (abr == NULL || *abr == '\0') return false;
485 return BoolFromDictionary(pdfDoc, dict, abr, data);
486}
487
488bool NameFromDictionary(const PdfMemDocument* pdfDoc,
489 const PdfDictionary& dict,
490 const char* key,
491 std::string* data) {
492 const PdfObject* value = resolveReferenceObject(pdfDoc,
493 dict.GetKey(PdfName(key)),
494 true);
495 if (value == NULL || !value->IsName()) {
496 return false;
497 }
498 if (data == NULL) {
499 return true;
500 }
501
502 *data = value->GetName().GetName();
503 return true;
504}
505
506bool NameFromDictionary(const PdfMemDocument* pdfDoc,
507 const PdfDictionary& dict,
508 const char* key,
509 const char* abr,
510 std::string* data) {
511 if (NameFromDictionary(pdfDoc, dict, key, data)) return true;
512 if (abr == NULL || *abr == '\0') return false;
513 return NameFromDictionary(pdfDoc, dict, abr, data);
514}
515
516bool StringFromDictionary(const PdfMemDocument* pdfDoc,
517 const PdfDictionary& dict,
518 const char* key,
519 std::string* data) {
520 const PdfObject* value = resolveReferenceObject(pdfDoc,
521 dict.GetKey(PdfName(key)),
522 true);
523 if (value == NULL || (!value->IsString() && !value->IsHexString())) {
524 return false;
525 }
526 if (data == NULL) {
527 return true;
528 }
529
530 *data = value->GetString().GetString();
531 return true;
532}
533
534bool StringFromDictionary(const PdfMemDocument* pdfDoc,
535 const PdfDictionary& dict,
536 const char* key,
537 const char* abr,
538 std::string* data) {
539 if (StringFromDictionary(pdfDoc, dict, key, data)) return true;
540 if (abr == NULL || *abr == '\0') return false;
541 return StringFromDictionary(pdfDoc, dict, abr, data);
542}
543
544/*
545bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
546 const PdfDictionary& dict,
547 const char* key,
548 SkPdfArray** data) {
549 const PdfObject* value = resolveReferenceObject(pdfDoc,
550 dict.GetKey(PdfName(key)),
551 true);
552 if (value == NULL || !value->IsArray()) {
553 return false;
554 }
555 if (data == NULL) {
556 return true;
557 }
558
559 return mapArray(*pdfDoc, *value, data);
560}
561
562
563bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
564 const PdfDictionary& dict,
565 const char* key,
566 const char* abr,
567 SkPdfArray** data) {
568 if (ArrayFromDictionary(pdfDoc, dict, key, data)) return true;
569 if (abr == NULL || *abr == '\0') return false;
570 return ArrayFromDictionary(pdfDoc, dict, abr, data);
571}
572
573
574bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
575 const PdfDictionary& dict,
576 const char* key,
577 SkPdfDictionary** data) {
578 const PdfObject* value = resolveReferenceObject(pdfDoc,
579 dict.GetKey(PdfName(key)),
580 true);
581 if (value == NULL || !value->IsDictionary()) {
582 return false;
583 }
584 if (data == NULL) {
585 return true;
586 }
587
588 return mapDictionary(*pdfDoc, *value, data);
589}
590
591bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
592 const PdfDictionary& dict,
593 const char* key,
594 const char* abr,
595 SkPdfDictionary** data) {
596 if (DictionaryFromDictionary(pdfDoc, dict, key, data)) return true;
597 if (abr == NULL || *abr == '\0') return false;
598 return DictionaryFromDictionary(pdfDoc, dict, abr, data);
599}
600
601
602bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
603 const PdfDictionary& dict,
604 const char* key,
605 SkPdfObject** data) {
606 const PdfObject* value = resolveReferenceObject(pdfDoc,
607 dict.GetKey(PdfName(key)),
608 true);
609 if (value == NULL) {
610 return false;
611 }
612 if (data == NULL) {
613 return true;
614 }
615 return mapObject(*pdfDoc, *value, data);
616}
617
618bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
619 const PdfDictionary& dict,
620 const char* key,
621 const char* abr,
622 SkPdfObject** data) {
623 if (ObjectFromDictionary(pdfDoc, dict, key, data)) return true;
624 if (abr == NULL || *abr == '\0') return false;
625 return ObjectFromDictionary(pdfDoc, dict, abr, data);
626}
627
628bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
629 const PdfDictionary& dict,
630 const char* key,
631 SkPdfStream** data) {
632 const PdfObject* value = resolveReferenceObject(pdfDoc,
633 dict.GetKey(PdfName(key)),
634 true);
635 if (value == NULL) {
636 return false;
637 }
638 if (data == NULL) {
639 return true;
640 }
641 return mapStream(*pdfDoc, *value, data);
642}
643
644bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
645 const PdfDictionary& dict,
646 const char* key,
647 const char* abr,
648 SkPdfStream** data) {
649 if (StreamFromDictionary(pdfDoc, dict, key, data)) return true;
650 if (abr == NULL || *abr == '\0') return false;
651 return StreamFromDictionary(pdfDoc, dict, abr, data);
652}
653*/
654
655// TODO(edisonn): perf!!!
656
657static SkColorTable* getGrayColortable() {
658 static SkColorTable* grayColortable = NULL;
659 if (grayColortable == NULL) {
660 SkPMColor* colors = new SkPMColor[256];
661 for (int i = 0 ; i < 256; i++) {
662 colors[i] = SkPreMultiplyARGB(255, i, i, i);
663 }
664 grayColortable = new SkColorTable(colors, 256);
665 }
666 return grayColortable;
667}
668
669SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
670 int width, int height, int bytesPerLine,
671 int bpc, const std::string& colorSpace,
672 bool transparencyMask) {
673 SkBitmap bitmap;
674
675 int components = GetColorSpaceComponents(colorSpace);
676//#define MAX_COMPONENTS 10
677
678 int bitsPerLine = width * components * bpc;
679 // TODO(edisonn): assume start of lines are aligned at 32 bits?
680 // Is there a faster way to load the uncompressed stream into a bitmap?
681
682 // minimal support for now
683 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
684 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
685
686 for (int h = 0 ; h < height; h++) {
687 long i = width * (height - 1 - h);
688 for (int w = 0 ; w < width; w++) {
689 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
690 uncompressedStream[3 * w + 1],
691 uncompressedStream[3 * w + 2]);
692 i++;
693 }
694 uncompressedStream += bytesPerLine;
695 }
696
697 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
698 bitmap.setPixels(uncompressedStreamArgb);
699 }
700 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
701 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
702
703 for (int h = 0 ; h < height; h++) {
704 long i = width * (height - 1 - h);
705 for (int w = 0 ; w < width; w++) {
706 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
707 uncompressedStream[w];
708 i++;
709 }
710 uncompressedStream += bytesPerLine;
711 }
712
713 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
714 width, height);
715 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
716 }
717
718 // TODO(edisonn): Report Warning, NYI, or error
719 return bitmap;
720}
721
722bool transferImageStreamToARGB(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
723 int width, int bytesPerLine,
724 int bpc, const std::string& colorSpace,
725 SkColor** uncompressedStreamArgb,
726 pdf_long* uncompressedStreamLengthInBytesArgb) {
727 int components = GetColorSpaceComponents(colorSpace);
728//#define MAX_COMPONENTS 10
729
730 int bitsPerLine = width * components * bpc;
731 // TODO(edisonn): assume start of lines are aligned at 32 bits?
732 int height = uncompressedStreamLength / bytesPerLine;
733
734 // minimal support for now
735 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
736 *uncompressedStreamLengthInBytesArgb = width * height * 4;
737 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
738
739 for (int h = 0 ; h < height; h++) {
740 long i = width * (height - 1 - h);
741 for (int w = 0 ; w < width; w++) {
742 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[3 * w],
743 uncompressedStream[3 * w + 1],
744 uncompressedStream[3 * w + 2]);
745 i++;
746 }
747 uncompressedStream += bytesPerLine;
748 }
749 return true;
750 }
751
752 if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
753 *uncompressedStreamLengthInBytesArgb = width * height * 4;
754 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
755
756 for (int h = 0 ; h < height; h++) {
757 long i = width * (height - 1 - h);
758 for (int w = 0 ; w < width; w++) {
759 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[w],
760 uncompressedStream[w],
761 uncompressedStream[w]);
762 i++;
763 }
764 uncompressedStream += bytesPerLine;
765 }
766 return true;
767 }
768
769 return false;
770}
771
772// utils
773
774// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
775// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
776// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
777// skia format, through a table
778
779// this functions returns the image, it does not look at the smask.
780
781SkBitmap getImageFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* image, bool transparencyMask) {
782 if (image == NULL || !image->valid()) {
783 // TODO(edisonn): report warning to be used in testing.
784 return SkBitmap();
785 }
786
787 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
788// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
789// obj.GetDictionary().GetKey(PdfName("Filter")));
790// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
791// value = resolveReferenceObject(pdfContext->fPdfDoc,
792// &value->GetArray()[0]);
793// }
794// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
795// SkStream stream = SkStream::
796// SkImageDecoder::Factory()
797// }
798
799 long bpc = image->BitsPerComponent();
800 long width = image->Width();
801 long height = image->Height();
802 std::string colorSpace = "DeviceRGB";
803
804 // TODO(edisonn): color space can be an array too!
805 if (image->isColorSpaceAName()) {
806 colorSpace = image->getColorSpaceAsName();
807 }
808
809/*
810 bool imageMask = image->imageMask();
811
812 if (imageMask) {
813 if (bpc != 0 && bpc != 1) {
814 // TODO(edisonn): report warning to be used in testing.
815 return SkBitmap();
816 }
817 bpc = 1;
818 }
819*/
820
821 const PdfObject* obj = image->podofo();
822
823 char* uncompressedStream = NULL;
824 pdf_long uncompressedStreamLength = 0;
825
826 PdfResult ret = kPartial_PdfResult;
827 // TODO(edisonn): get rid of try/catch exceptions! We should not throw on user data!
828 try {
829 obj->GetStream()->GetFilteredCopy(&uncompressedStream, &uncompressedStreamLength);
830 } catch (PdfError& e) {
831 // TODO(edisonn): report warning to be used in testing.
832 return SkBitmap();
833 }
834
835 int bytesPerLine = uncompressedStreamLength / height;
836#ifdef PDF_TRACE
837 if (uncompressedStreamLength % height != 0) {
838 printf("Warning uncompressedStreamLength % height != 0 !!!\n");
839 }
840#endif
841
842 SkBitmap bitmap = transferImageStreamToBitmap(
843 (unsigned char*)uncompressedStream, uncompressedStreamLength,
844 width, height, bytesPerLine,
845 bpc, colorSpace,
846 transparencyMask);
847
848 free(uncompressedStream);
849
850 return bitmap;
851}
852
853SkBitmap getSmaskFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* obj) {
854 const PdfObject* sMask = resolveReferenceObject(&pdfContext->fPdfDoc->podofo(),
855 obj->podofo()->GetDictionary().GetKey(PdfName("SMask")));
856
857#ifdef PDF_TRACE
858 std::string str;
859 if (sMask) {
860 sMask->ToString(str);
861 printf("/SMask of /Subtype /Image: %s\n", str.c_str());
862 }
863#endif
864
865 if (sMask) {
866 SkPdfImageDictionary skxobjmask(&pdfContext->fPdfDoc->podofo(), sMask);
867 return getImageFromObject(pdfContext, &skxobjmask, true);
868 }
869
870 // TODO(edisonn): implement GS SMask. Default to empty right now.
871 return pdfContext->fGraphicsState.fSMask;
872}
873
874PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfImageDictionary* skpdfimage) {
875 if (skpdfimage == NULL || !skpdfimage->valid()) {
876 return kIgnoreError_PdfResult;
877 }
878
879 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
880 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
881
882 canvas->save();
883 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
884 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
885
886 if (sMask.empty()) {
887 canvas->drawBitmapRect(image, dst, NULL);
888 } else {
889 canvas->saveLayer(&dst, NULL);
890 canvas->drawBitmapRect(image, dst, NULL);
891 SkPaint xfer;
892 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
893 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
894 canvas->drawBitmapRect(sMask, dst, &xfer);
895 canvas->restore();
896 }
897
898 canvas->restore();
899
900 return kPartial_PdfResult;
901}
902
903bool SkMatrixFromDictionary(const PdfMemDocument* pdfDoc,
904 const PdfDictionary& dict,
905 const char* key,
906 SkMatrix** matrix) {
907 const PdfObject* value = resolveReferenceObject(pdfDoc,
908 dict.GetKey(PdfName(key)));
909
910 if (value == NULL || !value->IsArray()) {
911 return false;
912 }
913
914 if (value->GetArray().GetSize() != 6) {
915 return false;
916 }
917
918 double array[6];
919 for (int i = 0; i < 6; i++) {
920 const PdfObject* elem = resolveReferenceObject(pdfDoc, &value->GetArray()[i]);
921 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
922 return false;
923 }
924 array[i] = elem->GetReal();
925 }
926
927 *matrix = new SkMatrix();
928 **matrix = SkMatrixFromPdfMatrix(array);
929 return true;
930}
931
932bool SkMatrixFromDictionary(const PdfMemDocument* pdfDoc,
933 const PdfDictionary& dict,
934 const char* key,
935 const char* abr,
936 SkMatrix** data) {
937 if (SkMatrixFromDictionary(pdfDoc, dict, key, data)) return true;
938 if (abr == NULL || *abr == '\0') return false;
939 return SkMatrixFromDictionary(pdfDoc, dict, abr, data);
940
941}
942
943bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
944 const PdfDictionary& dict,
945 const char* key,
946 SkRect** rect) {
947 const PdfObject* value = resolveReferenceObject(pdfDoc,
948 dict.GetKey(PdfName(key)));
949
950 if (value == NULL || !value->IsArray()) {
951 return false;
952 }
953
954 if (value->GetArray().GetSize() != 4) {
955 return false;
956 }
957
958 double array[4];
959 for (int i = 0; i < 4; i++) {
960 const PdfObject* elem = resolveReferenceObject(pdfDoc, &value->GetArray()[i]);
961 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
962 return false;
963 }
964 array[i] = elem->GetReal();
965 }
966
967 *rect = new SkRect();
968 **rect = SkRect::MakeLTRB(SkDoubleToScalar(array[0]),
969 SkDoubleToScalar(array[1]),
970 SkDoubleToScalar(array[2]),
971 SkDoubleToScalar(array[3]));
972 return true;
973}
974
975bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
976 const PdfDictionary& dict,
977 const char* key,
978 const char* abr,
979 SkRect** data) {
980 if (SkRectFromDictionary(pdfDoc, dict, key, data)) return true;
981 if (abr == NULL || *abr == '\0') return false;
982 return SkRectFromDictionary(pdfDoc, dict, abr, data);
983
984}
985
986
987SkPdfObject* get(const SkPdfObject* obj, const char* key, const char* abr = "") {
988 SkPdfObject* ret = NULL;
989 if (obj == NULL) return NULL;
990 const SkPdfDictionary* dict = obj->asDictionary();
991 if (dict == NULL) return NULL;
992 if (!dict->podofo()->IsDictionary()) return NULL;
993 ObjectFromDictionary(dict->doc(), dict->podofo()->GetDictionary(), key, abr, &ret);
994 return ret;
995}
996
997PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
998 if (!skobj || !skobj->podofo() || !skobj->podofo()->HasStream() || skobj->podofo()->GetStream() == NULL || skobj->podofo()->GetStream()->GetLength() == 0) {
999 return kOK_PdfResult;
1000 }
1001
1002 PdfOp_q(pdfContext, canvas, NULL);
1003 canvas->save();
1004
1005
1006 if (skobj->Resources()) {
1007 pdfContext->fGraphicsState.fResources = skobj->Resources();
1008 }
1009
1010 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
1011
1012 if (skobj->Matrix()) {
1013 pdfContext->fGraphicsState.fMatrix.preConcat(*skobj->Matrix());
1014 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1015 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1016 // TODO(edisonn) reset matrixTm and matricTlm also?
1017 }
1018
1019 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1020
1021 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1022
1023 if (skobj->BBox()) {
1024 canvas->clipRect(*skobj->BBox(), SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
1025 }
1026
1027 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
1028 // For this PdfContentsTokenizer needs to be extended.
1029
1030 PdfResult ret = kPartial_PdfResult;
1031 SkPdfTokenizer tokenizer(skobj);
1032 PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1033 looper.loop();
1034
1035 // TODO(edisonn): should we restore the variable stack at the same state?
1036 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1037 canvas->restore();
1038 PdfOp_Q(pdfContext, canvas, NULL);
1039 return ret;
1040}
1041
1042PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject& obj) {
1043 return kNYI_PdfResult;
1044}
1045
1046PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
1047 if (!skobj || !skobj->podofo() || !skobj->podofo()->HasStream() || skobj->podofo()->GetStream() == NULL || skobj->podofo()->GetStream()->GetLength() == 0) {
1048 return kOK_PdfResult;
1049 }
1050
1051 PdfOp_q(pdfContext, canvas, NULL);
1052 canvas->save();
1053
1054 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1055 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
1056
1057 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm;
1058 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1059
1060 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1061
1062 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1063
1064 SkRect rm = bBox;
1065 pdfContext->fGraphicsState.fMatrix.mapRect(&rm);
1066
1067 SkTraceRect(rm, "bbox mapped");
1068
1069 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
1070
1071 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
1072 // For this PdfContentsTokenizer needs to be extended.
1073
1074 PdfResult ret = kPartial_PdfResult;
1075 SkPdfTokenizer tokenizer(skobj);
1076 PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1077 looper.loop();
1078
1079 // TODO(edisonn): should we restore the variable stack at the same state?
1080 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1081 canvas->restore();
1082 PdfOp_Q(pdfContext, canvas, NULL);
1083 return ret;
1084}
1085
1086
1087// TODO(edisonn): faster, have the property on the SkPdfObject itself?
1088std::set<const PdfObject*> gInRendering;
1089
1090class CheckRecursiveRendering {
1091 const PdfObject& fObj;
1092public:
1093 CheckRecursiveRendering(const PdfObject& obj) : fObj(obj) {
1094 gInRendering.insert(&obj);
1095 }
1096
1097 ~CheckRecursiveRendering() {
1098 //SkASSERT(fObj.fInRendering);
1099 gInRendering.erase(&fObj);
1100 }
1101
1102 static bool IsInRendering(const PdfObject& obj) {
1103 return gInRendering.find(&obj) != gInRendering.end();
1104 }
1105};
1106
1107PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject& obj) {
1108 if (CheckRecursiveRendering::IsInRendering(*obj.podofo())) {
1109 // Oops, corrupt PDF!
1110 return kIgnoreError_PdfResult;
1111 }
1112
1113 CheckRecursiveRendering checkRecursion(*obj.podofo());
1114
1115 // TODO(edisonn): check type
1116 SkPdfXObjectDictionary* skobj = NULL;
1117 if (!mapXObjectDictionary(obj, &skobj)) return kIgnoreError_PdfResult;
1118
1119 if (!skobj || !skobj->valid()) return kIgnoreError_PdfResult;
1120
1121 PdfResult ret = kIgnoreError_PdfResult;
1122 switch (skobj->getType())
1123 {
1124 case kImageDictionary_SkPdfObjectType:
1125 ret = doXObject_Image(pdfContext, canvas, skobj->asImageDictionary());
1126 break;
1127 case kType1FormDictionary_SkPdfObjectType:
1128 ret = doXObject_Form(pdfContext, canvas, skobj->asType1FormDictionary());
1129 break;
1130 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
1131 //return doXObject_PS(skxobj.asPS());
1132 }
1133
1134 delete skobj;
1135 return ret;
1136}
1137
1138PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1139 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1140 canvas->save();
1141 return kOK_PdfResult;
1142}
1143
1144PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1145 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1146 pdfContext->fStateStack.pop();
1147 canvas->restore();
1148 return kOK_PdfResult;
1149}
1150
1151PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1152 double array[6];
1153 for (int i = 0 ; i < 6 ; i++) {
1154 array[5 - i] = pdfContext->fObjectStack.top()->asNumber()->value();
1155 pdfContext->fObjectStack.pop();
1156 }
1157
1158 // a b
1159 // c d
1160 // e f
1161
1162 // 0 1
1163 // 2 3
1164 // 4 5
1165
1166 // sx ky
1167 // kx sy
1168 // tx ty
1169 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1170
1171 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1172
1173#ifdef PDF_TRACE
1174 printf("cm ");
1175 for (int i = 0 ; i < 6 ; i++) {
1176 printf("%f ", array[i]);
1177 }
1178 printf("\n");
1179 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix);
1180#endif
1181
1182 return kOK_PdfResult;
1183}
1184
1185//leading TL Set the text leading, Tl
1186//, to leading, which is a number expressed in unscaled text
1187//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
1188PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1189 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1190
1191 pdfContext->fGraphicsState.fTextLeading = ty;
1192
1193 return kOK_PdfResult;
1194}
1195
1196PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1197 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1198 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1199
1200 double array[6] = {1, 0, 0, 1, tx, ty};
1201 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1202
1203 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1204 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1205
1206 return kPartial_PdfResult;
1207}
1208
1209PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1210 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1211 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1212
1213 // TODO(edisonn): Create factory methods or constructors so podofo is hidden
1214 PdfObject _ty(PdfVariant(-ty));
1215 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &_ty));
1216
1217 PdfOp_TL(pdfContext, canvas, looper);
1218
1219 PdfObject vtx(PdfVariant(-(-tx))); // TODO(edisonn): Hmm, the compiler thinks I have here a function pointer if we use (tx), but not -(-tx)
1220 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &vtx));
1221
1222 PdfObject vty(PdfVariant(-(-ty)));
1223 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &vty));
1224
1225 return PdfOp_Td(pdfContext, canvas, looper);
1226}
1227
1228PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1229 double f = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1230 double e = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1231 double d = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1232 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1233 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1234 double a = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1235
1236 double array[6];
1237 array[0] = a;
1238 array[1] = b;
1239 array[2] = c;
1240 array[3] = d;
1241 array[4] = e;
1242 array[5] = f;
1243
1244 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1245 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
1246
1247 // TODO(edisonn): Text positioning.
1248 pdfContext->fGraphicsState.fMatrixTm = matrix;
1249 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1250
1251 return kPartial_PdfResult;
1252}
1253
1254//— T* Move to the start of the next line. This operator has the same effect as the code
1255//0 Tl Td
1256//where Tl is the current leading parameter in the text state
1257PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1258 PdfObject zero(PdfVariant(0.0));
1259 PdfObject tl(PdfVariant(-(-pdfContext->fGraphicsState.fTextLeading)));
1260
1261 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &zero));
1262 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &tl));
1263 return PdfOp_Td(pdfContext, canvas, looper);
1264}
1265
1266PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1267 if (pdfContext->fGraphicsState.fPathClosed) {
1268 pdfContext->fGraphicsState.fPath.reset();
1269 pdfContext->fGraphicsState.fPathClosed = false;
1270 }
1271
1272 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1273 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1274
1275 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1276 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1277
1278 return kOK_PdfResult;
1279}
1280
1281PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1282 if (pdfContext->fGraphicsState.fPathClosed) {
1283 pdfContext->fGraphicsState.fPath.reset();
1284 pdfContext->fGraphicsState.fPathClosed = false;
1285 }
1286
1287 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1288 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1289
1290 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1291 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1292
1293 return kOK_PdfResult;
1294}
1295
1296PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1297 if (pdfContext->fGraphicsState.fPathClosed) {
1298 pdfContext->fGraphicsState.fPath.reset();
1299 pdfContext->fGraphicsState.fPathClosed = false;
1300 }
1301
1302 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1303 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1304 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1305 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1306 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1307 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1308
1309 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1310 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1311 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1312
1313 pdfContext->fGraphicsState.fCurPosX = x3;
1314 pdfContext->fGraphicsState.fCurPosY = y3;
1315
1316 return kOK_PdfResult;
1317}
1318
1319PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1320 if (pdfContext->fGraphicsState.fPathClosed) {
1321 pdfContext->fGraphicsState.fPath.reset();
1322 pdfContext->fGraphicsState.fPathClosed = false;
1323 }
1324
1325 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1326 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1327 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1328 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1329 double y1 = pdfContext->fGraphicsState.fCurPosY;
1330 double x1 = pdfContext->fGraphicsState.fCurPosX;
1331
1332 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1333 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1334 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1335
1336 pdfContext->fGraphicsState.fCurPosX = x3;
1337 pdfContext->fGraphicsState.fCurPosY = y3;
1338
1339 return kOK_PdfResult;
1340}
1341
1342PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1343 if (pdfContext->fGraphicsState.fPathClosed) {
1344 pdfContext->fGraphicsState.fPath.reset();
1345 pdfContext->fGraphicsState.fPathClosed = false;
1346 }
1347
1348 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1349 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1350 double y2 = pdfContext->fGraphicsState.fCurPosY;
1351 double x2 = pdfContext->fGraphicsState.fCurPosX;
1352 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1353 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1354
1355 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1356 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1357 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1358
1359 pdfContext->fGraphicsState.fCurPosX = x3;
1360 pdfContext->fGraphicsState.fCurPosY = y3;
1361
1362 return kOK_PdfResult;
1363}
1364
1365PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1366 if (pdfContext->fGraphicsState.fPathClosed) {
1367 pdfContext->fGraphicsState.fPath.reset();
1368 pdfContext->fGraphicsState.fPathClosed = false;
1369 }
1370
1371 double height = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1372 double width = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1373 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1374 double x = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1375
1376 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1377 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1378
1379 pdfContext->fGraphicsState.fCurPosX = x;
1380 pdfContext->fGraphicsState.fCurPosY = y + height;
1381
1382 return kOK_PdfResult;
1383}
1384
1385PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1386 pdfContext->fGraphicsState.fPath.close();
1387 return kOK_PdfResult;
1388}
1389
1390PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
1391 SkPath path = pdfContext->fGraphicsState.fPath;
1392
1393 if (close) {
1394 path.close();
1395 }
1396
1397 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1398
1399 SkPaint paint;
1400
1401 SkPoint line[2];
1402 if (fill && !stroke && path.isLine(line)) {
1403 paint.setStyle(SkPaint::kStroke_Style);
1404
1405 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1406 paint.setStrokeWidth(SkDoubleToScalar(0));
1407
1408 canvas->drawPath(path, paint);
1409 } else {
1410 if (fill) {
1411 paint.setStyle(SkPaint::kFill_Style);
1412 if (evenOdd) {
1413 path.setFillType(SkPath::kEvenOdd_FillType);
1414 }
1415
1416 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1417
1418 canvas->drawPath(path, paint);
1419 }
1420
1421 if (stroke) {
1422 paint.setStyle(SkPaint::kStroke_Style);
1423
1424 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1425
1426 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1427 canvas->drawPath(path, paint);
1428 }
1429 }
1430
1431 pdfContext->fGraphicsState.fPath.reset();
1432 // todo zoom ... other stuff ?
1433
1434 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1435#ifndef PDF_DEBUG_NO_CLIPING
1436 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1437#endif
1438 }
1439
1440 //pdfContext->fGraphicsState.fClipPath.reset();
1441 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1442
1443 return kPartial_PdfResult;
1444
1445}
1446
1447PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1448 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1449}
1450
1451PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1452 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1453}
1454
1455PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1456 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1457}
1458
1459PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1460 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1461}
1462
1463PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1464 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1465}
1466
1467PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1468 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1469}
1470
1471PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1472 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1473}
1474
1475PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1476 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1477}
1478
1479PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1480 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1481}
1482
1483PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1484 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1485 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1486#ifndef PDF_DEBUG_NO_CLIPING
1487 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1488#endif
1489 }
1490
1491 //pdfContext->fGraphicsState.fClipPath.reset();
1492 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1493
1494 pdfContext->fGraphicsState.fPathClosed = true;
1495
1496 return kOK_PdfResult;
1497}
1498
1499PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1500 pdfContext->fGraphicsState.fTextBlock = true;
1501 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1502 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1503
1504 return kPartial_PdfResult;
1505}
1506
1507PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1508 if (!pdfContext->fGraphicsState.fTextBlock) {
1509 return kIgnoreError_PdfResult;
1510 }
1511 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1512 return kPartial_PdfResult;
1513}
1514
1515//font size Tf Set the text font, Tf
1516//, to font and the text font size, Tfs, to size. font is the name of a
1517//font resource in the Fontsubdictionary of the current resource dictionary; size is
1518//a number representing a scale factor. There is no initial value for either font or
1519//size; they must be specified explicitly using Tf before any text is shown.
1520PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1521 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1522 std::string fontName = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1523
1524#ifdef PDF_TRACE
1525 printf("font name: %s\n", fontName.c_str());
1526 std::string str;
1527 pdfContext->fGraphicsState.fResources->podofo()->ToString(str);
1528 printf("Print Tf Resources: %s\n", str.c_str());
1529 pdfContext->fGraphicsState.fResources->Font()->podofo()->ToString(str);
1530 printf("Print Tf Resources/Font: %s\n", str.c_str());
1531#endif
1532
1533 SkPdfFontDictionary* fd = NULL;
1534 if (pdfContext->fGraphicsState.fResources->Font()) {
1535 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font()->get(fontName.c_str());
1536 mapFontDictionary(*objFont, &fd);
1537
1538#ifdef PDF_TRACE
1539 objFont->podofo()->ToString(str);
1540 printf("Print Font loaded: %s\n", str.c_str());
1541 fd->podofo()->ToString(str);
1542 printf("Print Font loaded and resolved and upgraded: %s\n", str.c_str());
1543#endif
1544
1545 }
1546
1547 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(fd);
1548
1549 if (skfont) {
1550 pdfContext->fGraphicsState.fSkFont = skfont;
1551 }
1552
1553 return kPartial_PdfResult;
1554}
1555
1556PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1557 if (!pdfContext->fGraphicsState.fTextBlock) {
1558 // TODO(edisonn): try to recover and draw it any way?
1559 return kIgnoreError_PdfResult;
1560 }
1561
1562 PdfResult ret = DrawText(pdfContext,
1563 pdfContext->fObjectStack.top(),
1564 canvas);
1565 pdfContext->fObjectStack.pop();
1566
1567 return ret;
1568}
1569
1570PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1571 if (!pdfContext->fGraphicsState.fTextBlock) {
1572 // TODO(edisonn): try to recover and draw it any way?
1573 return kIgnoreError_PdfResult;
1574 }
1575
1576 PdfOp_T_star(pdfContext, canvas, looper);
1577 // Do not pop, and push, just transfer the param to Tj
1578 return PdfOp_Tj(pdfContext, canvas, looper);
1579}
1580
1581PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1582 if (!pdfContext->fGraphicsState.fTextBlock) {
1583 // TODO(edisonn): try to recover and draw it any way?
1584 return kIgnoreError_PdfResult;
1585 }
1586
1587 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1588 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1589 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1590
1591 pdfContext->fObjectStack.push(aw);
1592 PdfOp_Tw(pdfContext, canvas, looper);
1593
1594 pdfContext->fObjectStack.push(ac);
1595 PdfOp_Tc(pdfContext, canvas, looper);
1596
1597 pdfContext->fObjectStack.push(str);
1598 PdfOp_quote(pdfContext, canvas, looper);
1599
1600 return kPartial_PdfResult;
1601}
1602
1603PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1604 if (!pdfContext->fGraphicsState.fTextBlock) {
1605 // TODO(edisonn): try to recover and draw it any way?
1606 return kIgnoreError_PdfResult;
1607 }
1608
1609 SkPdfArray* array = pdfContext->fObjectStack.top()->asArray();
1610 pdfContext->fObjectStack.pop();
1611
1612 for( int i=0; i<static_cast<int>(array->size()); i++ )
1613 {
1614 if( (*array)[i]->asString()) {
1615 SkPdfObject* obj = (*array)[i];
1616 DrawText(pdfContext,
1617 obj,
1618 canvas);
1619 } else if ((*array)[i]->asInteger() || (*array)[i]->asNumber()) {
1620 double dx = (*array)[i]->asNumber()->value();
1621 SkMatrix matrix;
1622 matrix.setAll(SkDoubleToScalar(1),
1623 SkDoubleToScalar(0),
1624 // TODO(edisonn): use writing mode, vertical/horizontal.
1625 SkDoubleToScalar(-dx), // amount is substracted!!!
1626 SkDoubleToScalar(0),
1627 SkDoubleToScalar(1),
1628 SkDoubleToScalar(0),
1629 SkDoubleToScalar(0),
1630 SkDoubleToScalar(0),
1631 SkDoubleToScalar(1));
1632
1633 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1634 }
1635 }
1636 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1637}
1638
1639PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1640 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1641 return kOK_PdfResult;
1642}
1643
1644PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1645 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1646}
1647
1648PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1649 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1650}
1651
1652PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1653 double c[4];
1654 pdf_int64 v[4];
1655
1656 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1657
1658 bool doubles = true;
1659 if (colorOperator->fColorSpace == "Indexed") {
1660 doubles = false;
1661 }
1662
1663#ifdef PDF_TRACE
1664 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.c_str(), n);
1665#endif
1666
1667 for (int i = n - 1; i >= 0 ; i--) {
1668 if (doubles) {
1669 c[i] = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1670 } else {
1671 v[i] = pdfContext->fObjectStack.top()->asInteger()->value(); pdfContext->fObjectStack.pop();
1672 }
1673 }
1674
1675 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
1676 if (colorOperator->fColorSpace == "DeviceRGB") {
1677 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
1678 }
1679 return kPartial_PdfResult;
1680}
1681
1682PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1683 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1684}
1685
1686PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1687 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1688}
1689
1690PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1691 PdfString name;
1692
1693 if (pdfContext->fObjectStack.top()->asName()) {
1694 pdfContext->fObjectStack.pop();
1695 }
1696
1697 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1698 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1699
1700 return kPartial_PdfResult;
1701}
1702
1703PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1704 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1705}
1706
1707PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1708 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1709}
1710
1711PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1712 double gray = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1713 return kNYI_PdfResult;
1714}
1715
1716PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1717 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1718}
1719
1720PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1721 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1722}
1723
1724PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1725 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1726 double g = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1727 double r = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1728
1729 colorOperator->fColorSpace = "DeviceRGB";
1730 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
1731 return kOK_PdfResult;
1732}
1733
1734PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1735 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1736}
1737
1738PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1739 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1740}
1741
1742PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1743 // TODO(edisonn): spec has some rules about overprint, implement them.
1744 double k = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1745 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1746 double m = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1747 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1748
1749 colorOperator->fColorSpace = "DeviceCMYK";
1750 // TODO(edisonn): Set color.
1751 return kNYI_PdfResult;
1752}
1753
1754PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1755 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1756}
1757
1758PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1759 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1760}
1761
1762PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1763 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1764 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1765
1766 return kOK_PdfResult;
1767}
1768
1769PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1770 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1771
1772#ifdef PDF_TRACE
1773 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
1774 printf("CLIP IS RECT\n");
1775 }
1776#endif
1777
1778 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
1779 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1780 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1781
1782 return kPartial_PdfResult;
1783}
1784
1785PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1786 *looper = new PdfCompatibilitySectionLooper();
1787 return kOK_PdfResult;
1788}
1789
1790PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1791#ifdef ASSERT_BAD_PDF_OPS
1792 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1793 // have the assert when testing good pdfs.
1794#endif
1795 return kIgnoreError_PdfResult;
1796}
1797
1798PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1799 *looper = new PdfInlineImageLooper();
1800 return kOK_PdfResult;
1801}
1802
1803PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1804#ifdef ASSERT_BAD_PDF_OPS
1805 SkASSERT(false); // must be processed in inline image looper, but let's
1806 // have the assert when testing good pdfs.
1807#endif
1808 return kIgnoreError_PdfResult;
1809}
1810
1811PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1812#ifdef ASSERT_BAD_PDF_OPS
1813 SkASSERT(false); // must be processed in inline image looper, but let's
1814 // have the assert when testing good pdfs.
1815#endif
1816 return kIgnoreError_PdfResult;
1817}
1818
1819//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1820PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1821 double lineWidth = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1822 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1823
1824 return kOK_PdfResult;
1825}
1826
1827//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
1828PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1829 pdfContext->fObjectStack.pop();
1830 //double lineCap = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1831
1832 return kNYI_PdfResult;
1833}
1834
1835//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
1836PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1837 pdfContext->fObjectStack.pop();
1838 //double lineJoin = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1839
1840 return kNYI_PdfResult;
1841}
1842
1843//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
1844PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1845 pdfContext->fObjectStack.pop();
1846 //double miterLimit = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1847
1848 return kNYI_PdfResult;
1849}
1850
1851//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1852//page 155).
1853PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1854 pdfContext->fObjectStack.pop();
1855 pdfContext->fObjectStack.pop();
1856
1857 return kNYI_PdfResult;
1858}
1859
1860//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
1861PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1862 pdfContext->fObjectStack.pop();
1863
1864 return kNYI_PdfResult;
1865}
1866
1867//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1868//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1869//fies the output device’s default flatness tolerance.
1870PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1871 pdfContext->fObjectStack.pop();
1872
1873 return kNYI_PdfResult;
1874}
1875
1876//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
1877//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
1878PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1879 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1880
1881#ifdef PDF_TRACE
1882 std::string str;
1883#endif
1884
1885 //Next, get the ExtGState Dictionary from the Resource Dictionary:
1886 const SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState();
1887
1888 if (extGStateDictionary == NULL) {
1889#ifdef PDF_TRACE
1890 printf("ExtGState is NULL!\n");
1891#endif
1892 return kIgnoreError_PdfResult;
1893 }
1894
1895 SkPdfObject* value = extGStateDictionary->get(name.c_str());
1896
1897#ifdef PDF_TRACE
1898// value->ToString(str);
1899// printf("gs object value: %s\n", str.c_str());
1900#endif
1901
1902 SkPdfGraphicsStateDictionary* gs = NULL;
1903 mapGraphicsStateDictionary(*value, &gs);
1904
1905 // TODO(edisonn): now load all those properties in graphic state.
1906 if (gs == NULL) {
1907 return kIgnoreError_PdfResult;
1908 }
1909
1910 if (gs->has_CA()) {
1911 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA();
1912 }
1913
1914 if (gs->has_ca()) {
1915 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca();
1916 }
1917
1918 if (gs->has_LW()) {
1919 pdfContext->fGraphicsState.fLineWidth = gs->LW();
1920 }
1921
1922
1923
1924 return kNYI_PdfResult;
1925}
1926
1927//charSpace Tc Set the character spacing, Tc
1928//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
1929//Initial value: 0.
1930PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1931 double charSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1932 pdfContext->fGraphicsState.fCharSpace = charSpace;
1933
1934 return kOK_PdfResult;
1935}
1936
1937//wordSpace Tw Set the word spacing, T
1938//w
1939//, to wordSpace, which is a number expressed in unscaled
1940//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
1941//value: 0.
1942PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1943 double wordSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1944 pdfContext->fGraphicsState.fWordSpace = wordSpace;
1945
1946 return kOK_PdfResult;
1947}
1948
1949//scale Tz Set the horizontal scaling, Th
1950//, to (scale ˜ 100). scale is a number specifying the
1951//percentage of the normal width. Initial value: 100 (normal width).
1952PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1953 double scale = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1954
1955 return kNYI_PdfResult;
1956}
1957
1958//render Tr Set the text rendering mode, T
1959//mode, to render, which is an integer. Initial value: 0.
1960PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1961 double render = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1962
1963 return kNYI_PdfResult;
1964}
1965//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
1966//units. Initial value: 0.
1967PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1968 double rise = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1969
1970 return kNYI_PdfResult;
1971}
1972
1973//wx wy d0
1974PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1975 pdfContext->fObjectStack.pop();
1976 pdfContext->fObjectStack.pop();
1977
1978 return kNYI_PdfResult;
1979}
1980
1981//wx wy llx lly urx ury d1
1982PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1983 pdfContext->fObjectStack.pop();
1984 pdfContext->fObjectStack.pop();
1985 pdfContext->fObjectStack.pop();
1986 pdfContext->fObjectStack.pop();
1987 pdfContext->fObjectStack.pop();
1988 pdfContext->fObjectStack.pop();
1989
1990 return kNYI_PdfResult;
1991}
1992
1993//name sh
1994PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1995 pdfContext->fObjectStack.pop();
1996
1997 return kNYI_PdfResult;
1998}
1999
2000//name Do
2001PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2002 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
2003
2004 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject();
2005
2006 if (xObject == NULL) {
2007#ifdef PDF_TRACE
2008 printf("XObject is NULL!\n");
2009#endif
2010 return kIgnoreError_PdfResult;
2011 }
2012
2013 SkPdfObject* value = xObject->get(name.c_str());
2014
2015#ifdef PDF_TRACE
2016// value->ToString(str);
2017// printf("Do object value: %s\n", str.c_str());
2018#endif
2019
2020 return doXObject(pdfContext, canvas, *value);
2021}
2022
2023//tag MP Designate a marked-content point. tag is a name object indicating the role or
2024//significance of the point.
2025PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2026 pdfContext->fObjectStack.pop();
2027
2028 return kNYI_PdfResult;
2029}
2030
2031//tag properties DP Designate a marked-content point with an associated property list. tag is a
2032//name object indicating the role or significance of the point; properties is
2033//either an inline dictionary containing the property list or a name object
2034//associated with it in the Properties subdictionary of the current resource
2035//dictionary (see Section 9.5.1, “Property Lists”).
2036PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2037 pdfContext->fObjectStack.pop();
2038 pdfContext->fObjectStack.pop();
2039
2040 return kNYI_PdfResult;
2041}
2042
2043//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2044//tag is a name object indicating the role or significance of the sequence.
2045PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2046 pdfContext->fObjectStack.pop();
2047
2048 return kNYI_PdfResult;
2049}
2050
2051//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2052//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2053//property list or a name object associated with it in the Properties subdictionary of the current resource dictionary (see Section 9.5.1, “Property Lists”).
2054PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2055 pdfContext->fObjectStack.pop();
2056 pdfContext->fObjectStack.pop();
2057
2058 return kNYI_PdfResult;
2059}
2060
2061//— EMC End a marked-content sequence begun by a BMC or BDC operator.
2062PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2063 return kNYI_PdfResult;
2064}
2065
2066void initPdfOperatorRenderes() {
2067 static bool gInitialized = false;
2068 if (gInitialized) {
2069 return;
2070 }
2071
2072 gPdfOps["q"] = PdfOp_q;
2073 gPdfOps["Q"] = PdfOp_Q;
2074 gPdfOps["cm"] = PdfOp_cm;
2075
2076 gPdfOps["TD"] = PdfOp_TD;
2077 gPdfOps["Td"] = PdfOp_Td;
2078 gPdfOps["Tm"] = PdfOp_Tm;
2079 gPdfOps["T*"] = PdfOp_T_star;
2080
2081 gPdfOps["m"] = PdfOp_m;
2082 gPdfOps["l"] = PdfOp_l;
2083 gPdfOps["c"] = PdfOp_c;
2084 gPdfOps["v"] = PdfOp_v;
2085 gPdfOps["y"] = PdfOp_y;
2086 gPdfOps["h"] = PdfOp_h;
2087 gPdfOps["re"] = PdfOp_re;
2088
2089 gPdfOps["S"] = PdfOp_S;
2090 gPdfOps["s"] = PdfOp_s;
2091 gPdfOps["f"] = PdfOp_f;
2092 gPdfOps["F"] = PdfOp_F;
2093 gPdfOps["f*"] = PdfOp_f_star;
2094 gPdfOps["B"] = PdfOp_B;
2095 gPdfOps["B*"] = PdfOp_B_star;
2096 gPdfOps["b"] = PdfOp_b;
2097 gPdfOps["b*"] = PdfOp_b_star;
2098 gPdfOps["n"] = PdfOp_n;
2099
2100 gPdfOps["BT"] = PdfOp_BT;
2101 gPdfOps["ET"] = PdfOp_ET;
2102
2103 gPdfOps["Tj"] = PdfOp_Tj;
2104 gPdfOps["'"] = PdfOp_quote;
2105 gPdfOps["\""] = PdfOp_doublequote;
2106 gPdfOps["TJ"] = PdfOp_TJ;
2107
2108 gPdfOps["CS"] = PdfOp_CS;
2109 gPdfOps["cs"] = PdfOp_cs;
2110 gPdfOps["SC"] = PdfOp_SC;
2111 gPdfOps["SCN"] = PdfOp_SCN;
2112 gPdfOps["sc"] = PdfOp_sc;
2113 gPdfOps["scn"] = PdfOp_scn;
2114 gPdfOps["G"] = PdfOp_G;
2115 gPdfOps["g"] = PdfOp_g;
2116 gPdfOps["RG"] = PdfOp_RG;
2117 gPdfOps["rg"] = PdfOp_rg;
2118 gPdfOps["K"] = PdfOp_K;
2119 gPdfOps["k"] = PdfOp_k;
2120
2121 gPdfOps["W"] = PdfOp_W;
2122 gPdfOps["W*"] = PdfOp_W_star;
2123
2124 gPdfOps["BX"] = PdfOp_BX;
2125 gPdfOps["EX"] = PdfOp_EX;
2126
2127 gPdfOps["BI"] = PdfOp_BI;
2128 gPdfOps["ID"] = PdfOp_ID;
2129 gPdfOps["EI"] = PdfOp_EI;
2130
2131 gPdfOps["w"] = PdfOp_w;
2132 gPdfOps["J"] = PdfOp_J;
2133 gPdfOps["j"] = PdfOp_j;
2134 gPdfOps["M"] = PdfOp_M;
2135 gPdfOps["d"] = PdfOp_d;
2136 gPdfOps["ri"] = PdfOp_ri;
2137 gPdfOps["i"] = PdfOp_i;
2138 gPdfOps["gs"] = PdfOp_gs;
2139
2140 gPdfOps["Tc"] = PdfOp_Tc;
2141 gPdfOps["Tw"] = PdfOp_Tw;
2142 gPdfOps["Tz"] = PdfOp_Tz;
2143 gPdfOps["TL"] = PdfOp_TL;
2144 gPdfOps["Tf"] = PdfOp_Tf;
2145 gPdfOps["Tr"] = PdfOp_Tr;
2146 gPdfOps["Ts"] = PdfOp_Ts;
2147
2148 gPdfOps["d0"] = PdfOp_d0;
2149 gPdfOps["d1"] = PdfOp_d1;
2150
2151 gPdfOps["sh"] = PdfOp_sh;
2152
2153 gPdfOps["Do"] = PdfOp_Do;
2154
2155 gPdfOps["MP"] = PdfOp_MP;
2156 gPdfOps["DP"] = PdfOp_DP;
2157 gPdfOps["BMC"] = PdfOp_BMC;
2158 gPdfOps["BDC"] = PdfOp_BDC;
2159 gPdfOps["EMC"] = PdfOp_EMC;
2160
2161 gInitialized = true;
2162}
2163
2164class InitPdfOps {
2165public:
2166 InitPdfOps() {
2167 initPdfOperatorRenderes();
2168 }
2169};
2170
2171InitPdfOps gInitPdfOps;
2172
2173void reportPdfRenderStats() {
2174 std::map<std::string, int>::iterator iter;
2175
2176 for (int i = 0 ; i < kCount_PdfResult; i++) {
2177 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++iter) {
2178 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_str(), iter->second);
2179 }
2180 }
2181}
2182
2183PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
2184 if (token.fType == kKeyword_TokenType)
2185 {
2186 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
2187 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[token.fKeyword];
2188 if (pdfOperatorRenderer) {
2189 // caller, main work is done by pdfOperatorRenderer(...)
2190 PdfTokenLooper* childLooper = NULL;
2191 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)][token.fKeyword]++;
2192
2193 if (childLooper) {
2194 childLooper->setUp(this);
2195 childLooper->loop();
2196 delete childLooper;
2197 }
2198 } else {
2199 gRenderStats[kUnsupported_PdfResult][token.fKeyword]++;
2200 }
2201 }
2202 else if (token.fType == kObject_TokenType)
2203 {
2204 fPdfContext->fObjectStack.push( token.fObject );
2205 }
2206 else if ( token.fType == kImageData_TokenType) {
2207 // TODO(edisonn): implement inline image.
2208 }
2209 else {
2210 return kIgnoreError_PdfResult;
2211 }
2212 return kOK_PdfResult;
2213}
2214
2215void PdfMainLooper::loop() {
2216 PdfToken token;
2217 while (readToken(fTokenizer, &token)) {
2218 consumeToken(token);
2219 }
2220}
2221
2222PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
2223 //pdfContext.fInlineImage.fKeyValuePairs[key] = value;
2224 return kNYI_PdfResult;
2225}
2226
2227void PdfInlineImageLooper::loop() {
2228 PdfToken token;
2229 while (readToken(fTokenizer, &token)) {
2230 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2231 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2232 looper->setUp(this);
2233 looper->loop();
2234 } else {
2235 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI") == 0) {
2236 done();
2237 return;
2238 }
2239
2240 consumeToken(token);
2241 }
2242 }
2243 // TODO(edisonn): report error/warning, EOF without EI.
2244}
2245
2246PdfResult PdfInlineImageLooper::done() {
2247
2248 // TODO(edisonn): long to short names
2249 // TODO(edisonn): set properties in a map
2250 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
2251 // the stream.
2252
2253 SkBitmap bitmap;
2254 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
2255
2256 // TODO(edisonn): matrix use.
2257 // Draw dummy red square, to show the prezence of the inline image.
2258 fCanvas->drawBitmap(bitmap,
2259 SkDoubleToScalar(0),
2260 SkDoubleToScalar(0),
2261 NULL);
2262 return kNYI_PdfResult;
2263}
2264
2265PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2266 return fParent->consumeToken(token);
2267}
2268
2269void PdfCompatibilitySectionLooper::loop() {
2270 // TODO(edisonn): save stacks position, or create a new stack?
2271 // TODO(edisonn): what happens if we pop out more variables then when we started?
2272 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2273 // pop-ing too much will not affect outside the section.
2274 PdfToken token;
2275 while (readToken(fTokenizer, &token)) {
2276 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2277 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2278 looper->setUp(this);
2279 looper->loop();
2280 delete looper;
2281 } else {
2282 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
2283 fParent->consumeToken(token);
2284 }
2285 }
2286 // TODO(edisonn): restore stack.
2287}
2288
2289// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2290// TODO(edisonn): Add API for Forms viewing and editing
2291// e.g. SkBitmap getPage(int page);
2292// int formsCount();
2293// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2294// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2295// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2296// resources needed, so the preview is fast.
2297// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2298// references automatically.
2299
2300bool SkPdfViewer::load(const SkString inputFileName, SkPicture* out) {
2301 try
2302 {
2303 std::cout << "Init: " << inputFileName.c_str() << std::endl;
2304
2305 SkPdfDoc doc(inputFileName.c_str());
2306 if (!doc.pages())
2307 {
2308 std::cout << "ERROR: Empty Document" << inputFileName.c_str() << std::endl;
2309 return false;
2310 } else {
2311
2312 for (int pn = 0; pn < doc.pages(); ++pn) {
2313 // TODO(edisonn): implement inheritance properties as per PDF spec
2314 //SkRect rect = page->MediaBox();
2315 SkRect rect = doc.MediaBox(pn);
2316
2317 #ifdef PDF_TRACE
2318 printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rect.width()), SkScalarToDouble(rect.height()));
2319 #endif
2320
2321 // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to use?
2322
2323 SkBitmap bitmap;
2324 #ifdef PDF_DEBUG_3X
2325 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()));
2326 #else
2327 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
2328 #endif
2329 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
2330 SkCanvas canvas(device);
2331
2332 gDumpBitmap = &bitmap;
2333
2334 doc.drawPage(pn, &canvas);
2335
2336 SkString out;
2337 out.appendf("%s-%i.png", inputFileName.c_str(), pn);
2338 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
2339 }
2340 return true;
2341 }
2342 }
2343 catch( PdfError & e )
2344 {
2345 std::cout << "ERROR: PDF can't be parsed!" << inputFileName.c_str() << std::endl;
2346 return false;
2347 }
2348
2349 return true;
2350}
2351
2352