pdf_viewer_main.cpp revision a2fab9d35c28a5abdd938d91f9d464b6f7a96ab0
1/*
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 "SkGraphics.h"
11#include "SkImageDecoder.h"
12#include "SkImageEncoder.h"
13#include "SkOSFile.h"
14#include "SkPicture.h"
15#include "SkStream.h"
16#include "SkTypeface.h"
17#include "SkTArray.h"
18#include "picture_utils.h"
19
20#include <iostream>
21#include <cstdio>
22#include <stack>
23
24#include "podofo.h"
25using namespace PoDoFo;
26
27bool LongFromDictionary(const PdfMemDocument* pdfDoc,
28                        const PdfDictionary& dict,
29                        const char* key,
30                        const char* abr,
31                        long* data);
32
33bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
34                          const PdfDictionary& dict,
35                          const char* key,
36                          const char* abr,
37                          double* data);
38
39bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
40                        const PdfDictionary& dict,
41                        const char* key,
42                        const char* abr,
43                        bool* data);
44
45bool NameFromDictionary(const PdfMemDocument* pdfDoc,
46                        const PdfDictionary& dict,
47                        const char* key,
48                        const char* abr,
49                        std::string* data);
50
51bool StringFromDictionary(const PdfMemDocument* pdfDoc,
52                          const PdfDictionary& dict,
53                          const char* key,
54                          const char* abr,
55                          std::string* data);
56
57class SkPdfDictionary;
58bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
59                              const PdfDictionary& dict,
60                              const char* key,
61                              const char* abr,
62                              SkPdfDictionary** data);
63
64class SkPdfObject;
65bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
66                        const PdfDictionary& dict,
67                        const char* key,
68                        const char* abr,
69                        SkPdfObject** data);
70
71
72#include "pdf_auto_gen.h"
73
74/*
75 * TODO(edisonn): ASAP so skp -> pdf -> png looks greap
76 * - load gs/ especially smask and already known prop
77 * - use transparency (I think ca and CA ops)
78 * - load font for baidu.pdf
79 * - load font for youtube.pdf
80*/
81
82//#define PDF_TRACE
83//#define PDF_TRACE_DIFF_IN_PNG
84//#define PDF_DEBUG_NO_CLIPING
85//#define PDF_DEBUG_NO_PAGE_CLIPING
86//#define PDF_DEBUG_3X
87
88// TODO(edisonn): move in trace util.
89#ifdef PDF_TRACE
90static void SkTraceMatrix(const SkMatrix& matrix, const char* sz = "") {
91    printf("SkMatrix %s ", sz);
92    for (int i = 0 ; i < 9 ; i++) {
93        printf("%f ", SkScalarToDouble(matrix.get(i)));
94    }
95    printf("\n");
96}
97#else
98#define SkTraceMatrix(a,b)
99#endif
100
101using namespace std;
102using namespace PoDoFo;
103
104// Utilities
105static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
106    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
107
108    bitmap->allocPixels();
109    bitmap->eraseColor(color);
110}
111
112// TODO(edisonn): synonyms? DeviceRGB and RGB ...
113int GetColorSpaceComponents(const std::string& colorSpace) {
114    if (colorSpace == "DeviceCMYK") {
115        return 4;
116    } else if (colorSpace == "DeviceGray" ||
117            colorSpace == "CalGray" ||
118            colorSpace == "Indexed") {
119        return 1;
120    } else if (colorSpace == "DeviceRGB" ||
121            colorSpace == "CalRGB" ||
122            colorSpace == "Lab") {
123        return 3;
124    } else {
125        return 0;
126    }
127}
128
129const PdfObject* resolveReferenceObject(const PdfMemDocument* pdfDoc,
130                                  const PdfObject* obj,
131                                  bool resolveOneElementArrays = false) {
132    while (obj && (obj->IsReference() || (resolveOneElementArrays &&
133                                          obj->IsArray() &&
134                                          obj->GetArray().GetSize() == 1))) {
135        if (obj->IsReference()) {
136            // We need to force the non const, the only update we will do is for recurssion checks.
137            PdfReference& ref = (PdfReference&)obj->GetReference();
138            obj = pdfDoc->GetObjects().GetObject(ref);
139        } else {
140            obj = &obj->GetArray()[0];
141        }
142    }
143
144    return obj;
145}
146
147static SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
148    SkMatrix matrix;
149    matrix.setAll(SkDoubleToScalar(array[0]),
150                  SkDoubleToScalar(array[2]),
151                  SkDoubleToScalar(array[4]),
152                  SkDoubleToScalar(array[1]),
153                  SkDoubleToScalar(array[3]),
154                  SkDoubleToScalar(array[5]),
155                  SkDoubleToScalar(0),
156                  SkDoubleToScalar(0),
157                  SkDoubleToScalar(1));
158
159    return matrix;
160}
161
162// TODO(edisonn): better class design.
163struct PdfColorOperator {
164    std::string fColorSpace;  // TODO(edisonn): use SkString
165    SkColor fColor;
166    // TODO(edisonn): add here other color space options.
167
168    void setRGBColor(SkColor color) {
169        // TODO(edisonn): ASSERT DeviceRGB is the color space.
170        fColor = color;
171    }
172    // TODO(edisonn): double check the default values for all fields.
173    PdfColorOperator() : fColor(SK_ColorBLACK) {}
174};
175
176// TODO(edisonn): better class design.
177struct PdfGraphicsState {
178    SkMatrix            fMatrix;
179    SkMatrix            fMatrixTm;
180    SkMatrix            fMatrixTlm;
181
182    double              fCurPosX;
183    double              fCurPosY;
184
185    double              fCurFontSize;
186    bool                fTextBlock;
187    PdfFont*            fCurFont;
188    SkPath              fPath;
189    bool                fPathClosed;
190
191    // Clip that is applied after the drawing is done!!!
192    bool                fHasClipPathToApply;
193    SkPath              fClipPath;
194
195    PdfColorOperator    fStroking;
196    PdfColorOperator    fNonStroking;
197
198    double              fLineWidth;
199    double              fTextLeading;
200    double              fWordSpace;
201    double              fCharSpace;
202
203    const PdfObject*    fObjectWithResources;
204
205    SkBitmap            fSMask;
206
207    PdfGraphicsState() {
208        fCurPosX      = 0.0;
209        fCurPosY      = 0.0;
210        fCurFontSize  = 0.0;
211        fTextBlock    = false;
212        fCurFont      = NULL;
213        fMatrix       = SkMatrix::I();
214        fMatrixTm     = SkMatrix::I();
215        fMatrixTlm    = SkMatrix::I();
216        fPathClosed   = true;
217        fLineWidth    = 0;
218        fTextLeading  = 0;
219        fWordSpace    = 0;
220        fCharSpace    = 0;
221        fObjectWithResources = NULL;
222        fHasClipPathToApply = false;
223    }
224};
225
226// TODO(edisonn): better class design.
227struct PdfInlineImage {
228    std::map<std::string, std::string> fKeyValuePairs;
229    std::string fImageData;
230
231};
232
233// TODO(edisonn): better class design.
234struct PdfContext {
235    std::stack<PdfVariant>          fVarStack;
236    std::stack<PdfGraphicsState>    fStateStack;
237    PdfGraphicsState                fGraphicsState;
238    PoDoFo::PdfPage*                fPdfPage;
239    PdfMemDocument*                 fPdfDoc;
240    SkMatrix                        fOriginalMatrix;
241
242    PdfInlineImage                  fInlineImage;
243
244    PdfContext() :  fPdfPage(NULL),
245                    fPdfDoc(NULL) {}
246
247};
248
249//  TODO(edisonn): temporary code, to report how much of the PDF we actually think we rendered.
250enum PdfResult {
251    kOK_PdfResult,
252    kPartial_PdfResult,
253    kNYI_PdfResult,
254    kIgnoreError_PdfResult,
255    kError_PdfResult,
256    kUnsupported_PdfResult,
257
258    kCount_PdfResult
259};
260
261struct PdfToken {
262    const char*      pszToken;
263    PdfVariant       var;
264    EPdfContentsType eType;
265
266    PdfToken() : pszToken(NULL) {}
267};
268
269PdfContext* gPdfContext = NULL;
270SkBitmap* gDumpBitmap = NULL;
271SkCanvas* gDumpCanvas = NULL;
272char gLastKeyword[100] = "";
273int gLastOpKeyword = -1;
274char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX";
275int gReadOp = 0;
276
277
278
279bool hasVisualEffect(const char* pdfOp) {
280    return true;
281    if (*pdfOp == '\0') return false;
282
283    char markedPdfOp[100] = ",";
284    strcat(markedPdfOp, pdfOp);
285    strcat(markedPdfOp, ",");
286
287    return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
288}
289
290// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
291static bool readToken(PdfContentsTokenizer* fTokenizer, PdfToken* token) {
292    bool ret = fTokenizer->ReadNext(token->eType, token->pszToken, token->var);
293
294    gReadOp++;
295
296#ifdef PDF_TRACE_DIFF_IN_PNG
297    // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
298    // the numbar and name of last operation, so the file name will reflect op that changed.
299    if (hasVisualEffect(gLastKeyword)) {  // TODO(edisonn): and has dirty bits.
300        gDumpCanvas->flush();
301
302        SkBitmap bitmap;
303        setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
304
305        memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
306
307        SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
308        SkCanvas canvas(device);
309
310        // draw context stuff here
311        SkPaint blueBorder;
312        blueBorder.setColor(SK_ColorBLUE);
313        blueBorder.setStyle(SkPaint::kStroke_Style);
314        blueBorder.setTextSize(SkDoubleToScalar(20));
315
316        SkString str;
317
318        const SkClipStack* clipStack = gDumpCanvas->getClipStack();
319        if (clipStack) {
320            SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
321            const SkClipStack::Element* elem;
322            double y = 0;
323            int total = 0;
324            while (elem = iter.next()) {
325                total++;
326                y += 30;
327
328                switch (elem->getType()) {
329                    case SkClipStack::Element::kRect_Type:
330                        canvas.drawRect(elem->getRect(), blueBorder);
331                        canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
332                        break;
333                    case SkClipStack::Element::kPath_Type:
334                        canvas.drawPath(elem->getPath(), blueBorder);
335                        canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
336                        break;
337                    case SkClipStack::Element::kEmpty_Type:
338                        canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
339                        break;
340                    default:
341                        canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
342                        break;
343                }
344            }
345
346            y += 30;
347            str.printf("Number of clips in stack: %i", total);
348            canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
349        }
350
351        const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
352        SkPath clipPath;
353        if (clipRegion.getBoundaryPath(&clipPath)) {
354            SkPaint redBorder;
355            redBorder.setColor(SK_ColorRED);
356            redBorder.setStyle(SkPaint::kStroke_Style);
357            canvas.drawPath(clipPath, redBorder);
358        }
359
360        canvas.flush();
361
362        SkString out;
363
364        // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
365        // ... and other properties, to be able to debug th code easily
366
367        out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
368        SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
369    }
370
371    if (token->eType == ePdfContentsType_Keyword) {
372        strcpy(gLastKeyword, token->pszToken);
373        gLastOpKeyword = gReadOp;
374    } else {
375        strcpy(gLastKeyword, "");
376    }
377#endif
378
379    return ret;
380}
381
382// TODO(edisonn): Document PdfTokenLooper and subclasses.
383class PdfTokenLooper {
384protected:
385    PdfTokenLooper* fParent;
386    PdfContentsTokenizer* fTokenizer;
387    PdfContext* fPdfContext;
388    SkCanvas* fCanvas;
389
390public:
391    PdfTokenLooper(PdfTokenLooper* parent,
392                   PdfContentsTokenizer* tokenizer,
393                   PdfContext* pdfContext,
394                   SkCanvas* canvas)
395        : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
396
397    virtual PdfResult consumeToken(PdfToken& token) = 0;
398    virtual void loop() = 0;
399
400    void setUp(PdfTokenLooper* parent) {
401        fParent = parent;
402        fTokenizer = parent->fTokenizer;
403        fPdfContext = parent->fPdfContext;
404        fCanvas = parent->fCanvas;
405    }
406};
407
408class PdfMainLooper : public PdfTokenLooper {
409public:
410    PdfMainLooper(PdfTokenLooper* parent,
411                  PdfContentsTokenizer* tokenizer,
412                  PdfContext* pdfContext,
413                  SkCanvas* canvas)
414        : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
415
416    virtual PdfResult consumeToken(PdfToken& token);
417    virtual void loop();
418};
419
420class PdfInlineImageLooper : public PdfTokenLooper {
421public:
422    PdfInlineImageLooper()
423        : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
424
425    virtual PdfResult consumeToken(PdfToken& token);
426    virtual void loop();
427    PdfResult done();
428};
429
430class PdfCompatibilitySectionLooper : public PdfTokenLooper {
431public:
432    PdfCompatibilitySectionLooper()
433        : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
434
435    virtual PdfResult consumeToken(PdfToken& token);
436    virtual void loop();
437};
438
439typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
440
441map<std::string, PdfOperatorRenderer> gPdfOps;
442
443map<std::string, int> gRenderStats[kCount_PdfResult];
444
445char* gRenderStatsNames[kCount_PdfResult] = {
446    "Success",
447    "Partially implemented",
448    "Not yet implemented",
449    "Ignore Error",
450    "Error",
451    "Unsupported/Unknown"
452};
453
454struct SkPdfStandardFont {
455    const char* fName;
456    bool fIsBold;
457    bool fIsItalic;
458};
459
460static map<std::string, SkPdfStandardFont>& getStandardFonts() {
461    static std::map<std::string, SkPdfStandardFont> gPdfStandardFonts;
462
463    // TODO (edisonn): , vs - ? what does it mean?
464    // TODO (edisonn): MT, PS, Oblique=italic?, ... what does it mean?
465    if (gPdfStandardFonts.empty()) {
466        gPdfStandardFonts["Arial"] = {"Arial", false, false};
467        gPdfStandardFonts["Arial,Bold"] = {"Arial", true, false};
468        gPdfStandardFonts["Arial,BoldItalic"] = {"Arial", true, true};
469        gPdfStandardFonts["Arial,Italic"] = {"Arial", false, true};
470        gPdfStandardFonts["Arial-Bold"] = {"Arial", true, false};
471        gPdfStandardFonts["Arial-BoldItalic"] = {"Arial", true, true};
472        gPdfStandardFonts["Arial-BoldItalicMT"] = {"Arial", true, true};
473        gPdfStandardFonts["Arial-BoldMT"] = {"Arial", true, false};
474        gPdfStandardFonts["Arial-Italic"] = {"Arial", false, true};
475        gPdfStandardFonts["Arial-ItalicMT"] = {"Arial", false, true};
476        gPdfStandardFonts["ArialMT"] = {"Arial", false, false};
477        gPdfStandardFonts["Courier"] = {"Courier New", false, false};
478        gPdfStandardFonts["Courier,Bold"] = {"Courier New", true, false};
479        gPdfStandardFonts["Courier,BoldItalic"] = {"Courier New", true, true};
480        gPdfStandardFonts["Courier,Italic"] = {"Courier New", false, true};
481        gPdfStandardFonts["Courier-Bold"] = {"Courier New", true, false};
482        gPdfStandardFonts["Courier-BoldOblique"] = {"Courier New", true, true};
483        gPdfStandardFonts["Courier-Oblique"] = {"Courier New", false, true};
484        gPdfStandardFonts["CourierNew"] = {"Courier New", false, false};
485        gPdfStandardFonts["CourierNew,Bold"] = {"Courier New", true, false};
486        gPdfStandardFonts["CourierNew,BoldItalic"] = {"Courier New", true, true};
487        gPdfStandardFonts["CourierNew,Italic"] = {"Courier New", false, true};
488        gPdfStandardFonts["CourierNew-Bold"] = {"Courier New", true, false};
489        gPdfStandardFonts["CourierNew-BoldItalic"] = {"Courier New", true, true};
490        gPdfStandardFonts["CourierNew-Italic"] = {"Courier New", false, true};
491        gPdfStandardFonts["CourierNewPS-BoldItalicMT"] = {"Courier New", true, true};
492        gPdfStandardFonts["CourierNewPS-BoldMT"] = {"Courier New", true, false};
493        gPdfStandardFonts["CourierNewPS-ItalicMT"] = {"Courier New", false, true};
494        gPdfStandardFonts["CourierNewPSMT"] = {"Courier New", false, false};
495        gPdfStandardFonts["Helvetica"] = {"Helvetica", false, false};
496        gPdfStandardFonts["Helvetica,Bold"] = {"Helvetica", true, false};
497        gPdfStandardFonts["Helvetica,BoldItalic"] = {"Helvetica", true, true};
498        gPdfStandardFonts["Helvetica,Italic"] = {"Helvetica", false, true};
499        gPdfStandardFonts["Helvetica-Bold"] = {"Helvetica", true, false};
500        gPdfStandardFonts["Helvetica-BoldItalic"] = {"Helvetica", true, true};
501        gPdfStandardFonts["Helvetica-BoldOblique"] = {"Helvetica", true, true};
502        gPdfStandardFonts["Helvetica-Italic"] = {"Helvetica", false, true};
503        gPdfStandardFonts["Helvetica-Oblique"] = {"Helvetica", false, true};
504        gPdfStandardFonts["Times-Bold"] = {"Times", true, false};
505        gPdfStandardFonts["Times-BoldItalic"] = {"Times", true, true};
506        gPdfStandardFonts["Times-Italic"] = {"Times", false, true};
507        gPdfStandardFonts["Times-Roman"] = {"Times New Roman", false, false};
508        gPdfStandardFonts["TimesNewRoman"] = {"Times New Roman", false, false};
509        gPdfStandardFonts["TimesNewRoman,Bold"] = {"Times New Roman", true, false};
510        gPdfStandardFonts["TimesNewRoman,BoldItalic"] = {"Times New Roman", true, true};
511        gPdfStandardFonts["TimesNewRoman,Italic"] = {"Times New Roman", false, true};
512        gPdfStandardFonts["TimesNewRoman-Bold"] = {"Times New Roman", true, false};
513        gPdfStandardFonts["TimesNewRoman-BoldItalic"] = {"Times New Roman", true, true};
514        gPdfStandardFonts["TimesNewRoman-Italic"] = {"Times New Roman", false, true};
515        gPdfStandardFonts["TimesNewRomanPS"] = {"Times New Roman", false, false};
516        gPdfStandardFonts["TimesNewRomanPS-Bold"] = {"Times New Roman", true, false};
517        gPdfStandardFonts["TimesNewRomanPS-BoldItalic"] = {"Times New Roman", true, true};
518        gPdfStandardFonts["TimesNewRomanPS-BoldItalicMT"] = {"Times New Roman", true, true};
519        gPdfStandardFonts["TimesNewRomanPS-BoldMT"] = {"Times New Roman", true, false};
520        gPdfStandardFonts["TimesNewRomanPS-Italic"] = {"Times New Roman", false, true};
521        gPdfStandardFonts["TimesNewRomanPS-ItalicMT"] = {"Times New Roman", false, true};
522        gPdfStandardFonts["TimesNewRomanPSMT"] = {"Times New Roman", false, false};
523    }
524
525    return gPdfStandardFonts;
526}
527
528static SkTypeface* SkTypefaceFromPdfStandardFont(const char* fontName, bool bold, bool italic) {
529    map<std::string, SkPdfStandardFont>& standardFontMap = getStandardFonts();
530
531    if (standardFontMap.find(fontName) != standardFontMap.end()) {
532        SkPdfStandardFont fontData = standardFontMap[fontName];
533
534        // TODO(edisonn): How does the bold/italic specified in standard definition combines with
535        // the one in /font key? use OR for now.
536        bold = bold || fontData.fIsBold;
537        italic = italic || fontData.fIsItalic;
538
539        SkTypeface* typeface = SkTypeface::CreateFromName(
540            fontData.fName,
541            SkTypeface::Style((bold ? SkTypeface::kBold : 0) |
542                              (italic ? SkTypeface::kItalic : 0)));
543        if (typeface) {
544            typeface->ref();
545        }
546        return typeface;
547    }
548    return NULL;
549}
550
551static SkTypeface* SkTypefaceFromPdfFont(PdfFont* font) {
552    PdfObject* fontObject = font->GetObject();
553
554    PdfObject* pBaseFont = NULL;
555    // TODO(edisonn): warning, PoDoFo has a bug in PdfFont constructor, does not call InitVars()
556    // for now fixed locally.
557    pBaseFont = fontObject->GetIndirectKey( "BaseFont" );
558    const char* pszBaseFontName = pBaseFont->GetName().GetName().c_str();
559
560#ifdef PDF_TRACE
561    std::string str;
562    fontObject->ToString(str);
563    printf("Base Font Name: %s\n", pszBaseFontName);
564    printf("Font Object Data: %s\n", str.c_str());
565#endif
566
567    SkTypeface* typeface = SkTypefaceFromPdfStandardFont(pszBaseFontName, font->IsBold(), font->IsItalic());
568
569    if (typeface != NULL) {
570        return typeface;
571    }
572
573    char name[1000];
574    // HACK
575    strncpy(name, pszBaseFontName, 1000);
576    char* comma = strstr(name, ",");
577    char* dash = strstr(name, "-");
578    if (comma) *comma = '\0';
579    if (dash) *dash = '\0';
580
581    typeface = SkTypeface::CreateFromName(
582                name,
583                SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
584                                  (font->IsItalic() ? SkTypeface::kItalic : 0)));
585
586    if (typeface != NULL) {
587#ifdef PDF_TRACE
588    printf("HACKED FONT found %s\n", name);
589#endif
590        return typeface;
591    }
592
593#ifdef PDF_TRACE
594    printf("FONT_NOT_FOUND %s\n", pszBaseFontName);
595#endif
596
597    // TODO(edisonn): Report Warning, NYI
598    return SkTypeface::CreateFromName(
599                "Times New Roman",
600                SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
601                                  (font->IsItalic() ? SkTypeface::kItalic : 0)));
602}
603
604// TODO(edisonn): move this code in podofo, so we don't have to fix the font.
605// This logic needs to be moved in PdfEncodingObjectFactory::CreateEncoding
606std::map<PdfFont*, PdfCMapEncoding*> gFontsFixed;
607PdfEncoding* FixPdfFont(PdfContext* pdfContext, PdfFont* fCurFont) {
608    // TODO(edisonn): and is Identity-H
609    if (gFontsFixed.find(fCurFont) == gFontsFixed.end()) {
610        if (fCurFont->GetObject()->IsDictionary() && fCurFont->GetObject()->GetDictionary().HasKey(PdfName("ToUnicode"))) {
611            PdfCMapEncoding* enc = new PdfCMapEncoding(
612                    fCurFont->GetObject(),
613                    (PdfObject*)resolveReferenceObject(pdfContext->fPdfDoc,
614                                           fCurFont->GetObject()->GetDictionary().GetKey(PdfName("ToUnicode"))),
615                    PdfCMapEncoding::eBaseEncoding_Identity);  // todo, read the base encoding
616            gFontsFixed[fCurFont] = enc;
617            return enc;
618        }
619
620        return NULL;
621    }
622
623    return gFontsFixed[fCurFont];
624}
625
626PdfResult DrawText(PdfContext* pdfContext,
627                   PdfFont* fCurFont,
628                   const PdfString& rString,
629                   SkCanvas* canvas)
630{
631    if (!fCurFont)
632    {
633        // TODO(edisonn): ignore the error, use the default font?
634        return kError_PdfResult;
635    }
636
637    const PdfEncoding* enc = FixPdfFont(pdfContext, fCurFont);
638    bool cMapUnicodeFont = enc != NULL;
639    if (!enc) enc = fCurFont->GetEncoding();
640    if (!enc)
641    {
642        // TODO(edisonn): Can we recover from this error?
643        return kError_PdfResult;
644    }
645
646    PdfString r2 = rString;
647    PdfString unicode;
648
649    if (cMapUnicodeFont) {
650        r2 = PdfString((pdf_utf16be*)rString.GetString(), rString.GetLength() / 2);
651    }
652
653    unicode = enc->ConvertToUnicode( r2, fCurFont );
654
655#ifdef PDF_TRACE
656    printf("%i %i ? %c rString.len = %i\n", (int)rString.GetString()[0], (int)rString.GetString()[1], (int)rString.GetString()[1], rString.GetLength());
657    printf("%i %i %i %i %c unicode.len = %i\n", (int)unicode.GetString()[0], (int)unicode.GetString()[1], (int)unicode.GetString()[2], (int)unicode.GetString()[3], (int)unicode.GetString()[0], unicode.GetLength());
658#endif
659
660    SkPaint paint;
661    // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
662    // Or maybe just not call setTextSize at all?
663    if (pdfContext->fGraphicsState.fCurFontSize != 0) {
664        paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
665    }
666    if (fCurFont->GetFontScale() != 0) {
667        paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
668    }
669    paint.setColor(pdfContext->fGraphicsState.fNonStroking.fColor);
670
671    paint.setTypeface(SkTypefaceFromPdfFont(fCurFont));
672
673    paint.setAntiAlias(true);
674    // TODO(edisonn): paint.setStyle(...);
675
676    canvas->save();
677    SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
678
679#if 0
680    // Reverse now the space, otherwise the text is upside down.
681    SkScalar z = SkIntToScalar(0);
682    SkScalar one = SkIntToScalar(1);
683
684    SkPoint normalSpace1[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
685    SkPoint mirrorSpace1[4];
686    pdfContext->fGraphicsState.fMatrixTm.mapPoints(mirrorSpace1, normalSpace1, 4);
687
688    SkPoint normalSpace2[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, -one), SkPoint::Make(z, -one)};
689    SkPoint mirrorSpace2[4];
690    pdfContext->fGraphicsState.fMatrixTm.mapPoints(mirrorSpace2, normalSpace2, 4);
691
692#ifdef PDF_TRACE
693    printf("mirror1[0], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[0].x()), SkScalarToDouble(mirrorSpace1[0].y()));
694    printf("mirror1[1], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[1].x()), SkScalarToDouble(mirrorSpace1[1].y()));
695    printf("mirror1[2], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[2].x()), SkScalarToDouble(mirrorSpace1[2].y()));
696    printf("mirror1[3], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[3].x()), SkScalarToDouble(mirrorSpace1[3].y()));
697    printf("mirror2[0], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[0].x()), SkScalarToDouble(mirrorSpace2[0].y()));
698    printf("mirror2[1], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[1].x()), SkScalarToDouble(mirrorSpace2[1].y()));
699    printf("mirror2[2], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[2].x()), SkScalarToDouble(mirrorSpace2[2].y()));
700    printf("mirror2[3], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[3].x()), SkScalarToDouble(mirrorSpace2[3].y()));
701#endif
702
703    SkMatrix mirror;
704    SkASSERT(mirror.setPolyToPoly(mirrorSpace1, mirrorSpace2, 4));
705
706    // TODO(edisonn): text positioning wrong right now. Need to get matrix operations right.
707    matrix.preConcat(mirror);
708    canvas->setMatrix(matrix);
709#endif
710
711    SkPoint point1;
712    pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
713
714    SkMatrix mirror;
715    mirror.setTranslate(0, -point1.y());
716    // TODO(edisonn): fix rotated text, and skewed too
717    mirror.postScale(SK_Scalar1, -SK_Scalar1);
718    // TODO(edisonn): post rotate, skew
719    mirror.postTranslate(0, point1.y());
720
721    matrix.postConcat(mirror);
722
723    canvas->setMatrix(matrix);
724
725    SkTraceMatrix(matrix, "mirrored");
726
727#ifdef PDF_TRACE
728    SkPoint point;
729    pdfContext->fGraphicsState.fMatrixTm.mapXY(SkDoubleToScalar(0), SkDoubleToScalar(0), &point);
730    printf("Original SkCanvas resolved coordinates, x = %f y = %f\n", SkScalarToDouble(point.x()), SkScalarToDouble(point.y()));
731    matrix.mapXY(SkDoubleToScalar(0), SkDoubleToScalar(0), &point);
732    printf("Mirored SkCanvas resolved coordinates, x = %f y = %f\n", SkScalarToDouble(point.x()), SkScalarToDouble(point.y()));
733#endif
734
735    // TODO(edisonn): remove this call once we load the font properly
736    // The extra * will show that we got at least the text positioning right
737    // even if font failed to be loaded
738//    canvas->drawText(".", 1, SkDoubleToScalar(-5.0), SkDoubleToScalar(0.0), paint);
739
740
741
742    // TODO(edisonn): use character and word spacing .. add utility function
743    if (cMapUnicodeFont) {
744        paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
745        SkScalar textWidth = paint.measureText(unicode.GetString(), unicode.GetLength());
746        pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
747        canvas->drawText(unicode.GetString(), unicode.GetLength(), SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
748    }
749    else {
750        paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
751        SkScalar textWidth = paint.measureText(unicode.GetStringUtf8().c_str(), strlen(unicode.GetStringUtf8().c_str()));
752        pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
753        canvas->drawText(unicode.GetStringUtf8().c_str(), strlen(unicode.GetStringUtf8().c_str()), SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
754    }
755
756//    paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
757//    unsigned char ch = *(unicode.GetString() + 3);
758//    if ((ch & 0xC0) != 0x80 && ch < 0x80) {
759//        printf("x%i", ch);
760//        SkScalar textWidth = paint.measureText(&ch, 1);
761//        pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
762//        canvas->drawText(&ch, 1, SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
763//    }
764
765    canvas->restore();
766
767
768    return kPartial_PdfResult;
769}
770
771// TODO(edisonn): create header files with declarations!
772PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
773PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
774PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
775PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
776
777// TODO(edisonn): deal with synonyms (/BPC == /BitsPerComponent), here or in GetKey?
778// Always pass long form in key, and have a map of long -> short key
779bool LongFromDictionary(const PdfMemDocument* pdfDoc,
780                        const PdfDictionary& dict,
781                        const char* key,
782                        long* data) {
783    const PdfObject* value = resolveReferenceObject(pdfDoc,
784                                              dict.GetKey(PdfName(key)));
785
786    if (value == NULL || !value->IsNumber()) {
787        return false;
788    }
789
790    *data = value->GetNumber();
791    return true;
792}
793
794bool LongFromDictionary(const PdfMemDocument* pdfDoc,
795                        const PdfDictionary& dict,
796                        const char* key,
797                        const char* abr,
798                        long* data) {
799    if (LongFromDictionary(pdfDoc, dict, key, data)) return true;
800    if (abr == NULL || *abr == '\0') return false;
801    return LongFromDictionary(pdfDoc, dict, abr, data);
802}
803
804bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
805                          const PdfDictionary& dict,
806                          const char* key,
807                          double* data) {
808    const PdfObject* value = resolveReferenceObject(pdfDoc,
809                                              dict.GetKey(PdfName(key)));
810
811    if (value == NULL || !value->IsReal()) {
812        return false;
813    }
814
815    *data = value->GetReal();
816    return true;
817}
818
819bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
820                          const PdfDictionary& dict,
821                          const char* key,
822                          const char* abr,
823                          double* data) {
824    if (DoubleFromDictionary(pdfDoc, dict, key, data)) return true;
825    if (abr == NULL || *abr == '\0') return false;
826    return DoubleFromDictionary(pdfDoc, dict, abr, data);
827}
828
829
830bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
831                        const PdfDictionary& dict,
832                        const char* key,
833                        bool* data) {
834    const PdfObject* value = resolveReferenceObject(pdfDoc,
835                                              dict.GetKey(PdfName(key)));
836
837    if (value == NULL || !value->IsBool()) {
838        return false;
839    }
840
841    *data = value->GetBool();
842    return true;
843}
844
845bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
846                        const PdfDictionary& dict,
847                        const char* key,
848                        const char* abr,
849                        bool* data) {
850    if (BoolFromDictionary(pdfDoc, dict, key, data)) return true;
851    if (abr == NULL || *abr == '\0') return false;
852    return BoolFromDictionary(pdfDoc, dict, abr, data);
853}
854
855bool NameFromDictionary(const PdfMemDocument* pdfDoc,
856                        const PdfDictionary& dict,
857                        const char* key,
858                        std::string* data) {
859    const PdfObject* value = resolveReferenceObject(pdfDoc,
860                                              dict.GetKey(PdfName(key)),
861                                              true);
862    if (value == NULL || !value->IsName()) {
863        return false;
864    }
865
866    *data = value->GetName().GetName();
867    return true;
868}
869
870bool NameFromDictionary(const PdfMemDocument* pdfDoc,
871                        const PdfDictionary& dict,
872                        const char* key,
873                        const char* abr,
874                        std::string* data) {
875    if (NameFromDictionary(pdfDoc, dict, key, data)) return true;
876    if (abr == NULL || *abr == '\0') return false;
877    return NameFromDictionary(pdfDoc, dict, abr, data);
878}
879
880bool StringFromDictionary(const PdfMemDocument* pdfDoc,
881                          const PdfDictionary& dict,
882                          const char* key,
883                          std::string* data) {
884    const PdfObject* value = resolveReferenceObject(pdfDoc,
885                                              dict.GetKey(PdfName(key)),
886                                              true);
887    if (value == NULL || (!value->IsString() && !value->IsHexString())) {
888        return false;
889    }
890
891    *data = value->GetString().GetString();
892    return true;
893}
894
895bool StringFromDictionary(const PdfMemDocument* pdfDoc,
896                          const PdfDictionary& dict,
897                          const char* key,
898                          const char* abr,
899                          std::string* data) {
900    if (StringFromDictionary(pdfDoc, dict, key, data)) return true;
901    if (abr == NULL || *abr == '\0') return false;
902    return StringFromDictionary(pdfDoc, dict, abr, data);
903}
904
905bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
906                              const PdfDictionary& dict,
907                              const char* key,
908                              SkPdfDictionary** data) {
909    const PdfObject* value = resolveReferenceObject(pdfDoc,
910                                              dict.GetKey(PdfName(key)),
911                                              true);
912    if (value == NULL || !value->IsDictionary()) {
913        return false;
914    }
915
916    return PodofoMapper::mapDictionary(*pdfDoc, *value, (SkPdfObject**)data);
917}
918
919bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
920                        const PdfDictionary& dict,
921                        const char* key,
922                        const char* abr,
923                        SkPdfDictionary** data) {
924    if (DictionaryFromDictionary(pdfDoc, dict, key, data)) return true;
925    if (abr == NULL || *abr == '\0') return false;
926    return DictionaryFromDictionary(pdfDoc, dict, abr, data);
927}
928
929bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
930                          const PdfDictionary& dict,
931                          const char* key,
932                          SkPdfObject** data) {
933    const PdfObject* value = resolveReferenceObject(pdfDoc,
934                                              dict.GetKey(PdfName(key)),
935                                              true);
936    if (value == NULL) {
937        return false;
938    }
939    return PodofoMapper::mapObject(*pdfDoc, *value, data);
940}
941
942bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
943                        const PdfDictionary& dict,
944                        const char* key,
945                        const char* abr,
946                        SkPdfObject** data) {
947    if (ObjectFromDictionary(pdfDoc, dict, key, data)) return true;
948    if (abr == NULL || *abr == '\0') return false;
949    return ObjectFromDictionary(pdfDoc, dict, abr, data);
950}
951
952
953// TODO(edisonn): perf!!!
954
955static SkColorTable* getGrayColortable() {
956    static SkColorTable* grayColortable = NULL;
957    if (grayColortable == NULL) {
958        SkPMColor* colors = new SkPMColor[256];
959        for (int i = 0 ; i < 256; i++) {
960            colors[i] = SkPreMultiplyARGB(255, i, i, i);
961        }
962        grayColortable = new SkColorTable(colors, 256);
963    }
964    return grayColortable;
965}
966
967SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
968                                     int width, int height, int bytesPerLine,
969                                     int bpc, const std::string& colorSpace,
970                                     bool transparencyMask) {
971    SkBitmap bitmap;
972
973    int components = GetColorSpaceComponents(colorSpace);
974//#define MAX_COMPONENTS 10
975
976    int bitsPerLine = width * components * bpc;
977    // TODO(edisonn): assume start of lines are aligned at 32 bits?
978    // Is there a faster way to load the uncompressed stream into a bitmap?
979
980    // minimal support for now
981    if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
982        SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
983
984        for (int h = 0 ; h < height; h++) {
985            long i = width * (height - 1 - h);
986            for (int w = 0 ; w < width; w++) {
987                uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
988                                                          uncompressedStream[3 * w + 1],
989                                                          uncompressedStream[3 * w + 2]);
990                i++;
991            }
992            uncompressedStream += bytesPerLine;
993        }
994
995        bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
996        bitmap.setPixels(uncompressedStreamArgb);
997    }
998    else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
999        unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
1000
1001        for (int h = 0 ; h < height; h++) {
1002            long i = width * (height - 1 - h);
1003            for (int w = 0 ; w < width; w++) {
1004                uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
1005                                                             uncompressedStream[w];
1006                i++;
1007            }
1008            uncompressedStream += bytesPerLine;
1009        }
1010
1011        bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
1012                         width, height);
1013        bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
1014    }
1015
1016    // TODO(edisonn): Report Warning, NYI, or error
1017    return bitmap;
1018}
1019
1020bool transferImageStreamToARGB(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
1021                               int width, int bytesPerLine,
1022                               int bpc, const std::string& colorSpace,
1023                               SkColor** uncompressedStreamArgb,
1024                               pdf_long* uncompressedStreamLengthInBytesArgb) {
1025    int components = GetColorSpaceComponents(colorSpace);
1026//#define MAX_COMPONENTS 10
1027
1028    int bitsPerLine = width * components * bpc;
1029    // TODO(edisonn): assume start of lines are aligned at 32 bits?
1030    int height = uncompressedStreamLength / bytesPerLine;
1031
1032    // minimal support for now
1033    if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
1034        *uncompressedStreamLengthInBytesArgb = width * height * 4;
1035        *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
1036
1037        for (int h = 0 ; h < height; h++) {
1038            long i = width * (height - 1 - h);
1039            for (int w = 0 ; w < width; w++) {
1040                (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[3 * w],
1041                                                             uncompressedStream[3 * w + 1],
1042                                                             uncompressedStream[3 * w + 2]);
1043                i++;
1044            }
1045            uncompressedStream += bytesPerLine;
1046        }
1047        return true;
1048    }
1049
1050    if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
1051        *uncompressedStreamLengthInBytesArgb = width * height * 4;
1052        *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
1053
1054        for (int h = 0 ; h < height; h++) {
1055            long i = width * (height - 1 - h);
1056            for (int w = 0 ; w < width; w++) {
1057                (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[w],
1058                                                             uncompressedStream[w],
1059                                                             uncompressedStream[w]);
1060                i++;
1061            }
1062            uncompressedStream += bytesPerLine;
1063        }
1064        return true;
1065    }
1066
1067    return false;
1068}
1069
1070// utils
1071
1072// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
1073// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
1074// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
1075// skia format, through a table
1076
1077// this functions returns the image, it does not look at the smask.
1078
1079SkBitmap getImageFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* image, bool transparencyMask) {
1080    if (image == NULL || !image->valid()) {
1081        // TODO(edisonn): report warning to be used in testing.
1082        return SkBitmap();
1083    }
1084
1085    // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
1086//    PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
1087//                                              obj.GetDictionary().GetKey(PdfName("Filter")));
1088//    if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
1089//        value = resolveReferenceObject(pdfContext->fPdfDoc,
1090//                                       &value->GetArray()[0]);
1091//    }
1092//    if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
1093//        SkStream stream = SkStream::
1094//        SkImageDecoder::Factory()
1095//    }
1096
1097    long bpc = image->BitsPerComponent();
1098    long width = image->Width();
1099    long height = image->Height();
1100    SkPdfObject* colorSpaceDict = image->ColorSpace();
1101    std::string colorSpace = "DeviceRGB";
1102    // TODO(edisonn): for multiple type fileds, generate code, like, isName(), isArray(), ...and fields like colorSpace_name(), colorSpace_array()
1103    // so we do nto go to podofo anywhere in our cpp file
1104    if (colorSpaceDict && colorSpaceDict->podofo() && colorSpaceDict->podofo()->IsName()) {
1105        colorSpace = colorSpaceDict->podofo()->GetName().GetName();
1106    }
1107
1108/*
1109    bool imageMask = image->imageMask();
1110
1111    if (imageMask) {
1112        if (bpc != 0 && bpc != 1) {
1113            // TODO(edisonn): report warning to be used in testing.
1114            return SkBitmap();
1115        }
1116        bpc = 1;
1117    }
1118*/
1119
1120    const PdfObject* obj = image->podofo();
1121
1122    char* uncompressedStream = NULL;
1123    pdf_long uncompressedStreamLength = 0;
1124
1125    PdfResult ret = kPartial_PdfResult;
1126    // TODO(edisonn): get rid of try/catch exceptions! We should not throw on user data!
1127    try {
1128        obj->GetStream()->GetFilteredCopy(&uncompressedStream, &uncompressedStreamLength);
1129    } catch (PdfError& e) {
1130        // TODO(edisonn): report warning to be used in testing.
1131        return SkBitmap();
1132    }
1133
1134    int bytesPerLine = uncompressedStreamLength / height;
1135#ifdef PDF_TRACE
1136    if (uncompressedStreamLength % height != 0) {
1137        printf("Warning uncompressedStreamLength % height != 0 !!!\n");
1138    }
1139#endif
1140
1141    SkBitmap bitmap = transferImageStreamToBitmap(
1142            (unsigned char*)uncompressedStream, uncompressedStreamLength,
1143            width, height, bytesPerLine,
1144            bpc, colorSpace,
1145            transparencyMask);
1146
1147    free(uncompressedStream);
1148
1149    return bitmap;
1150}
1151
1152SkBitmap getSmaskFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* obj) {
1153    const PdfObject* sMask = resolveReferenceObject(pdfContext->fPdfDoc,
1154                                              obj->podofo()->GetDictionary().GetKey(PdfName("SMask")));
1155
1156#ifdef PDF_TRACE
1157    std::string str;
1158    if (sMask) {
1159        sMask->ToString(str);
1160        printf("/SMask of /Subtype /Image: %s\n", str.c_str());
1161    }
1162#endif
1163
1164    if (sMask) {
1165        SkPdfImageDictionary skxobjmask(pdfContext->fPdfDoc, sMask);
1166        return getImageFromObject(pdfContext, &skxobjmask, true);
1167    }
1168
1169    // TODO(edisonn): implement GS SMask. Default to empty right now.
1170    return pdfContext->fGraphicsState.fSMask;
1171}
1172
1173PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfImageDictionary* skpdfimage) {
1174    if (skpdfimage == NULL || !skpdfimage->valid()) {
1175        return kIgnoreError_PdfResult;
1176    }
1177
1178    SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
1179    SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
1180
1181    canvas->save();
1182    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1183    SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
1184
1185    if (sMask.empty()) {
1186        canvas->drawBitmapRect(image, dst, NULL);
1187    } else {
1188        canvas->saveLayer(&dst, NULL);
1189        canvas->drawBitmapRect(image, dst, NULL);
1190        SkPaint xfer;
1191        xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
1192        canvas->drawBitmapRect(sMask, dst, &xfer);
1193        canvas->restore();
1194    }
1195
1196    canvas->restore();
1197
1198    return kPartial_PdfResult;
1199}
1200
1201bool SkMatrixFromDictionary(PdfContext* pdfContext,
1202                            const PdfDictionary& dict,
1203                            const char* key,
1204                            SkMatrix* matrix) {
1205    const PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
1206                                                    dict.GetKey(PdfName(key)));
1207
1208    if (value == NULL || !value->IsArray()) {
1209        return false;
1210    }
1211
1212    if (value->GetArray().GetSize() != 6) {
1213        return false;
1214    }
1215
1216    double array[6];
1217    for (int i = 0; i < 6; i++) {
1218        const PdfObject* elem = resolveReferenceObject(pdfContext->fPdfDoc, &value->GetArray()[i]);
1219        if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1220            return false;
1221        }
1222        array[i] = elem->GetReal();
1223    }
1224
1225    *matrix = SkMatrixFromPdfMatrix(array);
1226    return true;
1227}
1228
1229bool SkRectFromDictionary(PdfContext* pdfContext,
1230                          const PdfDictionary& dict,
1231                          const char* key,
1232                          SkRect* rect) {
1233    const PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
1234                                                    dict.GetKey(PdfName(key)));
1235
1236    if (value == NULL || !value->IsArray()) {
1237        return false;
1238    }
1239
1240    if (value->GetArray().GetSize() != 4) {
1241        return false;
1242    }
1243
1244    double array[4];
1245    for (int i = 0; i < 4; i++) {
1246        const PdfObject* elem = resolveReferenceObject(pdfContext->fPdfDoc, &value->GetArray()[i]);
1247        if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1248            return false;
1249        }
1250        array[i] = elem->GetReal();
1251    }
1252
1253    *rect = SkRect::MakeLTRB(SkDoubleToScalar(array[0]),
1254                             SkDoubleToScalar(array[1]),
1255                             SkDoubleToScalar(array[2]),
1256                             SkDoubleToScalar(array[3]));
1257    return true;
1258}
1259
1260PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject& obj) {
1261    if (!obj.HasStream() || obj.GetStream() == NULL || obj.GetStream()->GetLength() == 0) {
1262        return kOK_PdfResult;
1263    }
1264
1265    PdfOp_q(pdfContext, canvas, NULL);
1266    canvas->save();
1267
1268    pdfContext->fGraphicsState.fObjectWithResources = &obj;
1269
1270    SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
1271
1272    SkMatrix matrix;
1273    if (SkMatrixFromDictionary(pdfContext, obj.GetDictionary(), "Matrix", &matrix)) {
1274        pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1275        pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1276        pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1277        // TODO(edisonn) reset matrixTm and matricTlm also?
1278    }
1279
1280    SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1281
1282    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1283
1284    SkRect bbox;
1285    if (SkRectFromDictionary(pdfContext, obj.GetDictionary(), "BBox", &bbox)) {
1286        canvas->clipRect(bbox, SkRegion::kIntersect_Op, true);  // TODO(edisonn): AA from settings.
1287    }
1288
1289    // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
1290    // For this PdfContentsTokenizer needs to be extended.
1291
1292    char* uncompressedStream = NULL;
1293    pdf_long uncompressedStreamLength = 0;
1294
1295    PdfResult ret = kPartial_PdfResult;
1296
1297    // TODO(edisonn): get rid of try/catch exceptions! We should not throw on user data!
1298    try {
1299        obj.GetStream()->GetFilteredCopy(&uncompressedStream, &uncompressedStreamLength);
1300        if (uncompressedStream != NULL && uncompressedStreamLength != 0) {
1301            PdfContentsTokenizer tokenizer(uncompressedStream, uncompressedStreamLength);
1302            PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1303            looper.loop();
1304        }
1305        free(uncompressedStream);
1306    } catch (PdfError& e) {
1307        ret = kIgnoreError_PdfResult;
1308    }
1309
1310    // TODO(edisonn): should we restore the variable stack at the same state?
1311    // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1312    canvas->restore();
1313    PdfOp_Q(pdfContext, canvas, NULL);
1314    return ret;
1315}
1316
1317PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject& obj) {
1318    return kNYI_PdfResult;
1319}
1320
1321// TODO(edisonn): faster, have the property on the PdfObject itself.
1322std::set<const PdfObject*> gInRendering;
1323
1324class CheckRecursiveRendering {
1325    const PdfObject& fObj;
1326public:
1327    CheckRecursiveRendering(const PdfObject& obj) : fObj(obj) {
1328        gInRendering.insert(&obj);
1329    }
1330
1331    ~CheckRecursiveRendering() {
1332        //SkASSERT(fObj.fInRendering);
1333        gInRendering.erase(&fObj);
1334    }
1335
1336    static bool IsInRendering(const PdfObject& obj) {
1337        return gInRendering.find(&obj) != gInRendering.end();
1338    }
1339};
1340
1341PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject& obj) {
1342    if (CheckRecursiveRendering::IsInRendering(obj)) {
1343        // Oops, corrupt PDF!
1344        return kIgnoreError_PdfResult;
1345    }
1346
1347    CheckRecursiveRendering checkRecursion(obj);
1348
1349    // TODO(edisonn): check type
1350    SkPdfObject* skobj = NULL;
1351    if (!PodofoMapper::mapXObjectDictionary(*pdfContext->fPdfDoc, obj, &skobj)) return kIgnoreError_PdfResult;
1352
1353    if (!skobj || !skobj->valid()) return kIgnoreError_PdfResult;
1354
1355    PdfResult ret = kIgnoreError_PdfResult;
1356    switch (skobj->getType())
1357    {
1358        case kObjectDictionaryXObjectDictionaryImageDictionary_SkPdfObjectType:
1359            ret = doXObject_Image(pdfContext, canvas, skobj->asImageDictionary());
1360            break;
1361        case kObjectDictionaryXObjectDictionaryType1FormDictionary_SkPdfObjectType:
1362            ret = doXObject_Form(pdfContext, canvas, obj);//skobj->asType1FormDictionary());
1363            break;
1364        //case kObjectDictionaryXObjectPS_SkPdfObjectType:
1365            //return doXObject_PS(skxobj.asPS());
1366    }
1367
1368    delete skobj;
1369    return ret;
1370}
1371
1372PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1373    pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1374    canvas->save();
1375    return kOK_PdfResult;
1376}
1377
1378PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1379    pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1380    pdfContext->fStateStack.pop();
1381    canvas->restore();
1382    return kOK_PdfResult;
1383}
1384
1385PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1386    double array[6];
1387    for (int i = 0 ; i < 6 ; i++) {
1388        array[5 - i] = pdfContext->fVarStack.top().GetReal();
1389        pdfContext->fVarStack.pop();
1390    }
1391
1392    // a b
1393    // c d
1394    // e f
1395
1396    // 0 1
1397    // 2 3
1398    // 4 5
1399
1400    // sx ky
1401    // kx sy
1402    // tx ty
1403    SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1404
1405    pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1406
1407#ifdef PDF_TRACE
1408    printf("cm ");
1409    for (int i = 0 ; i < 6 ; i++) {
1410        printf("%f ", array[i]);
1411    }
1412    printf("\n");
1413    SkTraceMatrix(pdfContext->fGraphicsState.fMatrix);
1414#endif
1415
1416    return kOK_PdfResult;
1417}
1418
1419//leading TL Set the text leading, Tl
1420//, to leading, which is a number expressed in unscaled text
1421//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
1422PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1423    double ty = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1424
1425    pdfContext->fGraphicsState.fTextLeading = ty;
1426
1427    return kOK_PdfResult;
1428}
1429
1430PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1431    double ty = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1432    double tx = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1433
1434    double array[6] = {1, 0, 0, 1, tx, ty};
1435    SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1436
1437    pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1438    pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1439
1440    return kPartial_PdfResult;
1441}
1442
1443PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1444    double ty = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1445    double tx = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1446
1447    PdfVariant _ty(-ty);
1448    pdfContext->fVarStack.push(_ty);
1449    PdfOp_TL(pdfContext, canvas, looper);
1450
1451    PdfVariant vtx(tx);
1452    PdfVariant vty(ty);
1453    pdfContext->fVarStack.push(vtx);
1454    pdfContext->fVarStack.push(vty);
1455    return PdfOp_Td(pdfContext, canvas, looper);
1456}
1457
1458PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1459    double f = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1460    double e = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1461    double d = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1462    double c = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1463    double b = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1464    double a = pdfContext->fVarStack.top().GetReal(); pdfContext->fVarStack.pop();
1465
1466    double array[6];
1467    array[0] = a;
1468    array[1] = b;
1469    array[2] = c;
1470    array[3] = d;
1471    array[4] = e;
1472    array[5] = f;
1473
1474    SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1475    matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
1476
1477    // TODO(edisonn): Text positioning.
1478    pdfContext->fGraphicsState.fMatrixTm = matrix;
1479    pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1480
1481    return kPartial_PdfResult;
1482}
1483
1484//— T* Move to the start of the next line. This operator has the same effect as the code
1485//0 Tl Td
1486//where Tl is the current leading parameter in the text state
1487PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1488    PdfVariant zero(0.0);
1489    PdfVariant tl(pdfContext->fGraphicsState.fTextLeading);
1490
1491    pdfContext->fVarStack.push(zero);
1492    pdfContext->fVarStack.push(tl);
1493    return PdfOp_Td(pdfContext, canvas, looper);
1494}
1495
1496PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1497    if (pdfContext->fGraphicsState.fPathClosed) {
1498        pdfContext->fGraphicsState.fPath.reset();
1499        pdfContext->fGraphicsState.fPathClosed = false;
1500    }
1501
1502    pdfContext->fGraphicsState.fCurPosY = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1503    pdfContext->fGraphicsState.fCurPosX = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1504
1505    pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1506                                          SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1507
1508    return kOK_PdfResult;
1509}
1510
1511PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1512    if (pdfContext->fGraphicsState.fPathClosed) {
1513        pdfContext->fGraphicsState.fPath.reset();
1514        pdfContext->fGraphicsState.fPathClosed = false;
1515    }
1516
1517    pdfContext->fGraphicsState.fCurPosY = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1518    pdfContext->fGraphicsState.fCurPosX = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1519
1520    pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1521                                          SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1522
1523    return kOK_PdfResult;
1524}
1525
1526PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1527    if (pdfContext->fGraphicsState.fPathClosed) {
1528        pdfContext->fGraphicsState.fPath.reset();
1529        pdfContext->fGraphicsState.fPathClosed = false;
1530    }
1531
1532    double y3 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1533    double x3 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1534    double y2 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1535    double x2 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1536    double y1 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1537    double x1 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1538
1539    pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1540                                            SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1541                                            SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1542
1543    pdfContext->fGraphicsState.fCurPosX = x3;
1544    pdfContext->fGraphicsState.fCurPosY = y3;
1545
1546    return kOK_PdfResult;
1547}
1548
1549PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1550    if (pdfContext->fGraphicsState.fPathClosed) {
1551        pdfContext->fGraphicsState.fPath.reset();
1552        pdfContext->fGraphicsState.fPathClosed = false;
1553    }
1554
1555    double y3 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1556    double x3 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1557    double y2 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1558    double x2 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1559    double y1 = pdfContext->fGraphicsState.fCurPosY;
1560    double x1 = pdfContext->fGraphicsState.fCurPosX;
1561
1562    pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1563                                            SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1564                                            SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1565
1566    pdfContext->fGraphicsState.fCurPosX = x3;
1567    pdfContext->fGraphicsState.fCurPosY = y3;
1568
1569    return kOK_PdfResult;
1570}
1571
1572PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1573    if (pdfContext->fGraphicsState.fPathClosed) {
1574        pdfContext->fGraphicsState.fPath.reset();
1575        pdfContext->fGraphicsState.fPathClosed = false;
1576    }
1577
1578    double y3 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1579    double x3 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1580    double y2 = pdfContext->fGraphicsState.fCurPosY;
1581    double x2 = pdfContext->fGraphicsState.fCurPosX;
1582    double y1 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1583    double x1 = pdfContext->fVarStack.top().GetReal();    pdfContext->fVarStack.pop();
1584
1585    pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1586                                            SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1587                                            SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1588
1589    pdfContext->fGraphicsState.fCurPosX = x3;
1590    pdfContext->fGraphicsState.fCurPosY = y3;
1591
1592    return kOK_PdfResult;
1593}
1594
1595PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1596    if (pdfContext->fGraphicsState.fPathClosed) {
1597        pdfContext->fGraphicsState.fPath.reset();
1598        pdfContext->fGraphicsState.fPathClosed = false;
1599    }
1600
1601    double height = pdfContext->fVarStack.top().GetReal();      pdfContext->fVarStack.pop();
1602    double width = pdfContext->fVarStack.top().GetReal();       pdfContext->fVarStack.pop();
1603    double y = pdfContext->fVarStack.top().GetReal();           pdfContext->fVarStack.pop();
1604    double x = pdfContext->fVarStack.top().GetReal();           pdfContext->fVarStack.pop();
1605
1606    pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1607                                           SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1608
1609    pdfContext->fGraphicsState.fCurPosX = x;
1610    pdfContext->fGraphicsState.fCurPosY = y + height;
1611
1612    return kOK_PdfResult;
1613}
1614
1615PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1616    pdfContext->fGraphicsState.fPath.close();
1617    pdfContext->fGraphicsState.fPathClosed = true;
1618    return kOK_PdfResult;
1619}
1620
1621PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
1622    SkPath path = pdfContext->fGraphicsState.fPath;
1623
1624    if (close) {
1625        path.close();
1626    }
1627
1628    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1629
1630    SkPaint paint;
1631
1632    // TODO(edisonn): get this from pdfContext->options,
1633    // or pdfContext->addPaintOptions(&paint);
1634    paint.setAntiAlias(true);
1635
1636    // TODO(edisonn): dashing, miter, ...
1637
1638//    path.transform(pdfContext->fGraphicsState.fMatrix);
1639//    path.transform(pdfContext->fOriginalMatrix);
1640
1641    SkPoint line[2];
1642    if (fill && !stroke && path.isLine(line)) {
1643        paint.setStyle(SkPaint::kStroke_Style);
1644        paint.setColor(pdfContext->fGraphicsState.fNonStroking.fColor);
1645        paint.setStrokeWidth(SkDoubleToScalar(0));
1646        canvas->drawPath(path, paint);
1647    } else {
1648        if (fill) {
1649            paint.setStyle(SkPaint::kFill_Style);
1650            if (evenOdd) {
1651                path.setFillType(SkPath::kEvenOdd_FillType);
1652            }
1653            paint.setColor(pdfContext->fGraphicsState.fNonStroking.fColor);
1654            canvas->drawPath(path, paint);
1655        }
1656
1657        if (stroke) {
1658            paint.setStyle(SkPaint::kStroke_Style);
1659            paint.setColor(pdfContext->fGraphicsState.fStroking.fColor);
1660            paint.setStrokeWidth(SkDoubleToScalar(pdfContext->fGraphicsState.fLineWidth));
1661            path.setFillType(SkPath::kWinding_FillType);  // reset it, just in case it messes up the stroke
1662            canvas->drawPath(path, paint);
1663        }
1664    }
1665
1666    pdfContext->fGraphicsState.fPath.reset();
1667    // todo zoom ... other stuff ?
1668
1669    if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1670#ifndef PDF_DEBUG_NO_CLIPING
1671        canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1672#endif
1673    }
1674
1675    //pdfContext->fGraphicsState.fClipPath.reset();
1676    pdfContext->fGraphicsState.fHasClipPathToApply = false;
1677
1678    return kPartial_PdfResult;
1679
1680}
1681
1682PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1683    return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1684}
1685
1686PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1687    return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1688}
1689
1690PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1691    return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1692}
1693
1694PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1695    return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1696}
1697
1698PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1699    return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1700}
1701
1702PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1703    return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1704}
1705
1706PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1707    return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1708}
1709
1710PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1711    return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1712}
1713
1714PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1715    return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1716}
1717
1718PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1719    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1720    if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1721#ifndef PDF_DEBUG_NO_CLIPING
1722        canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1723#endif
1724    }
1725
1726    //pdfContext->fGraphicsState.fClipPath.reset();
1727    pdfContext->fGraphicsState.fHasClipPathToApply = false;
1728
1729    pdfContext->fGraphicsState.fPathClosed = true;
1730
1731    return kOK_PdfResult;
1732}
1733
1734PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1735    pdfContext->fGraphicsState.fTextBlock   = true;
1736    pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1737    pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1738
1739    return kPartial_PdfResult;
1740}
1741
1742PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1743    if (!pdfContext->fGraphicsState.fTextBlock) {
1744        return kIgnoreError_PdfResult;
1745    }
1746    // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1747    return kPartial_PdfResult;
1748}
1749
1750//font size Tf Set the text font, Tf
1751//, to font and the text font size, Tfs, to size. font is the name of a
1752//font resource in the Fontsubdictionary of the current resource dictionary; size is
1753//a number representing a scale factor. There is no initial value for either font or
1754//size; they must be specified explicitly using Tf before any text is shown.
1755PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1756    pdfContext->fGraphicsState.fCurFontSize = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1757    PdfName fontName = pdfContext->fVarStack.top().GetName();                           pdfContext->fVarStack.pop();
1758
1759    // TODO(edisonn): Load font from pdfContext->fGraphicsState.fObjectWithResources ?
1760    PdfObject* pFont = pdfContext->fPdfPage->GetFromResources( PdfName("Font"), fontName );
1761    if( !pFont )
1762    {
1763        // TODO(edisonn): try to ignore the error, make sure we do not crash.
1764        return kIgnoreError_PdfResult;
1765    }
1766
1767    pdfContext->fGraphicsState.fCurFont = pdfContext->fPdfDoc->GetFont( pFont );
1768    if( !pdfContext->fGraphicsState.fCurFont )
1769    {
1770        // TODO(edisonn): check ~/crasing, for one of the files PoDoFo throws exception
1771        // when calling pFont->Reference(), with Linked list corruption.
1772        return kIgnoreError_PdfResult;
1773    }
1774
1775    return kPartial_PdfResult;
1776}
1777
1778PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1779    if (!pdfContext->fGraphicsState.fTextBlock) {
1780        // TODO(edisonn): try to recover and draw it any way?
1781        return kIgnoreError_PdfResult;
1782    }
1783
1784    PdfResult ret = DrawText(pdfContext,
1785                             pdfContext->fGraphicsState.fCurFont,
1786                             pdfContext->fVarStack.top().GetString(),
1787                             canvas);
1788    pdfContext->fVarStack.pop();
1789
1790    return ret;
1791}
1792
1793PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1794    if (!pdfContext->fGraphicsState.fTextBlock) {
1795        // TODO(edisonn): try to recover and draw it any way?
1796        return kIgnoreError_PdfResult;
1797    }
1798
1799    PdfOp_T_star(pdfContext, canvas, looper);
1800    // Do not pop, and push, just transfer the param to Tj
1801    return PdfOp_Tj(pdfContext, canvas, looper);
1802}
1803
1804PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1805    if (!pdfContext->fGraphicsState.fTextBlock) {
1806        // TODO(edisonn): try to recover and draw it any way?
1807        return kIgnoreError_PdfResult;
1808    }
1809
1810    PdfVariant str = pdfContext->fVarStack.top();       pdfContext->fVarStack.pop();
1811    PdfVariant ac = pdfContext->fVarStack.top();        pdfContext->fVarStack.pop();
1812    PdfVariant aw = pdfContext->fVarStack.top();        pdfContext->fVarStack.pop();
1813
1814    pdfContext->fVarStack.push(aw);
1815    PdfOp_Tw(pdfContext, canvas, looper);
1816
1817    pdfContext->fVarStack.push(ac);
1818    PdfOp_Tc(pdfContext, canvas, looper);
1819
1820    pdfContext->fVarStack.push(str);
1821    PdfOp_quote(pdfContext, canvas, looper);
1822
1823    return kPartial_PdfResult;
1824}
1825
1826PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1827    if (!pdfContext->fGraphicsState.fTextBlock) {
1828        // TODO(edisonn): try to recover and draw it any way?
1829        return kIgnoreError_PdfResult;
1830    }
1831
1832    PdfArray array = pdfContext->fVarStack.top().GetArray();
1833    pdfContext->fVarStack.pop();
1834
1835    for( int i=0; i<static_cast<int>(array.GetSize()); i++ )
1836    {
1837        if( array[i].IsString() || array[i].IsHexString() ) {
1838            DrawText(pdfContext,
1839                           pdfContext->fGraphicsState.fCurFont,
1840                           array[i].GetString(),
1841                           canvas);
1842        } else if (array[i].IsReal() || array[i].IsNumber()) {
1843            double dx = array[i].GetReal();
1844            SkMatrix matrix;
1845            matrix.setAll(SkDoubleToScalar(1),
1846                          SkDoubleToScalar(0),
1847                          // TODO(edisonn): use writing mode, vertical/horizontal.
1848                          SkDoubleToScalar(-dx),  // amount is substracted!!!
1849                          SkDoubleToScalar(0),
1850                          SkDoubleToScalar(1),
1851                          SkDoubleToScalar(0),
1852                          SkDoubleToScalar(0),
1853                          SkDoubleToScalar(0),
1854                          SkDoubleToScalar(1));
1855
1856            pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1857        }
1858    }
1859    return kPartial_PdfResult;  // TODO(edisonn): Implement fully DrawText before returing OK.
1860}
1861
1862PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1863    colorOperator->fColorSpace = pdfContext->fVarStack.top().GetName().GetName();    pdfContext->fVarStack.pop();
1864    return kOK_PdfResult;
1865}
1866
1867PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1868    return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1869}
1870
1871PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1872    return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1873}
1874
1875PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1876    double c[4];
1877    pdf_int64 v[4];
1878
1879    int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1880
1881    bool doubles = true;
1882    if (colorOperator->fColorSpace == "Indexed") {
1883        doubles = false;
1884    }
1885
1886#ifdef PDF_TRACE
1887    printf("color space = %s, N = %i\n", colorOperator->fColorSpace.c_str(), n);
1888#endif
1889
1890    for (int i = n - 1; i >= 0 ; i--) {
1891        if (doubles) {
1892            c[i] = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1893        } else {
1894            v[i] = pdfContext->fVarStack.top().GetNumber();   pdfContext->fVarStack.pop();
1895        }
1896    }
1897
1898    // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
1899    if (colorOperator->fColorSpace == "DeviceRGB") {
1900        colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
1901    }
1902    return kPartial_PdfResult;
1903}
1904
1905PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1906    return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1907}
1908
1909PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1910    return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1911}
1912
1913PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1914    PdfString name;
1915
1916    if (pdfContext->fVarStack.top().IsName()) {
1917        pdfContext->fVarStack.pop();
1918    }
1919
1920    // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1921    PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1922
1923    return kPartial_PdfResult;
1924}
1925
1926PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1927    return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1928}
1929
1930PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1931    return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1932}
1933
1934PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1935    double gray = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1936    return kNYI_PdfResult;
1937}
1938
1939PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1940    return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1941}
1942
1943PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1944    return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1945}
1946
1947PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1948    double b = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1949    double g = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1950    double r = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1951
1952    colorOperator->fColorSpace = "DeviceRGB";
1953    colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
1954    return kOK_PdfResult;
1955}
1956
1957PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1958    return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1959}
1960
1961PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1962    return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1963}
1964
1965PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1966    // TODO(edisonn): spec has some rules about overprint, implement them.
1967    double k = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1968    double y = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1969    double m = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1970    double c = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
1971
1972    colorOperator->fColorSpace = "DeviceCMYK";
1973    // TODO(edisonn): Set color.
1974    return kNYI_PdfResult;
1975}
1976
1977PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1978    return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1979}
1980
1981PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1982    return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1983}
1984
1985PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1986    pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1987    pdfContext->fGraphicsState.fHasClipPathToApply = true;
1988
1989    return kOK_PdfResult;
1990}
1991
1992PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1993    pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1994
1995#ifdef PDF_TRACE
1996    if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
1997        printf("CLIP IS RECT\n");
1998    }
1999#endif
2000
2001    // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
2002    pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
2003    pdfContext->fGraphicsState.fHasClipPathToApply = true;
2004
2005    return kPartial_PdfResult;
2006}
2007
2008PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2009    *looper = new PdfCompatibilitySectionLooper();
2010    return kOK_PdfResult;
2011}
2012
2013PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2014#ifdef ASSERT_BAD_PDF_OPS
2015    SkASSERT(false);  // EX must be consumed by PdfCompatibilitySectionLooper, but let's
2016                      // have the assert when testing good pdfs.
2017#endif
2018    return kIgnoreError_PdfResult;
2019}
2020
2021PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2022    *looper = new PdfInlineImageLooper();
2023    return kOK_PdfResult;
2024}
2025
2026PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2027#ifdef ASSERT_BAD_PDF_OPS
2028    SkASSERT(false);  // must be processed in inline image looper, but let's
2029                      // have the assert when testing good pdfs.
2030#endif
2031    return kIgnoreError_PdfResult;
2032}
2033
2034PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2035#ifdef ASSERT_BAD_PDF_OPS
2036    SkASSERT(false);  // must be processed in inline image looper, but let's
2037                      // have the assert when testing good pdfs.
2038#endif
2039    return kIgnoreError_PdfResult;
2040}
2041
2042//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
2043PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2044    double lineWidth = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2045    pdfContext->fGraphicsState.fLineWidth = lineWidth;
2046
2047    return kOK_PdfResult;
2048}
2049
2050//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
2051PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2052    pdfContext->fVarStack.pop();
2053    //double lineCap = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2054
2055    return kNYI_PdfResult;
2056}
2057
2058//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
2059PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2060    pdfContext->fVarStack.pop();
2061    //double lineJoin = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2062
2063    return kNYI_PdfResult;
2064}
2065
2066//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
2067PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2068    pdfContext->fVarStack.pop();
2069    //double miterLimit = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2070
2071    return kNYI_PdfResult;
2072}
2073
2074//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
2075//page 155).
2076PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2077    pdfContext->fVarStack.pop();
2078    pdfContext->fVarStack.pop();
2079
2080    return kNYI_PdfResult;
2081}
2082
2083//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
2084PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2085    pdfContext->fVarStack.pop();
2086
2087    return kNYI_PdfResult;
2088}
2089
2090//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
2091//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
2092//fies the output device’s default flatness tolerance.
2093PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2094    pdfContext->fVarStack.pop();
2095
2096    return kNYI_PdfResult;
2097}
2098
2099//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2100//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
2101PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2102    PdfName name = pdfContext->fVarStack.top().GetName();    pdfContext->fVarStack.pop();
2103
2104#ifdef PDF_TRACE
2105    std::string str;
2106#endif
2107
2108    const PdfDictionary& pageDict = pdfContext->fGraphicsState.fObjectWithResources->GetDictionary();
2109
2110#ifdef PDF_TRACE
2111    pdfContext->fGraphicsState.fObjectWithResources->ToString(str);
2112    printf("Print Object with resources: %s\n", str.c_str());
2113#endif
2114
2115    const PdfObject* resources = resolveReferenceObject(pdfContext->fPdfDoc,
2116                                                        pageDict.GetKey("Resources"));
2117
2118    if (resources == NULL) {
2119#ifdef PDF_TRACE
2120        printf("WARNING: No Resources for a page with 'gs' operator!\n");
2121#endif
2122        return kIgnoreError_PdfResult;
2123    }
2124
2125#ifdef PDF_TRACE
2126    resources->ToString(str);
2127    printf("Print gs Page Resources: %s\n", str.c_str());
2128#endif
2129
2130    if (!resources->IsDictionary()) {
2131#ifdef PDF_TRACE
2132        printf("Resources is not a dictionary!\n");
2133#endif
2134        return kIgnoreError_PdfResult;
2135    }
2136
2137    const PdfDictionary& resourceDict = resources->GetDictionary();
2138    //Next, get the ExtGState Dictionary from the Resource Dictionary:
2139    const PdfObject* extGStateDictionary = resolveReferenceObject(pdfContext->fPdfDoc,
2140                                                                resourceDict.GetKey("ExtGState"));
2141
2142    if (extGStateDictionary == NULL) {
2143#ifdef PDF_TRACE
2144        printf("ExtGState is NULL!\n");
2145#endif
2146        return kIgnoreError_PdfResult;
2147    }
2148
2149    if (!extGStateDictionary->IsDictionary()) {
2150#ifdef PDF_TRACE
2151        printf("extGStateDictionary is not a dictionary!\n");
2152#endif
2153        return kIgnoreError_PdfResult;
2154    }
2155
2156    const PdfObject* value =
2157            resolveReferenceObject(pdfContext->fPdfDoc,
2158                                   extGStateDictionary->GetDictionary().GetKey(name));
2159
2160    if (value == NULL) {
2161#ifdef PDF_TRACE
2162        printf("Named object not found!\n");
2163#endif
2164        return kIgnoreError_PdfResult;
2165    }
2166
2167#ifdef PDF_TRACE
2168    value->ToString(str);
2169    printf("gs object value: %s\n", str.c_str());
2170#endif
2171
2172    // TODO(edisonn): now load all those properties in graphic state.
2173
2174    return kNYI_PdfResult;
2175}
2176
2177//charSpace Tc Set the character spacing, Tc
2178//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2179//Initial value: 0.
2180PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2181    double charSpace = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2182    pdfContext->fGraphicsState.fCharSpace = charSpace;
2183
2184    return kOK_PdfResult;
2185}
2186
2187//wordSpace Tw Set the word spacing, T
2188//w
2189//, to wordSpace, which is a number expressed in unscaled
2190//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2191//value: 0.
2192PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2193    double wordSpace = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2194    pdfContext->fGraphicsState.fWordSpace = wordSpace;
2195
2196    return kOK_PdfResult;
2197}
2198
2199//scale Tz Set the horizontal scaling, Th
2200//, to (scale ˜ 100). scale is a number specifying the
2201//percentage of the normal width. Initial value: 100 (normal width).
2202PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2203    double scale = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2204
2205    return kNYI_PdfResult;
2206}
2207
2208//render Tr Set the text rendering mode, T
2209//mode, to render, which is an integer. Initial value: 0.
2210PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2211    double render = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2212
2213    return kNYI_PdfResult;
2214}
2215
2216//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2217//units. Initial value: 0.
2218PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2219    double rise = pdfContext->fVarStack.top().GetReal();     pdfContext->fVarStack.pop();
2220
2221    return kNYI_PdfResult;
2222}
2223
2224//wx wy d0
2225PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2226    pdfContext->fVarStack.pop();
2227    pdfContext->fVarStack.pop();
2228
2229    return kNYI_PdfResult;
2230}
2231
2232//wx wy llx lly urx ury d1
2233PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2234    pdfContext->fVarStack.pop();
2235    pdfContext->fVarStack.pop();
2236    pdfContext->fVarStack.pop();
2237    pdfContext->fVarStack.pop();
2238    pdfContext->fVarStack.pop();
2239    pdfContext->fVarStack.pop();
2240
2241    return kNYI_PdfResult;
2242}
2243
2244//name sh
2245PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2246    pdfContext->fVarStack.pop();
2247
2248    return kNYI_PdfResult;
2249}
2250
2251//name Do
2252PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2253    PdfName name = pdfContext->fVarStack.top().GetName();    pdfContext->fVarStack.pop();
2254
2255    const PdfDictionary& pageDict = pdfContext->fGraphicsState.fObjectWithResources->GetDictionary();
2256    const PdfObject* resources = resolveReferenceObject(pdfContext->fPdfDoc,
2257                                                        pageDict.GetKey("Resources"));
2258
2259    if (resources == NULL) {
2260#ifdef PDF_TRACE
2261        printf("WARNING: No Resources for a page with 'Do' operator!s\n");
2262#endif
2263        return kIgnoreError_PdfResult;
2264    }
2265
2266#ifdef PDF_TRACE
2267    std::string str;
2268    resources->ToString(str);
2269    printf("Print Do Page Resources: %s\n", str.c_str());
2270#endif
2271
2272    if (!resources->IsDictionary()) {
2273#ifdef PDF_TRACE
2274        printf("Resources is not a dictionary!\n");
2275#endif
2276        return kIgnoreError_PdfResult;
2277    }
2278
2279    const PdfDictionary& resourceDict = resources->GetDictionary();
2280    //Next, get the XObject Dictionary from the Resource Dictionary:
2281    const PdfObject* xObjectDictionary = resolveReferenceObject(pdfContext->fPdfDoc,
2282                                                                resourceDict.GetKey("XObject"));
2283
2284    if (xObjectDictionary == NULL) {
2285#ifdef PDF_TRACE
2286        printf("XObject is NULL!\n");
2287#endif
2288        return kIgnoreError_PdfResult;
2289    }
2290
2291    if (!xObjectDictionary->IsDictionary()) {
2292#ifdef PDF_TRACE
2293        printf("xObjectDictionary is not a dictionary!\n");
2294#endif
2295        return kIgnoreError_PdfResult;
2296    }
2297
2298    const PdfObject* value =
2299            resolveReferenceObject(pdfContext->fPdfDoc,
2300                                   xObjectDictionary->GetDictionary().GetKey(name));
2301
2302    if (value == NULL) {
2303#ifdef PDF_TRACE
2304        printf("Named object not found!\n");
2305#endif
2306        return kIgnoreError_PdfResult;
2307    }
2308
2309#ifdef PDF_TRACE
2310    value->ToString(str);
2311    printf("Do object value: %s\n", str.c_str());
2312#endif
2313
2314    return doXObject(pdfContext, canvas, *value);
2315}
2316
2317
2318//tag MP Designate a marked-content point. tag is a name object indicating the role or
2319//significance of the point.
2320PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2321    pdfContext->fVarStack.pop();
2322
2323    return kNYI_PdfResult;
2324}
2325
2326//tag properties DP Designate a marked-content point with an associated property list. tag is a
2327//name object indicating the role or significance of the point; properties is
2328//either an inline dictionary containing the property list or a name object
2329//associated with it in the Properties subdictionary of the current resource
2330//dictionary (see Section 9.5.1, “Property Lists”).
2331PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2332    pdfContext->fVarStack.pop();
2333    pdfContext->fVarStack.pop();
2334
2335    return kNYI_PdfResult;
2336}
2337
2338//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2339//tag is a name object indicating the role or significance of the sequence.
2340PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2341    pdfContext->fVarStack.pop();
2342
2343    return kNYI_PdfResult;
2344}
2345
2346//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2347//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2348//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”).
2349PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2350    pdfContext->fVarStack.pop();
2351    pdfContext->fVarStack.pop();
2352
2353    return kNYI_PdfResult;
2354}
2355
2356//— EMC End a marked-content sequence begun by a BMC or BDC operator.
2357PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2358    return kNYI_PdfResult;
2359}
2360
2361void initPdfOperatorRenderes() {
2362    static bool gInitialized = false;
2363    if (gInitialized) {
2364        return;
2365    }
2366
2367    gPdfOps["q"] =      PdfOp_q;
2368    gPdfOps["Q"] =      PdfOp_Q;
2369    gPdfOps["cm"] =     PdfOp_cm;
2370
2371    gPdfOps["TD"] =     PdfOp_TD;
2372    gPdfOps["Td"] =     PdfOp_Td;
2373    gPdfOps["Tm"] =     PdfOp_Tm;
2374    gPdfOps["T*"] =     PdfOp_T_star;
2375
2376    gPdfOps["m"] =      PdfOp_m;
2377    gPdfOps["l"] =      PdfOp_l;
2378    gPdfOps["c"] =      PdfOp_c;
2379    gPdfOps["v"] =      PdfOp_v;
2380    gPdfOps["y"] =      PdfOp_y;
2381    gPdfOps["h"] =      PdfOp_h;
2382    gPdfOps["re"] =     PdfOp_re;
2383
2384    gPdfOps["S"] =      PdfOp_S;
2385    gPdfOps["s"] =      PdfOp_s;
2386    gPdfOps["f"] =      PdfOp_f;
2387    gPdfOps["F"] =      PdfOp_F;
2388    gPdfOps["f*"] =     PdfOp_f_star;
2389    gPdfOps["B"] =      PdfOp_B;
2390    gPdfOps["B*"] =     PdfOp_B_star;
2391    gPdfOps["b"] =      PdfOp_b;
2392    gPdfOps["b*"] =     PdfOp_b_star;
2393    gPdfOps["n"] =      PdfOp_n;
2394
2395    gPdfOps["BT"] =     PdfOp_BT;
2396    gPdfOps["ET"] =     PdfOp_ET;
2397
2398    gPdfOps["Tj"] =     PdfOp_Tj;
2399    gPdfOps["'"] =      PdfOp_quote;
2400    gPdfOps["\""] =     PdfOp_doublequote;
2401    gPdfOps["TJ"] =     PdfOp_TJ;
2402
2403    gPdfOps["CS"] =     PdfOp_CS;
2404    gPdfOps["cs"] =     PdfOp_cs;
2405    gPdfOps["SC"] =     PdfOp_SC;
2406    gPdfOps["SCN"] =    PdfOp_SCN;
2407    gPdfOps["sc"] =     PdfOp_sc;
2408    gPdfOps["scn"] =    PdfOp_scn;
2409    gPdfOps["G"] =      PdfOp_G;
2410    gPdfOps["g"] =      PdfOp_g;
2411    gPdfOps["RG"] =     PdfOp_RG;
2412    gPdfOps["rg"] =     PdfOp_rg;
2413    gPdfOps["K"] =      PdfOp_K;
2414    gPdfOps["k"] =      PdfOp_k;
2415
2416    gPdfOps["W"] =      PdfOp_W;
2417    gPdfOps["W*"] =     PdfOp_W_star;
2418
2419    gPdfOps["BX"] =     PdfOp_BX;
2420    gPdfOps["EX"] =     PdfOp_EX;
2421
2422    gPdfOps["BI"] =     PdfOp_BI;
2423    gPdfOps["ID"] =     PdfOp_ID;
2424    gPdfOps["EI"] =     PdfOp_EI;
2425
2426    gPdfOps["w"] =      PdfOp_w;
2427    gPdfOps["J"] =      PdfOp_J;
2428    gPdfOps["j"] =      PdfOp_j;
2429    gPdfOps["M"] =      PdfOp_M;
2430    gPdfOps["d"] =      PdfOp_d;
2431    gPdfOps["ri"] =     PdfOp_ri;
2432    gPdfOps["i"] =      PdfOp_i;
2433    gPdfOps["gs"] =     PdfOp_gs;
2434
2435    gPdfOps["Tc"] =     PdfOp_Tc;
2436    gPdfOps["Tw"] =     PdfOp_Tw;
2437    gPdfOps["Tz"] =     PdfOp_Tz;
2438    gPdfOps["TL"] =     PdfOp_TL;
2439    gPdfOps["Tf"] =     PdfOp_Tf;
2440    gPdfOps["Tr"] =     PdfOp_Tr;
2441    gPdfOps["Ts"] =     PdfOp_Ts;
2442
2443    gPdfOps["d0"] =     PdfOp_d0;
2444    gPdfOps["d1"] =     PdfOp_d1;
2445
2446    gPdfOps["sh"] =     PdfOp_sh;
2447
2448    gPdfOps["Do"] =     PdfOp_Do;
2449
2450    gPdfOps["MP"] =     PdfOp_MP;
2451    gPdfOps["DP"] =     PdfOp_DP;
2452    gPdfOps["BMC"] =    PdfOp_BMC;
2453    gPdfOps["BDC"] =    PdfOp_BDC;
2454    gPdfOps["EMC"] =    PdfOp_EMC;
2455
2456    gInitialized = true;
2457}
2458
2459void reportPdfRenderStats() {
2460    std::map<std::string, int>::iterator iter;
2461
2462    for (int i = 0 ; i < kCount_PdfResult; i++) {
2463        for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++iter) {
2464            printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_str(), iter->second);
2465        }
2466    }
2467}
2468
2469PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
2470    if( token.eType == ePdfContentsType_Keyword )
2471    {
2472        // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
2473#ifdef PDF_TRACE
2474        printf("KEYWORD: %s\n", token.pszToken);
2475#endif
2476        PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[token.pszToken];
2477        if (pdfOperatorRenderer) {
2478            // caller, main work is done by pdfOperatorRenderer(...)
2479            PdfTokenLooper* childLooper = NULL;
2480            gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)][token.pszToken]++;
2481
2482            if (childLooper) {
2483                childLooper->setUp(this);
2484                childLooper->loop();
2485                delete childLooper;
2486            }
2487        } else {
2488            gRenderStats[kUnsupported_PdfResult][token.pszToken]++;
2489        }
2490    }
2491    else if ( token.eType == ePdfContentsType_Variant )
2492    {
2493#ifdef PDF_TRACE
2494        std::string _var;
2495        token.var.ToString(_var);
2496        printf("var: %s\n", _var.c_str());
2497#endif
2498        fPdfContext->fVarStack.push( token.var );
2499    }
2500    else if ( token.eType == ePdfContentsType_ImageData) {
2501        // TODO(edisonn): implement inline image.
2502    }
2503    else {
2504        return kIgnoreError_PdfResult;
2505    }
2506    return kOK_PdfResult;
2507}
2508
2509void PdfMainLooper::loop() {
2510    PdfToken token;
2511    while (readToken(fTokenizer, &token)) {
2512        consumeToken(token);
2513    }
2514}
2515
2516PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
2517    //pdfContext.fInlineImage.fKeyValuePairs[key] = value;
2518    return kNYI_PdfResult;
2519}
2520
2521void PdfInlineImageLooper::loop() {
2522    PdfToken token;
2523    while (readToken(fTokenizer, &token)) {
2524        if (token.eType == ePdfContentsType_Keyword && strcmp(token.pszToken, "BX") == 0) {
2525            PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2526            looper->setUp(this);
2527            looper->loop();
2528        } else {
2529            if (token.eType == ePdfContentsType_Keyword && strcmp(token.pszToken, "EI") == 0) {
2530                done();
2531                return;
2532            }
2533
2534            consumeToken(token);
2535        }
2536    }
2537    // TODO(edisonn): report error/warning, EOF without EI.
2538}
2539
2540PdfResult PdfInlineImageLooper::done() {
2541
2542    // TODO(edisonn): long to short names
2543    // TODO(edisonn): set properties in a map
2544    // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
2545    // the stream.
2546
2547    SkBitmap bitmap;
2548    setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
2549
2550    // TODO(edisonn): matrix use.
2551    // Draw dummy red square, to show the prezence of the inline image.
2552    fCanvas->drawBitmap(bitmap,
2553                       SkDoubleToScalar(0),
2554                       SkDoubleToScalar(0),
2555                       NULL);
2556    return kNYI_PdfResult;
2557}
2558
2559PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2560    return fParent->consumeToken(token);
2561}
2562
2563void PdfCompatibilitySectionLooper::loop() {
2564    // TODO(edisonn): save stacks position, or create a new stack?
2565    // TODO(edisonn): what happens if we pop out more variables then when we started?
2566    // restore them? fail? We could create a new operands stack for every new BX/EX section,
2567    // pop-ing too much will not affect outside the section.
2568    PdfToken token;
2569    while (readToken(fTokenizer, &token)) {
2570        if (token.eType == ePdfContentsType_Keyword && strcmp(token.pszToken, "BX") == 0) {
2571            PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2572            looper->setUp(this);
2573            looper->loop();
2574            delete looper;
2575        } else {
2576            if (token.eType == ePdfContentsType_Keyword && strcmp(token.pszToken, "EX") == 0) break;
2577            fParent->consumeToken(token);
2578        }
2579    }
2580    // TODO(edisonn): restore stack.
2581}
2582
2583// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2584// TODO(edisonn): Add API for Forms viewing and editing
2585// e.g. SkBitmap getPage(int page);
2586//      int formsCount();
2587//      SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2588// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2589// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2590// resources needed, so the preview is fast.
2591// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2592// references automatically.
2593class SkPdfViewer : public SkRefCnt {
2594public:
2595
2596  bool load(const SkString inputFileName, SkPicture* out) {
2597
2598    initPdfOperatorRenderes();
2599
2600    try
2601    {
2602        std::cout << "Init: " << inputFileName.c_str() << std::endl;
2603
2604        PdfMemDocument doc(inputFileName.c_str());
2605        if( !doc.GetPageCount() )
2606        {
2607            std::cout << "ERROR: Empty Document" << inputFileName.c_str() << std::endl;
2608            return false;
2609        } else {
2610
2611            for (int pn = 0; pn < doc.GetPageCount(); ++pn) {
2612                PoDoFo::PdfPage* page = doc.GetPage(pn);
2613                PdfRect rect = page->GetMediaBox();
2614#ifdef PDF_TRACE
2615                printf("Page Width: %f, Page Height: %f\n", rect.GetWidth(), rect.GetHeight());
2616#endif
2617
2618                // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to use?
2619
2620                SkBitmap bitmap;
2621#ifdef PDF_DEBUG_3X
2622                setup_bitmap(&bitmap, 3*rect.GetWidth(), 3*rect.GetHeight());
2623#else
2624                setup_bitmap(&bitmap, rect.GetWidth(), rect.GetHeight());
2625#endif
2626                SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
2627                SkCanvas canvas(device);
2628
2629
2630                const char*      pszToken = NULL;
2631                PdfVariant       var;
2632                EPdfContentsType eType;
2633
2634                PdfContentsTokenizer tokenizer( page );
2635
2636                PdfContext pdfContext;
2637                pdfContext.fPdfPage = page;
2638                pdfContext.fPdfDoc = &doc;
2639                pdfContext.fOriginalMatrix = SkMatrix::I();
2640                pdfContext.fGraphicsState.fObjectWithResources = pdfContext.fPdfPage->GetObject();
2641
2642                gPdfContext = &pdfContext;
2643                gDumpBitmap = &bitmap;
2644                gDumpCanvas = &canvas;
2645
2646
2647                // TODO(edisonn): get matrix stuff right.
2648                // TODO(edisonn): add DPI/scale/zoom.
2649                SkScalar z = SkIntToScalar(0);
2650                SkScalar w = SkDoubleToScalar(rect.GetWidth());
2651                SkScalar h = SkDoubleToScalar(rect.GetHeight());
2652
2653                SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(w, z), SkPoint::Make(w, h), SkPoint::Make(z, h)};
2654//                SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2655
2656                // TODO(edisonn): add flag for this app to create sourunding buffer zone
2657                // TODO(edisonn): add flagg for no clipping.
2658                // Use larger image to make sure we do not draw anything outside of page
2659                // could be used in tests.
2660
2661#ifdef PDF_DEBUG_3X
2662                SkPoint skiaSpace[4] = {SkPoint::Make(w+z, h+h), SkPoint::Make(w+w, h+h), SkPoint::Make(w+w, h+z), SkPoint::Make(w+z, h+z)};
2663#else
2664                SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2665#endif
2666                //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2667                //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2668
2669                //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2670                //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2671
2672                //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2673                //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2674
2675                SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2676                SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2677
2678
2679                pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
2680                pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
2681                pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
2682
2683                canvas.setMatrix(pdfContext.fOriginalMatrix);
2684
2685#ifndef PDF_DEBUG_NO_PAGE_CLIPING
2686                canvas.clipRect(SkRect::MakeXYWH(z, z, w, h), SkRegion::kIntersect_Op, true);
2687#endif
2688
2689                PdfMainLooper looper(NULL, &tokenizer, &pdfContext, &canvas);
2690                looper.loop();
2691
2692                canvas.flush();
2693
2694                SkString out;
2695                out.appendf("%s-%i.png", inputFileName.c_str(), pn);
2696                SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
2697            }
2698            return true;
2699        }
2700    }
2701    catch( PdfError & e )
2702    {
2703        std::cout << "ERROR: PDF can't be parsed!" << inputFileName.c_str() << std::endl;
2704        return false;
2705    }
2706
2707    return true;
2708  }
2709  bool write(void*) const { return false; }
2710};
2711
2712
2713
2714/**
2715 * Given list of directories and files to use as input, expects to find .pdf
2716 * files and it will convert them to .png files writing them in the same directory
2717 * one file for each page.
2718 *
2719 * Returns zero exit code if all .pdf files were converted successfully,
2720 * otherwise returns error code 1.
2721 */
2722
2723static const char PDF_FILE_EXTENSION[] = "pdf";
2724static const char PNG_FILE_EXTENSION[] = "png";
2725
2726// TODO(edisonn): add ability to write to a new directory.
2727static void usage(const char* argv0) {
2728    SkDebugf("PDF to PNG rendering tool\n");
2729    SkDebugf("\n"
2730"Usage: \n"
2731"     %s <input>... -w <outputDir> \n"
2732, argv0);
2733    SkDebugf("\n\n");
2734    SkDebugf(
2735"     input:     A list of directories and files to use as input. Files are\n"
2736"                expected to have the .skp extension.\n\n");
2737    SkDebugf(
2738"     outputDir: directory to write the rendered pdfs.\n\n");
2739    SkDebugf("\n");
2740}
2741
2742/** Replaces the extension of a file.
2743 * @param path File name whose extension will be changed.
2744 * @param old_extension The old extension.
2745 * @param new_extension The new extension.
2746 * @returns false if the file did not has the expected extension.
2747 *  if false is returned, contents of path are undefined.
2748 */
2749static bool replace_filename_extension(SkString* path,
2750                                       const char old_extension[],
2751                                       const char new_extension[]) {
2752    if (path->endsWith(old_extension)) {
2753        path->remove(path->size() - strlen(old_extension),
2754                     strlen(old_extension));
2755        if (!path->endsWith(".")) {
2756            return false;
2757        }
2758        path->append(new_extension);
2759        return true;
2760    }
2761    return false;
2762}
2763
2764/** Builds the output filename. path = dir/name, and it replaces expected
2765 * .skp extension with .pdf extention.
2766 * @param path Output filename.
2767 * @param name The name of the file.
2768 * @returns false if the file did not has the expected extension.
2769 *  if false is returned, contents of path are undefined.
2770 */
2771static bool make_output_filepath(SkString* path, const SkString& dir,
2772                                 const SkString& name) {
2773    sk_tools::make_filepath(path, dir, name);
2774    return replace_filename_extension(path,
2775                                      PDF_FILE_EXTENSION,
2776                                      PNG_FILE_EXTENSION);
2777}
2778
2779/** Write the output of pdf renderer to a file.
2780 * @param outputDir Output dir.
2781 * @param inputFilename The skp file that was read.
2782 * @param renderer The object responsible to write the pdf file.
2783 */
2784static bool write_output(const SkString& outputDir,
2785                         const SkString& inputFilename,
2786                         const SkPdfViewer& renderer) {
2787    if (outputDir.isEmpty()) {
2788        SkDynamicMemoryWStream stream;
2789        renderer.write(&stream);
2790        return true;
2791    }
2792
2793    SkString outputPath;
2794    if (!make_output_filepath(&outputPath, outputDir, inputFilename)) {
2795        return false;
2796    }
2797
2798    SkFILEWStream stream(outputPath.c_str());
2799    if (!stream.isValid()) {
2800        SkDebugf("Could not write to file %s\n", outputPath.c_str());
2801        return false;
2802    }
2803    renderer.write(&stream);
2804
2805    return true;
2806}
2807
2808/** Reads an skp file, renders it to pdf and writes the output to a pdf file
2809 * @param inputPath The skp file to be read.
2810 * @param outputDir Output dir.
2811 * @param renderer The object responsible to render the skp object into pdf.
2812 */
2813static bool parse_pdf(const SkString& inputPath, const SkString& outputDir,
2814                       SkPdfViewer& renderer) {
2815    SkString inputFilename;
2816    sk_tools::get_basename(&inputFilename, inputPath);
2817
2818    SkFILEStream inputStream;
2819    inputStream.setPath(inputPath.c_str());
2820    if (!inputStream.isValid()) {
2821        SkDebugf("Could not open file %s\n", inputPath.c_str());
2822        return false;
2823    }
2824
2825    bool success = false;
2826
2827    success = renderer.load(inputPath, NULL);
2828
2829
2830//    success = write_output(outputDir, inputFilename, renderer);
2831
2832    //renderer.end();
2833    return success;
2834}
2835
2836/** For each file in the directory or for the file passed in input, call
2837 * parse_pdf.
2838 * @param input A directory or an pdf file.
2839 * @param outputDir Output dir.
2840 * @param renderer The object responsible to render the skp object into pdf.
2841 */
2842static int process_input(const SkString& input, const SkString& outputDir,
2843                         SkPdfViewer& renderer) {
2844    int failures = 0;
2845    if (sk_isdir(input.c_str())) {
2846        SkOSFile::Iter iter(input.c_str(), PDF_FILE_EXTENSION);
2847        SkString inputFilename;
2848        while (iter.next(&inputFilename)) {
2849            SkString inputPath;
2850            sk_tools::make_filepath(&inputPath, input, inputFilename);
2851            if (!parse_pdf(inputPath, outputDir, renderer)) {
2852                ++failures;
2853            }
2854        }
2855    } else {
2856        SkString inputPath(input);
2857        if (!parse_pdf(inputPath, outputDir, renderer)) {
2858            ++failures;
2859        }
2860    }
2861    return failures;
2862}
2863
2864static void parse_commandline(int argc, char* const argv[],
2865                              SkTArray<SkString>* inputs,
2866                              SkString* outputDir) {
2867    const char* argv0 = argv[0];
2868    char* const* stop = argv + argc;
2869
2870    for (++argv; argv < stop; ++argv) {
2871        if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
2872            usage(argv0);
2873            exit(-1);
2874        } else if (0 == strcmp(*argv, "-w")) {
2875            ++argv;
2876            if (argv >= stop) {
2877                SkDebugf("Missing outputDir for -w\n");
2878                usage(argv0);
2879                exit(-1);
2880            }
2881            *outputDir = SkString(*argv);
2882        } else {
2883            inputs->push_back(SkString(*argv));
2884        }
2885    }
2886
2887    if (inputs->count() < 1) {
2888        usage(argv0);
2889        exit(-1);
2890    }
2891}
2892
2893int tool_main(int argc, char** argv);
2894int tool_main(int argc, char** argv) {
2895    SkAutoGraphics ag;
2896    SkTArray<SkString> inputs;
2897
2898    SkAutoTUnref<SkPdfViewer>
2899        renderer(SkNEW(SkPdfViewer));
2900    SkASSERT(renderer.get());
2901
2902    SkString outputDir;
2903    parse_commandline(argc, argv, &inputs, &outputDir);
2904
2905    int failures = 0;
2906    for (int i = 0; i < inputs.count(); i ++) {
2907        failures += process_input(inputs[i], outputDir, *renderer);
2908    }
2909
2910    reportPdfRenderStats();
2911
2912    if (failures != 0) {
2913        SkDebugf("Failed to render %i PDFs.\n", failures);
2914        return 1;
2915    }
2916
2917    return 0;
2918}
2919
2920#if !defined SK_BUILD_FOR_IOS
2921int main(int argc, char * const argv[]) {
2922    return tool_main(argc, (char**) argv);
2923}
2924#endif
2925