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