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