Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: experimental/PdfViewer/pdf_viewer_main.cpp

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

Powered by Google App Engine
This is Rietveld 408576698