blob: 39d93ef9a058df4761ebf555db7742b3e03f92c2 [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"
edisonn@google.comafe5e9e2013-06-19 17:42:17 +000010#include "SkForceLinking.h"
edisonn@google.com01cd4d52013-06-10 20:44:45 +000011#include "SkGraphics.h"
12#include "SkImageDecoder.h"
13#include "SkImageEncoder.h"
14#include "SkOSFile.h"
15#include "SkPicture.h"
16#include "SkStream.h"
17#include "SkTypeface.h"
18#include "SkTArray.h"
19#include "picture_utils.h"
20
21#include <iostream>
22#include <cstdio>
23#include <stack>
24
25#include "podofo.h"
edisonn@google.comaf3daa02013-06-12 19:07:45 +000026using namespace PoDoFo;
27
edisonn@google.comff278442013-06-21 21:03:15 +000028
edisonn@google.comafe5e9e2013-06-19 17:42:17 +000029__SK_FORCE_IMAGE_DECODER_LINKING;
30
edisonn@google.comff278442013-06-21 21:03:15 +000031
32//#define PDF_TRACE
33//#define PDF_TRACE_DIFF_IN_PNG
34//#define PDF_DEBUG_NO_CLIPING
35//#define PDF_DEBUG_NO_PAGE_CLIPING
36//#define PDF_DEBUG_3X
37
38
edisonn@google.com59543d32013-06-18 22:00:40 +000039const PdfObject* resolveReferenceObject(const PdfMemDocument* pdfDoc,
40 const PdfObject* obj,
41 bool resolveOneElementArrays = false);
42
edisonn@google.comaf3daa02013-06-12 19:07:45 +000043bool LongFromDictionary(const PdfMemDocument* pdfDoc,
44 const PdfDictionary& dict,
45 const char* key,
46 const char* abr,
47 long* data);
48
edisonn@google.coma2fab9d2013-06-14 19:22:19 +000049bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
50 const PdfDictionary& dict,
51 const char* key,
52 const char* abr,
53 double* data);
54
edisonn@google.comaf3daa02013-06-12 19:07:45 +000055bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
56 const PdfDictionary& dict,
57 const char* key,
58 const char* abr,
59 bool* data);
60
61bool NameFromDictionary(const PdfMemDocument* pdfDoc,
62 const PdfDictionary& dict,
63 const char* key,
64 const char* abr,
65 std::string* data);
66
edisonn@google.coma2fab9d2013-06-14 19:22:19 +000067bool StringFromDictionary(const PdfMemDocument* pdfDoc,
68 const PdfDictionary& dict,
69 const char* key,
70 const char* abr,
71 std::string* data);
72
73class SkPdfDictionary;
74bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
75 const PdfDictionary& dict,
76 const char* key,
77 const char* abr,
78 SkPdfDictionary** data);
79
edisonn@google.com68d15c82013-06-17 20:46:27 +000080template <typename T>
81bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
82 const PdfDictionary& dict,
83 const char* key,
84 const char* abr,
85 T** data);
86
edisonn@google.coma2fab9d2013-06-14 19:22:19 +000087class SkPdfObject;
88bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
89 const PdfDictionary& dict,
90 const char* key,
91 const char* abr,
92 SkPdfObject** data);
edisonn@google.comaf3daa02013-06-12 19:07:45 +000093
94
edisonn@google.com1277cf02013-06-17 23:36:45 +000095struct SkPdfFileSpec {};
96class SkPdfArray;
edisonn@google.comff278442013-06-21 21:03:15 +000097class SkPdfStream;
edisonn@google.com1277cf02013-06-17 23:36:45 +000098struct SkPdfDate {};
99struct SkPdfTree {};
100struct SkPdfFunction {};
101
102bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
103 const PdfDictionary& dict,
104 const char* key,
105 const char* abr,
106 SkPdfArray* data);
107
108
109bool FileSpecFromDictionary(const PdfMemDocument* pdfDoc,
110 const PdfDictionary& dict,
111 const char* key,
112 const char* abr,
113 SkPdfFileSpec* data);
114
115
116bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
117 const PdfDictionary& dict,
118 const char* key,
119 const char* abr,
edisonn@google.comff278442013-06-21 21:03:15 +0000120 SkPdfStream** data);
edisonn@google.com1277cf02013-06-17 23:36:45 +0000121
122bool TreeFromDictionary(const PdfMemDocument* pdfDoc,
123 const PdfDictionary& dict,
124 const char* key,
125 const char* abr,
126 SkPdfTree** data);
127
128bool DateFromDictionary(const PdfMemDocument* pdfDoc,
129 const PdfDictionary& dict,
130 const char* key,
131 const char* abr,
132 SkPdfDate* data);
133
134bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
135 const PdfDictionary& dict,
136 const char* key,
137 const char* abr,
138 SkRect* data);
139
140bool FunctionFromDictionary(const PdfMemDocument* pdfDoc,
141 const PdfDictionary& dict,
142 const char* key,
143 const char* abr,
144 SkPdfFunction* data);
145
146
edisonn@google.com59543d32013-06-18 22:00:40 +0000147#include "SkPdfHeaders_autogen.h"
148#include "SkPdfPodofoMapper_autogen.h"
149#include "SkPdfParser.h"
edisonn@google.comff278442013-06-21 21:03:15 +0000150#include "SkPdfFont.h"
151
152// TODO(edisonn): fix the mess with the files.
153#include "SkPdfFont.cpp"
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000154
edisonn@google.com1277cf02013-06-17 23:36:45 +0000155bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
156 const PdfDictionary& dict,
157 const char* key,
158 const char* abr,
159 SkPdfArray* data) {return false;}
160
161bool FileSpecFromDictionary(const PdfMemDocument* pdfDoc,
162 const PdfDictionary& dict,
163 const char* key,
164 const char* abr,
165 SkPdfFileSpec* data) {return false;}
166
167bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
168 const PdfDictionary& dict,
169 const char* key,
170 const char* abr,
edisonn@google.comff278442013-06-21 21:03:15 +0000171 SkPdfStream** data);
edisonn@google.com1277cf02013-06-17 23:36:45 +0000172
173bool TreeFromDictionary(const PdfMemDocument* pdfDoc,
174 const PdfDictionary& dict,
175 const char* key,
176 const char* abr,
177 SkPdfTree** data) {return false;}
178
179bool DateFromDictionary(const PdfMemDocument* pdfDoc,
180 const PdfDictionary& dict,
181 const char* key,
182 const char* abr,
183 SkPdfDate* data) {return false;}
184
edisonn@google.com1277cf02013-06-17 23:36:45 +0000185bool FunctionFromDictionary(const PdfMemDocument* pdfDoc,
186 const PdfDictionary& dict,
187 const char* key,
188 const char* abr,
189 SkPdfFunction* data) {return false;}
190
191
192
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000193/*
edisonn@google.com68d15c82013-06-17 20:46:27 +0000194 * TODO(edisonn):
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000195 * - all font types and all ppdf font features
196 * - word spacing
197 * - load font for baidu.pdf
198 * - load font for youtube.pdf
199 * - parser for pdf from the definition already available in pdfspec_autogen.py
edisonn@google.comff278442013-06-21 21:03:15 +0000200 * - all docs from ~/work
201 * - encapsulate podofo in the pdf api so the skpdf does not know anything about podofo ... in progress
202 * - load gs/ especially smask and already known prop (skp) ... in progress
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000203 * - wrapper on classes for customizations? e.g.
204 * SkPdfPageObjectVanila - has only the basic loaders/getters
205 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
206 * need to find a nice object model for all this with constructors and factories
207 * - deal with inheritable automatically ?
208 * - deal with specific type in spec directly, add all dictionary types to known types
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000209*/
210
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000211
212// TODO(edisonn): move in trace util.
213#ifdef PDF_TRACE
214static void SkTraceMatrix(const SkMatrix& matrix, const char* sz = "") {
215 printf("SkMatrix %s ", sz);
216 for (int i = 0 ; i < 9 ; i++) {
217 printf("%f ", SkScalarToDouble(matrix.get(i)));
218 }
219 printf("\n");
220}
221#else
222#define SkTraceMatrix(a,b)
223#endif
224
225using namespace std;
226using namespace PoDoFo;
227
228// Utilities
229static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
230 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
231
232 bitmap->allocPixels();
233 bitmap->eraseColor(color);
234}
235
236// TODO(edisonn): synonyms? DeviceRGB and RGB ...
237int GetColorSpaceComponents(const std::string& colorSpace) {
238 if (colorSpace == "DeviceCMYK") {
239 return 4;
240 } else if (colorSpace == "DeviceGray" ||
241 colorSpace == "CalGray" ||
242 colorSpace == "Indexed") {
243 return 1;
244 } else if (colorSpace == "DeviceRGB" ||
245 colorSpace == "CalRGB" ||
246 colorSpace == "Lab") {
247 return 3;
248 } else {
249 return 0;
250 }
251}
252
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000253const PdfObject* resolveReferenceObject(const PdfMemDocument* pdfDoc,
254 const PdfObject* obj,
edisonn@google.com59543d32013-06-18 22:00:40 +0000255 bool resolveOneElementArrays) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000256 while (obj && (obj->IsReference() || (resolveOneElementArrays &&
257 obj->IsArray() &&
258 obj->GetArray().GetSize() == 1))) {
259 if (obj->IsReference()) {
260 // We need to force the non const, the only update we will do is for recurssion checks.
261 PdfReference& ref = (PdfReference&)obj->GetReference();
262 obj = pdfDoc->GetObjects().GetObject(ref);
263 } else {
264 obj = &obj->GetArray()[0];
265 }
266 }
267
268 return obj;
269}
270
271static SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
272 SkMatrix matrix;
273 matrix.setAll(SkDoubleToScalar(array[0]),
274 SkDoubleToScalar(array[2]),
275 SkDoubleToScalar(array[4]),
276 SkDoubleToScalar(array[1]),
277 SkDoubleToScalar(array[3]),
278 SkDoubleToScalar(array[5]),
279 SkDoubleToScalar(0),
280 SkDoubleToScalar(0),
281 SkDoubleToScalar(1));
282
283 return matrix;
284}
285
286// TODO(edisonn): better class design.
287struct PdfColorOperator {
288 std::string fColorSpace; // TODO(edisonn): use SkString
289 SkColor fColor;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000290 double fOpacity; // ca or CA
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000291 // TODO(edisonn): add here other color space options.
292
293 void setRGBColor(SkColor color) {
294 // TODO(edisonn): ASSERT DeviceRGB is the color space.
295 fColor = color;
296 }
297 // TODO(edisonn): double check the default values for all fields.
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000298 PdfColorOperator() : fColor(SK_ColorBLACK), fOpacity(1) {}
299
300 void applyGraphicsState(SkPaint* paint) {
301 paint->setColor(SkColorSetA(fColor, fOpacity * 255));
302 }
303
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000304};
305
306// TODO(edisonn): better class design.
307struct PdfGraphicsState {
308 SkMatrix fMatrix;
309 SkMatrix fMatrixTm;
310 SkMatrix fMatrixTlm;
311
312 double fCurPosX;
313 double fCurPosY;
314
315 double fCurFontSize;
316 bool fTextBlock;
317 PdfFont* fCurFont;
edisonn@google.comff278442013-06-21 21:03:15 +0000318 SkPdfFont* fSkFont;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000319 SkPath fPath;
320 bool fPathClosed;
321
322 // Clip that is applied after the drawing is done!!!
323 bool fHasClipPathToApply;
324 SkPath fClipPath;
325
326 PdfColorOperator fStroking;
327 PdfColorOperator fNonStroking;
328
329 double fLineWidth;
330 double fTextLeading;
331 double fWordSpace;
332 double fCharSpace;
333
edisonn@google.com59543d32013-06-18 22:00:40 +0000334 SkPdfResourceDictionary* fResources;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000335
336 SkBitmap fSMask;
337
338 PdfGraphicsState() {
339 fCurPosX = 0.0;
340 fCurPosY = 0.0;
341 fCurFontSize = 0.0;
342 fTextBlock = false;
343 fCurFont = NULL;
344 fMatrix = SkMatrix::I();
345 fMatrixTm = SkMatrix::I();
346 fMatrixTlm = SkMatrix::I();
347 fPathClosed = true;
348 fLineWidth = 0;
349 fTextLeading = 0;
350 fWordSpace = 0;
351 fCharSpace = 0;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000352 fHasClipPathToApply = false;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000353 fResources = NULL;
edisonn@google.comff278442013-06-21 21:03:15 +0000354 fSkFont = NULL;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000355 }
356
357 void applyGraphicsState(SkPaint* paint, bool stroking) {
358 if (stroking) {
359 fStroking.applyGraphicsState(paint);
360 } else {
361 fNonStroking.applyGraphicsState(paint);
362 }
363
364 // TODO(edisonn): get this from pdfContext->options,
365 // or pdfContext->addPaintOptions(&paint);
366 paint->setAntiAlias(true);
367
368 // TODO(edisonn): dashing, miter, ...
369 paint->setStrokeWidth(SkDoubleToScalar(fLineWidth));
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000370 }
371};
372
373// TODO(edisonn): better class design.
374struct PdfInlineImage {
375 std::map<std::string, std::string> fKeyValuePairs;
376 std::string fImageData;
377
378};
379
380// TODO(edisonn): better class design.
381struct PdfContext {
edisonn@google.com59543d32013-06-18 22:00:40 +0000382 std::stack<SkPdfObject*> fObjectStack;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000383 std::stack<PdfGraphicsState> fStateStack;
384 PdfGraphicsState fGraphicsState;
edisonn@google.com59543d32013-06-18 22:00:40 +0000385 SkPdfDoc& fPdfDoc;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000386 SkMatrix fOriginalMatrix;
387
388 PdfInlineImage fInlineImage;
389
edisonn@google.com59543d32013-06-18 22:00:40 +0000390 PdfContext(SkPdfDoc& doc) : fPdfDoc(doc) {}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000391
392};
393
394// TODO(edisonn): temporary code, to report how much of the PDF we actually think we rendered.
395enum PdfResult {
396 kOK_PdfResult,
397 kPartial_PdfResult,
398 kNYI_PdfResult,
399 kIgnoreError_PdfResult,
400 kError_PdfResult,
401 kUnsupported_PdfResult,
402
403 kCount_PdfResult
404};
405
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000406PdfContext* gPdfContext = NULL;
407SkBitmap* gDumpBitmap = NULL;
408SkCanvas* gDumpCanvas = NULL;
409char gLastKeyword[100] = "";
410int gLastOpKeyword = -1;
edisonn@google.com59543d32013-06-18 22:00:40 +0000411char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000412int gReadOp = 0;
413
414
415
416bool hasVisualEffect(const char* pdfOp) {
417 return true;
418 if (*pdfOp == '\0') return false;
419
420 char markedPdfOp[100] = ",";
421 strcat(markedPdfOp, pdfOp);
422 strcat(markedPdfOp, ",");
423
424 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
425}
426
427// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.comff278442013-06-21 21:03:15 +0000428static bool readToken(SkPdfTokenizer* fTokenizer, PdfToken* token) {
429 bool ret = fTokenizer->readToken(token);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000430
431 gReadOp++;
432
433#ifdef PDF_TRACE_DIFF_IN_PNG
434 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
435 // the numbar and name of last operation, so the file name will reflect op that changed.
436 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
437 gDumpCanvas->flush();
438
439 SkBitmap bitmap;
440 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
441
442 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
443
444 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
445 SkCanvas canvas(device);
446
447 // draw context stuff here
448 SkPaint blueBorder;
449 blueBorder.setColor(SK_ColorBLUE);
450 blueBorder.setStyle(SkPaint::kStroke_Style);
451 blueBorder.setTextSize(SkDoubleToScalar(20));
452
453 SkString str;
454
455 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
456 if (clipStack) {
457 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
458 const SkClipStack::Element* elem;
459 double y = 0;
460 int total = 0;
461 while (elem = iter.next()) {
462 total++;
463 y += 30;
464
465 switch (elem->getType()) {
466 case SkClipStack::Element::kRect_Type:
467 canvas.drawRect(elem->getRect(), blueBorder);
468 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
469 break;
470 case SkClipStack::Element::kPath_Type:
471 canvas.drawPath(elem->getPath(), blueBorder);
472 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
473 break;
474 case SkClipStack::Element::kEmpty_Type:
475 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
476 break;
477 default:
478 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
479 break;
480 }
481 }
482
483 y += 30;
484 str.printf("Number of clips in stack: %i", total);
485 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
486 }
487
488 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
489 SkPath clipPath;
490 if (clipRegion.getBoundaryPath(&clipPath)) {
491 SkPaint redBorder;
492 redBorder.setColor(SK_ColorRED);
493 redBorder.setStyle(SkPaint::kStroke_Style);
494 canvas.drawPath(clipPath, redBorder);
495 }
496
497 canvas.flush();
498
499 SkString out;
500
501 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
502 // ... and other properties, to be able to debug th code easily
503
504 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
505 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
506 }
507
edisonn@google.com59543d32013-06-18 22:00:40 +0000508 if (token->fType == kKeyword_TokenType) {
509 strcpy(gLastKeyword, token->fKeyword);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000510 gLastOpKeyword = gReadOp;
511 } else {
512 strcpy(gLastKeyword, "");
513 }
514#endif
515
516 return ret;
517}
518
519// TODO(edisonn): Document PdfTokenLooper and subclasses.
520class PdfTokenLooper {
521protected:
522 PdfTokenLooper* fParent;
edisonn@google.comff278442013-06-21 21:03:15 +0000523 SkPdfTokenizer* fTokenizer;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000524 PdfContext* fPdfContext;
525 SkCanvas* fCanvas;
526
527public:
528 PdfTokenLooper(PdfTokenLooper* parent,
edisonn@google.comff278442013-06-21 21:03:15 +0000529 SkPdfTokenizer* tokenizer,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000530 PdfContext* pdfContext,
531 SkCanvas* canvas)
532 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
533
534 virtual PdfResult consumeToken(PdfToken& token) = 0;
535 virtual void loop() = 0;
536
537 void setUp(PdfTokenLooper* parent) {
538 fParent = parent;
539 fTokenizer = parent->fTokenizer;
540 fPdfContext = parent->fPdfContext;
541 fCanvas = parent->fCanvas;
542 }
543};
544
545class PdfMainLooper : public PdfTokenLooper {
546public:
547 PdfMainLooper(PdfTokenLooper* parent,
edisonn@google.comff278442013-06-21 21:03:15 +0000548 SkPdfTokenizer* tokenizer,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000549 PdfContext* pdfContext,
550 SkCanvas* canvas)
551 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
552
553 virtual PdfResult consumeToken(PdfToken& token);
554 virtual void loop();
555};
556
557class PdfInlineImageLooper : public PdfTokenLooper {
558public:
559 PdfInlineImageLooper()
edisonn@google.comff278442013-06-21 21:03:15 +0000560 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000561
562 virtual PdfResult consumeToken(PdfToken& token);
563 virtual void loop();
564 PdfResult done();
565};
566
567class PdfCompatibilitySectionLooper : public PdfTokenLooper {
568public:
569 PdfCompatibilitySectionLooper()
edisonn@google.comff278442013-06-21 21:03:15 +0000570 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000571
572 virtual PdfResult consumeToken(PdfToken& token);
573 virtual void loop();
574};
575
576typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
577
578map<std::string, PdfOperatorRenderer> gPdfOps;
579
580map<std::string, int> gRenderStats[kCount_PdfResult];
581
582char* gRenderStatsNames[kCount_PdfResult] = {
583 "Success",
584 "Partially implemented",
585 "Not yet implemented",
586 "Ignore Error",
587 "Error",
588 "Unsupported/Unknown"
589};
590
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000591static SkTypeface* SkTypefaceFromPdfFont(PdfFont* font) {
edisonn@google.comff278442013-06-21 21:03:15 +0000592 if (font == NULL) {
593 return SkTypeface::CreateFromName("Times New Roman", SkTypeface::kNormal);
594 }
595
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000596 PdfObject* fontObject = font->GetObject();
597
598 PdfObject* pBaseFont = NULL;
599 // TODO(edisonn): warning, PoDoFo has a bug in PdfFont constructor, does not call InitVars()
600 // for now fixed locally.
601 pBaseFont = fontObject->GetIndirectKey( "BaseFont" );
602 const char* pszBaseFontName = pBaseFont->GetName().GetName().c_str();
603
604#ifdef PDF_TRACE
605 std::string str;
606 fontObject->ToString(str);
607 printf("Base Font Name: %s\n", pszBaseFontName);
608 printf("Font Object Data: %s\n", str.c_str());
609#endif
610
611 SkTypeface* typeface = SkTypefaceFromPdfStandardFont(pszBaseFontName, font->IsBold(), font->IsItalic());
612
613 if (typeface != NULL) {
614 return typeface;
615 }
616
617 char name[1000];
618 // HACK
619 strncpy(name, pszBaseFontName, 1000);
620 char* comma = strstr(name, ",");
621 char* dash = strstr(name, "-");
622 if (comma) *comma = '\0';
623 if (dash) *dash = '\0';
624
625 typeface = SkTypeface::CreateFromName(
626 name,
627 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
628 (font->IsItalic() ? SkTypeface::kItalic : 0)));
629
630 if (typeface != NULL) {
631#ifdef PDF_TRACE
632 printf("HACKED FONT found %s\n", name);
633#endif
634 return typeface;
635 }
636
637#ifdef PDF_TRACE
638 printf("FONT_NOT_FOUND %s\n", pszBaseFontName);
639#endif
640
641 // TODO(edisonn): Report Warning, NYI
642 return SkTypeface::CreateFromName(
643 "Times New Roman",
644 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
645 (font->IsItalic() ? SkTypeface::kItalic : 0)));
646}
647
edisonn@google.comff278442013-06-21 21:03:15 +0000648
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000649// TODO(edisonn): move this code in podofo, so we don't have to fix the font.
650// This logic needs to be moved in PdfEncodingObjectFactory::CreateEncoding
651std::map<PdfFont*, PdfCMapEncoding*> gFontsFixed;
652PdfEncoding* FixPdfFont(PdfContext* pdfContext, PdfFont* fCurFont) {
653 // TODO(edisonn): and is Identity-H
654 if (gFontsFixed.find(fCurFont) == gFontsFixed.end()) {
655 if (fCurFont->GetObject()->IsDictionary() && fCurFont->GetObject()->GetDictionary().HasKey(PdfName("ToUnicode"))) {
656 PdfCMapEncoding* enc = new PdfCMapEncoding(
657 fCurFont->GetObject(),
edisonn@google.com59543d32013-06-18 22:00:40 +0000658 (PdfObject*)resolveReferenceObject(&pdfContext->fPdfDoc.podofo(),
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000659 fCurFont->GetObject()->GetDictionary().GetKey(PdfName("ToUnicode"))),
660 PdfCMapEncoding::eBaseEncoding_Identity); // todo, read the base encoding
661 gFontsFixed[fCurFont] = enc;
662 return enc;
663 }
664
665 return NULL;
666 }
667
668 return gFontsFixed[fCurFont];
669}
670
671PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +0000672 const SkPdfObject* str,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000673 SkCanvas* canvas)
674{
edisonn@google.comff278442013-06-21 21:03:15 +0000675
676 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
677 if (skfont == NULL) {
678 skfont = SkPdfFont::Default();
679 }
680
681 SkUnencodedText binary(str);
682
683 SkDecodedText decoded;
684 skfont->encoding()->decodeText(binary, &decoded);
685
686 SkUnicodeText unicode;
687 skfont->ToUnicode(decoded, &unicode);
688
689 SkPaint paint;
690 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
691 // Or maybe just not call setTextSize at all?
692 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
693 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
694 }
695
696// if (fCurFont && fCurFont->GetFontScale() != 0) {
697// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
698// }
699
700 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
701
702 canvas->save();
703
704#if 1
705 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
706
707 SkPoint point1;
708 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
709
710 SkMatrix mirror;
711 mirror.setTranslate(0, -point1.y());
712 // TODO(edisonn): fix rotated text, and skewed too
713 mirror.postScale(SK_Scalar1, -SK_Scalar1);
714 // TODO(edisonn): post rotate, skew
715 mirror.postTranslate(0, point1.y());
716
717 matrix.postConcat(mirror);
718
719 canvas->setMatrix(matrix);
720
721 SkTraceMatrix(matrix, "mirrored");
722#endif
723
724 skfont->drawText(unicode, &paint, canvas, &pdfContext->fGraphicsState.fMatrixTm);
725 canvas->restore();
726
727/*
728 PdfString& rString = str->podofo()->GetString();
729
730 //pdfContext->fGraphicsState.fSkFont->GetDecoding()->ToUnicode(rString);
731 //void* text;
732 //int len;
733 //SkPaint paint;
734 //pdfContext->fGraphicsState.fSkFont->drawText(text, len, paint, canvas, &pdfContext->fGraphicsState.fMatrixTm);
735
736 PdfFont* fCurFont = pdfContext->fGraphicsState.fCurFont;
737
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000738 if (!fCurFont)
739 {
740 // TODO(edisonn): ignore the error, use the default font?
edisonn@google.comff278442013-06-21 21:03:15 +0000741 // return kError_PdfResult;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000742 }
743
edisonn@google.comff278442013-06-21 21:03:15 +0000744 const PdfEncoding* enc = fCurFont ? FixPdfFont(pdfContext, fCurFont) : NULL;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000745 bool cMapUnicodeFont = enc != NULL;
edisonn@google.comff278442013-06-21 21:03:15 +0000746 if (!enc) enc = fCurFont ? fCurFont->GetEncoding() : NULL;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000747 if (!enc)
748 {
749 // TODO(edisonn): Can we recover from this error?
edisonn@google.comff278442013-06-21 21:03:15 +0000750 //return kError_PdfResult;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000751 }
752
753 PdfString r2 = rString;
754 PdfString unicode;
755
756 if (cMapUnicodeFont) {
757 r2 = PdfString((pdf_utf16be*)rString.GetString(), rString.GetLength() / 2);
758 }
759
edisonn@google.comff278442013-06-21 21:03:15 +0000760 unicode = enc ? enc->ConvertToUnicode( r2, fCurFont ) : r2.ToUnicode();
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000761
762#ifdef PDF_TRACE
763 printf("%i %i ? %c rString.len = %i\n", (int)rString.GetString()[0], (int)rString.GetString()[1], (int)rString.GetString()[1], rString.GetLength());
764 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());
765#endif
766
767 SkPaint paint;
768 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
769 // Or maybe just not call setTextSize at all?
770 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
771 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
772 }
edisonn@google.comff278442013-06-21 21:03:15 +0000773 if (fCurFont && fCurFont->GetFontScale() != 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000774 paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
775 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000776
777 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000778
779 paint.setTypeface(SkTypefaceFromPdfFont(fCurFont));
780
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000781 canvas->save();
782 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
783
784#if 0
785 // Reverse now the space, otherwise the text is upside down.
786 SkScalar z = SkIntToScalar(0);
787 SkScalar one = SkIntToScalar(1);
788
789 SkPoint normalSpace1[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
790 SkPoint mirrorSpace1[4];
791 pdfContext->fGraphicsState.fMatrixTm.mapPoints(mirrorSpace1, normalSpace1, 4);
792
793 SkPoint normalSpace2[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, -one), SkPoint::Make(z, -one)};
794 SkPoint mirrorSpace2[4];
795 pdfContext->fGraphicsState.fMatrixTm.mapPoints(mirrorSpace2, normalSpace2, 4);
796
797#ifdef PDF_TRACE
798 printf("mirror1[0], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[0].x()), SkScalarToDouble(mirrorSpace1[0].y()));
799 printf("mirror1[1], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[1].x()), SkScalarToDouble(mirrorSpace1[1].y()));
800 printf("mirror1[2], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[2].x()), SkScalarToDouble(mirrorSpace1[2].y()));
801 printf("mirror1[3], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[3].x()), SkScalarToDouble(mirrorSpace1[3].y()));
802 printf("mirror2[0], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[0].x()), SkScalarToDouble(mirrorSpace2[0].y()));
803 printf("mirror2[1], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[1].x()), SkScalarToDouble(mirrorSpace2[1].y()));
804 printf("mirror2[2], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[2].x()), SkScalarToDouble(mirrorSpace2[2].y()));
805 printf("mirror2[3], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[3].x()), SkScalarToDouble(mirrorSpace2[3].y()));
806#endif
807
808 SkMatrix mirror;
809 SkASSERT(mirror.setPolyToPoly(mirrorSpace1, mirrorSpace2, 4));
810
811 // TODO(edisonn): text positioning wrong right now. Need to get matrix operations right.
812 matrix.preConcat(mirror);
813 canvas->setMatrix(matrix);
814#endif
815
816 SkPoint point1;
817 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
818
819 SkMatrix mirror;
820 mirror.setTranslate(0, -point1.y());
821 // TODO(edisonn): fix rotated text, and skewed too
822 mirror.postScale(SK_Scalar1, -SK_Scalar1);
823 // TODO(edisonn): post rotate, skew
824 mirror.postTranslate(0, point1.y());
825
826 matrix.postConcat(mirror);
827
828 canvas->setMatrix(matrix);
829
830 SkTraceMatrix(matrix, "mirrored");
831
832#ifdef PDF_TRACE
833 SkPoint point;
834 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkDoubleToScalar(0), SkDoubleToScalar(0), &point);
835 printf("Original SkCanvas resolved coordinates, x = %f y = %f\n", SkScalarToDouble(point.x()), SkScalarToDouble(point.y()));
836 matrix.mapXY(SkDoubleToScalar(0), SkDoubleToScalar(0), &point);
837 printf("Mirored SkCanvas resolved coordinates, x = %f y = %f\n", SkScalarToDouble(point.x()), SkScalarToDouble(point.y()));
838#endif
839
840 // TODO(edisonn): remove this call once we load the font properly
841 // The extra * will show that we got at least the text positioning right
842 // even if font failed to be loaded
843// canvas->drawText(".", 1, SkDoubleToScalar(-5.0), SkDoubleToScalar(0.0), paint);
844
845
846
847 // TODO(edisonn): use character and word spacing .. add utility function
848 if (cMapUnicodeFont) {
849 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
850 SkScalar textWidth = paint.measureText(unicode.GetString(), unicode.GetLength());
851 pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
852 canvas->drawText(unicode.GetString(), unicode.GetLength(), SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
853 }
854 else {
855 paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
856 SkScalar textWidth = paint.measureText(unicode.GetStringUtf8().c_str(), strlen(unicode.GetStringUtf8().c_str()));
857 pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
858 canvas->drawText(unicode.GetStringUtf8().c_str(), strlen(unicode.GetStringUtf8().c_str()), SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
859 }
860
861// paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
862// unsigned char ch = *(unicode.GetString() + 3);
863// if ((ch & 0xC0) != 0x80 && ch < 0x80) {
864// printf("x%i", ch);
865// SkScalar textWidth = paint.measureText(&ch, 1);
866// pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
867// canvas->drawText(&ch, 1, SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
868// }
869
870 canvas->restore();
871
edisonn@google.comff278442013-06-21 21:03:15 +0000872*/
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000873 return kPartial_PdfResult;
874}
875
876// TODO(edisonn): create header files with declarations!
877PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
878PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
879PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
880PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
881
882// TODO(edisonn): deal with synonyms (/BPC == /BitsPerComponent), here or in GetKey?
883// Always pass long form in key, and have a map of long -> short key
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000884bool LongFromDictionary(const PdfMemDocument* pdfDoc,
885 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000886 const char* key,
887 long* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000888 const PdfObject* value = resolveReferenceObject(pdfDoc,
889 dict.GetKey(PdfName(key)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000890
891 if (value == NULL || !value->IsNumber()) {
892 return false;
893 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000894 if (data == NULL) {
895 return true;
896 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000897
898 *data = value->GetNumber();
899 return true;
900}
901
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000902bool LongFromDictionary(const PdfMemDocument* pdfDoc,
903 const PdfDictionary& dict,
904 const char* key,
905 const char* abr,
906 long* data) {
907 if (LongFromDictionary(pdfDoc, dict, key, data)) return true;
908 if (abr == NULL || *abr == '\0') return false;
909 return LongFromDictionary(pdfDoc, dict, abr, data);
910}
911
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000912bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
913 const PdfDictionary& dict,
914 const char* key,
915 double* data) {
916 const PdfObject* value = resolveReferenceObject(pdfDoc,
917 dict.GetKey(PdfName(key)));
918
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000919 if (value == NULL || (!value->IsReal() && !value->IsNumber())) {
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000920 return false;
921 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000922 if (data == NULL) {
923 return true;
924 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000925
926 *data = value->GetReal();
927 return true;
928}
929
930bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
931 const PdfDictionary& dict,
932 const char* key,
933 const char* abr,
934 double* data) {
935 if (DoubleFromDictionary(pdfDoc, dict, key, data)) return true;
936 if (abr == NULL || *abr == '\0') return false;
937 return DoubleFromDictionary(pdfDoc, dict, abr, data);
938}
939
940
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000941bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
942 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000943 const char* key,
944 bool* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000945 const PdfObject* value = resolveReferenceObject(pdfDoc,
946 dict.GetKey(PdfName(key)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000947
948 if (value == NULL || !value->IsBool()) {
949 return false;
950 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000951 if (data == NULL) {
952 return true;
953 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000954
955 *data = value->GetBool();
956 return true;
957}
958
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000959bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
960 const PdfDictionary& dict,
961 const char* key,
962 const char* abr,
963 bool* data) {
964 if (BoolFromDictionary(pdfDoc, dict, key, data)) return true;
965 if (abr == NULL || *abr == '\0') return false;
966 return BoolFromDictionary(pdfDoc, dict, abr, data);
967}
968
969bool NameFromDictionary(const PdfMemDocument* pdfDoc,
970 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000971 const char* key,
972 std::string* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000973 const PdfObject* value = resolveReferenceObject(pdfDoc,
974 dict.GetKey(PdfName(key)),
975 true);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000976 if (value == NULL || !value->IsName()) {
977 return false;
978 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000979 if (data == NULL) {
980 return true;
981 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000982
983 *data = value->GetName().GetName();
984 return true;
985}
986
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000987bool NameFromDictionary(const PdfMemDocument* pdfDoc,
988 const PdfDictionary& dict,
989 const char* key,
990 const char* abr,
991 std::string* data) {
992 if (NameFromDictionary(pdfDoc, dict, key, data)) return true;
993 if (abr == NULL || *abr == '\0') return false;
994 return NameFromDictionary(pdfDoc, dict, abr, data);
995}
996
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000997bool StringFromDictionary(const PdfMemDocument* pdfDoc,
998 const PdfDictionary& dict,
999 const char* key,
1000 std::string* data) {
1001 const PdfObject* value = resolveReferenceObject(pdfDoc,
1002 dict.GetKey(PdfName(key)),
1003 true);
1004 if (value == NULL || (!value->IsString() && !value->IsHexString())) {
1005 return false;
1006 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001007 if (data == NULL) {
1008 return true;
1009 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001010
1011 *data = value->GetString().GetString();
1012 return true;
1013}
1014
1015bool StringFromDictionary(const PdfMemDocument* pdfDoc,
1016 const PdfDictionary& dict,
1017 const char* key,
1018 const char* abr,
1019 std::string* data) {
1020 if (StringFromDictionary(pdfDoc, dict, key, data)) return true;
1021 if (abr == NULL || *abr == '\0') return false;
1022 return StringFromDictionary(pdfDoc, dict, abr, data);
1023}
1024
1025bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
1026 const PdfDictionary& dict,
1027 const char* key,
1028 SkPdfDictionary** data) {
1029 const PdfObject* value = resolveReferenceObject(pdfDoc,
1030 dict.GetKey(PdfName(key)),
1031 true);
1032 if (value == NULL || !value->IsDictionary()) {
1033 return false;
1034 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001035 if (data == NULL) {
1036 return true;
1037 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001038
edisonn@google.com68d15c82013-06-17 20:46:27 +00001039 return PodofoMapper::map(*pdfDoc, *value, (SkPdfObject**)data);
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001040}
1041
1042bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
1043 const PdfDictionary& dict,
1044 const char* key,
1045 const char* abr,
1046 SkPdfDictionary** data) {
1047 if (DictionaryFromDictionary(pdfDoc, dict, key, data)) return true;
1048 if (abr == NULL || *abr == '\0') return false;
1049 return DictionaryFromDictionary(pdfDoc, dict, abr, data);
1050}
1051
edisonn@google.com68d15c82013-06-17 20:46:27 +00001052template <typename T>
1053bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
1054 const PdfDictionary& dict,
1055 const char* key,
1056 SkPdfDictionary** data) {
1057 const PdfObject* value = resolveReferenceObject(pdfDoc,
1058 dict.GetKey(PdfName(key)),
1059 true);
1060 if (value == NULL || !value->IsDictionary()) {
1061 return false;
1062 }
1063
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001064 if (data == NULL) {
1065 return true;
1066 }
1067
edisonn@google.com68d15c82013-06-17 20:46:27 +00001068 return PodofoMapper::map(*pdfDoc, *value, (T**)data);
1069}
1070
1071template <typename T>
1072bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
1073 const PdfDictionary& dict,
1074 const char* key,
1075 const char* abr,
1076 T** data) {
1077 if (DictionaryFromDictionary2<T>(pdfDoc, dict, key, data)) return true;
1078 if (abr == NULL || *abr == '\0') return false;
1079 return DictionaryFromDictionary2<T>(pdfDoc, dict, abr, data);
1080}
1081
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001082bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
1083 const PdfDictionary& dict,
1084 const char* key,
1085 SkPdfObject** data) {
1086 const PdfObject* value = resolveReferenceObject(pdfDoc,
1087 dict.GetKey(PdfName(key)),
1088 true);
1089 if (value == NULL) {
1090 return false;
1091 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001092 if (data == NULL) {
1093 return true;
1094 }
edisonn@google.com68d15c82013-06-17 20:46:27 +00001095 return PodofoMapper::map(*pdfDoc, *value, data);
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001096}
1097
1098bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
1099 const PdfDictionary& dict,
1100 const char* key,
1101 const char* abr,
1102 SkPdfObject** data) {
1103 if (ObjectFromDictionary(pdfDoc, dict, key, data)) return true;
1104 if (abr == NULL || *abr == '\0') return false;
1105 return ObjectFromDictionary(pdfDoc, dict, abr, data);
1106}
1107
edisonn@google.comff278442013-06-21 21:03:15 +00001108bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
1109 const PdfDictionary& dict,
1110 const char* key,
1111 SkPdfStream** data) {
1112 const PdfObject* value = resolveReferenceObject(pdfDoc,
1113 dict.GetKey(PdfName(key)),
1114 true);
1115 if (value == NULL) {
1116 return false;
1117 }
1118 if (data == NULL) {
1119 return true;
1120 }
1121 return PodofoMapper::map(*pdfDoc, *value, data);
1122}
1123
1124bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
1125 const PdfDictionary& dict,
1126 const char* key,
1127 const char* abr,
1128 SkPdfStream** data) {
1129 if (StreamFromDictionary(pdfDoc, dict, key, data)) return true;
1130 if (abr == NULL || *abr == '\0') return false;
1131 return StreamFromDictionary(pdfDoc, dict, abr, data);
1132}
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001133
edisonn@google.com68d15c82013-06-17 20:46:27 +00001134
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001135// TODO(edisonn): perf!!!
1136
1137static SkColorTable* getGrayColortable() {
1138 static SkColorTable* grayColortable = NULL;
1139 if (grayColortable == NULL) {
1140 SkPMColor* colors = new SkPMColor[256];
1141 for (int i = 0 ; i < 256; i++) {
1142 colors[i] = SkPreMultiplyARGB(255, i, i, i);
1143 }
1144 grayColortable = new SkColorTable(colors, 256);
1145 }
1146 return grayColortable;
1147}
1148
1149SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
1150 int width, int height, int bytesPerLine,
1151 int bpc, const std::string& colorSpace,
1152 bool transparencyMask) {
1153 SkBitmap bitmap;
1154
1155 int components = GetColorSpaceComponents(colorSpace);
1156//#define MAX_COMPONENTS 10
1157
1158 int bitsPerLine = width * components * bpc;
1159 // TODO(edisonn): assume start of lines are aligned at 32 bits?
1160 // Is there a faster way to load the uncompressed stream into a bitmap?
1161
1162 // minimal support for now
1163 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
1164 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
1165
1166 for (int h = 0 ; h < height; h++) {
1167 long i = width * (height - 1 - h);
1168 for (int w = 0 ; w < width; w++) {
1169 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
1170 uncompressedStream[3 * w + 1],
1171 uncompressedStream[3 * w + 2]);
1172 i++;
1173 }
1174 uncompressedStream += bytesPerLine;
1175 }
1176
1177 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
1178 bitmap.setPixels(uncompressedStreamArgb);
1179 }
1180 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
1181 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
1182
1183 for (int h = 0 ; h < height; h++) {
1184 long i = width * (height - 1 - h);
1185 for (int w = 0 ; w < width; w++) {
1186 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
1187 uncompressedStream[w];
1188 i++;
1189 }
1190 uncompressedStream += bytesPerLine;
1191 }
1192
1193 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
1194 width, height);
1195 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
1196 }
1197
1198 // TODO(edisonn): Report Warning, NYI, or error
1199 return bitmap;
1200}
1201
1202bool transferImageStreamToARGB(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
1203 int width, int bytesPerLine,
1204 int bpc, const std::string& colorSpace,
1205 SkColor** uncompressedStreamArgb,
1206 pdf_long* uncompressedStreamLengthInBytesArgb) {
1207 int components = GetColorSpaceComponents(colorSpace);
1208//#define MAX_COMPONENTS 10
1209
1210 int bitsPerLine = width * components * bpc;
1211 // TODO(edisonn): assume start of lines are aligned at 32 bits?
1212 int height = uncompressedStreamLength / bytesPerLine;
1213
1214 // minimal support for now
1215 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
1216 *uncompressedStreamLengthInBytesArgb = width * height * 4;
1217 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
1218
1219 for (int h = 0 ; h < height; h++) {
1220 long i = width * (height - 1 - h);
1221 for (int w = 0 ; w < width; w++) {
1222 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[3 * w],
1223 uncompressedStream[3 * w + 1],
1224 uncompressedStream[3 * w + 2]);
1225 i++;
1226 }
1227 uncompressedStream += bytesPerLine;
1228 }
1229 return true;
1230 }
1231
1232 if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
1233 *uncompressedStreamLengthInBytesArgb = width * height * 4;
1234 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
1235
1236 for (int h = 0 ; h < height; h++) {
1237 long i = width * (height - 1 - h);
1238 for (int w = 0 ; w < width; w++) {
1239 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[w],
1240 uncompressedStream[w],
1241 uncompressedStream[w]);
1242 i++;
1243 }
1244 uncompressedStream += bytesPerLine;
1245 }
1246 return true;
1247 }
1248
1249 return false;
1250}
1251
1252// utils
1253
1254// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
1255// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
1256// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
1257// skia format, through a table
1258
1259// this functions returns the image, it does not look at the smask.
1260
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001261SkBitmap getImageFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001262 if (image == NULL || !image->valid()) {
1263 // TODO(edisonn): report warning to be used in testing.
1264 return SkBitmap();
1265 }
1266
1267 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
1268// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
1269// obj.GetDictionary().GetKey(PdfName("Filter")));
1270// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
1271// value = resolveReferenceObject(pdfContext->fPdfDoc,
1272// &value->GetArray()[0]);
1273// }
1274// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
1275// SkStream stream = SkStream::
1276// SkImageDecoder::Factory()
1277// }
1278
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001279 long bpc = image->BitsPerComponent();
1280 long width = image->Width();
1281 long height = image->Height();
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001282 std::string colorSpace = "DeviceRGB";
edisonn@google.com1277cf02013-06-17 23:36:45 +00001283
1284 // TODO(edisonn): color space can be an array too!
1285 if (image->isColorSpaceAName()) {
1286 colorSpace = image->getColorSpaceAsName();
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001287 }
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001288
1289/*
1290 bool imageMask = image->imageMask();
1291
1292 if (imageMask) {
1293 if (bpc != 0 && bpc != 1) {
1294 // TODO(edisonn): report warning to be used in testing.
1295 return SkBitmap();
1296 }
1297 bpc = 1;
1298 }
1299*/
1300
1301 const PdfObject* obj = image->podofo();
1302
1303 char* uncompressedStream = NULL;
1304 pdf_long uncompressedStreamLength = 0;
1305
1306 PdfResult ret = kPartial_PdfResult;
1307 // TODO(edisonn): get rid of try/catch exceptions! We should not throw on user data!
1308 try {
1309 obj->GetStream()->GetFilteredCopy(&uncompressedStream, &uncompressedStreamLength);
1310 } catch (PdfError& e) {
1311 // TODO(edisonn): report warning to be used in testing.
1312 return SkBitmap();
1313 }
1314
1315 int bytesPerLine = uncompressedStreamLength / height;
1316#ifdef PDF_TRACE
1317 if (uncompressedStreamLength % height != 0) {
1318 printf("Warning uncompressedStreamLength % height != 0 !!!\n");
1319 }
1320#endif
1321
1322 SkBitmap bitmap = transferImageStreamToBitmap(
1323 (unsigned char*)uncompressedStream, uncompressedStreamLength,
1324 width, height, bytesPerLine,
1325 bpc, colorSpace,
1326 transparencyMask);
1327
1328 free(uncompressedStream);
1329
1330 return bitmap;
1331}
1332
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001333SkBitmap getSmaskFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* obj) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001334 const PdfObject* sMask = resolveReferenceObject(&pdfContext->fPdfDoc.podofo(),
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001335 obj->podofo()->GetDictionary().GetKey(PdfName("SMask")));
1336
1337#ifdef PDF_TRACE
1338 std::string str;
1339 if (sMask) {
1340 sMask->ToString(str);
1341 printf("/SMask of /Subtype /Image: %s\n", str.c_str());
1342 }
1343#endif
1344
1345 if (sMask) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001346 SkPdfImageDictionary skxobjmask(&pdfContext->fPdfDoc.podofo(), sMask);
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001347 return getImageFromObject(pdfContext, &skxobjmask, true);
1348 }
1349
1350 // TODO(edisonn): implement GS SMask. Default to empty right now.
1351 return pdfContext->fGraphicsState.fSMask;
1352}
1353
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001354PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfImageDictionary* skpdfimage) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001355 if (skpdfimage == NULL || !skpdfimage->valid()) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001356 return kIgnoreError_PdfResult;
1357 }
1358
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001359 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
1360 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001361
1362 canvas->save();
1363 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1364 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
1365
1366 if (sMask.empty()) {
1367 canvas->drawBitmapRect(image, dst, NULL);
1368 } else {
1369 canvas->saveLayer(&dst, NULL);
1370 canvas->drawBitmapRect(image, dst, NULL);
1371 SkPaint xfer;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001372 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001373 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
1374 canvas->drawBitmapRect(sMask, dst, &xfer);
1375 canvas->restore();
1376 }
1377
1378 canvas->restore();
1379
1380 return kPartial_PdfResult;
1381}
1382
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001383bool SkMatrixFromDictionary(PdfContext* pdfContext,
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001384 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001385 const char* key,
1386 SkMatrix* matrix) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001387 const PdfObject* value = resolveReferenceObject(&pdfContext->fPdfDoc.podofo(),
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001388 dict.GetKey(PdfName(key)));
1389
1390 if (value == NULL || !value->IsArray()) {
1391 return false;
1392 }
1393
1394 if (value->GetArray().GetSize() != 6) {
1395 return false;
1396 }
1397
1398 double array[6];
1399 for (int i = 0; i < 6; i++) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001400 const PdfObject* elem = resolveReferenceObject(&pdfContext->fPdfDoc.podofo(), &value->GetArray()[i]);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001401 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1402 return false;
1403 }
1404 array[i] = elem->GetReal();
1405 }
1406
1407 *matrix = SkMatrixFromPdfMatrix(array);
1408 return true;
1409}
1410
edisonn@google.com59543d32013-06-18 22:00:40 +00001411bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001412 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001413 const char* key,
1414 SkRect* rect) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001415 const PdfObject* value = resolveReferenceObject(pdfDoc,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001416 dict.GetKey(PdfName(key)));
1417
1418 if (value == NULL || !value->IsArray()) {
1419 return false;
1420 }
1421
1422 if (value->GetArray().GetSize() != 4) {
1423 return false;
1424 }
1425
1426 double array[4];
1427 for (int i = 0; i < 4; i++) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001428 const PdfObject* elem = resolveReferenceObject(pdfDoc, &value->GetArray()[i]);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001429 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1430 return false;
1431 }
1432 array[i] = elem->GetReal();
1433 }
1434
1435 *rect = SkRect::MakeLTRB(SkDoubleToScalar(array[0]),
1436 SkDoubleToScalar(array[1]),
1437 SkDoubleToScalar(array[2]),
1438 SkDoubleToScalar(array[3]));
1439 return true;
1440}
1441
edisonn@google.com59543d32013-06-18 22:00:40 +00001442bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
1443 const PdfDictionary& dict,
1444 const char* key,
1445 const char* abr,
1446 SkRect* data) {
1447 if (SkRectFromDictionary(pdfDoc, dict, key, data)) return true;
1448 if (abr == NULL || *abr == '\0') return false;
1449 return SkRectFromDictionary(pdfDoc, dict, abr, data);
1450
1451}
1452
1453
edisonn@google.com68d15c82013-06-17 20:46:27 +00001454SkPdfObject* get(const SkPdfObject* obj, const char* key, const char* abr = "") {
1455 SkPdfObject* ret = NULL;
1456 if (obj == NULL) return NULL;
1457 const SkPdfDictionary* dict = obj->asDictionary();
1458 if (dict == NULL) return NULL;
1459 if (!dict->podofo()->IsDictionary()) return NULL;
1460 ObjectFromDictionary(dict->doc(), dict->podofo()->GetDictionary(), key, abr, &ret);
1461 return ret;
1462}
1463
1464PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
1465 if (!skobj || !skobj->podofo() || !skobj->podofo()->HasStream() || skobj->podofo()->GetStream() == NULL || skobj->podofo()->GetStream()->GetLength() == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001466 return kOK_PdfResult;
1467 }
1468
1469 PdfOp_q(pdfContext, canvas, NULL);
1470 canvas->save();
1471
edisonn@google.com68d15c82013-06-17 20:46:27 +00001472 if (get(skobj, "Resources")) {
1473 SkPdfResourceDictionary* res = NULL;
1474
1475 PodofoMapper::map(*get(skobj, "Resources"), &res);
1476
1477 if (res) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001478 pdfContext->fGraphicsState.fResources = res;
edisonn@google.com68d15c82013-06-17 20:46:27 +00001479 }
1480 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001481
1482 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
1483
1484 SkMatrix matrix;
edisonn@google.com68d15c82013-06-17 20:46:27 +00001485 if (SkMatrixFromDictionary(pdfContext, skobj->podofo()->GetDictionary(), "Matrix", &matrix)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001486 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1487 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1488 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1489 // TODO(edisonn) reset matrixTm and matricTlm also?
1490 }
1491
1492 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1493
1494 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1495
1496 SkRect bbox;
edisonn@google.com59543d32013-06-18 22:00:40 +00001497 if (SkRectFromDictionary(&pdfContext->fPdfDoc.podofo(), skobj->podofo()->GetDictionary(), "BBox", &bbox)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001498 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
1499 }
1500
1501 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
1502 // For this PdfContentsTokenizer needs to be extended.
1503
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001504 PdfResult ret = kPartial_PdfResult;
edisonn@google.comff278442013-06-21 21:03:15 +00001505 SkPdfTokenizer tokenizer(skobj);
1506 PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1507 looper.loop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001508
1509 // TODO(edisonn): should we restore the variable stack at the same state?
1510 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1511 canvas->restore();
1512 PdfOp_Q(pdfContext, canvas, NULL);
1513 return ret;
1514}
1515
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001516PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject& obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001517 return kNYI_PdfResult;
1518}
1519
edisonn@google.com59543d32013-06-18 22:00:40 +00001520// TODO(edisonn): faster, have the property on the SkPdfObject itself?
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001521std::set<const PdfObject*> gInRendering;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001522
1523class CheckRecursiveRendering {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001524 const PdfObject& fObj;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001525public:
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001526 CheckRecursiveRendering(const PdfObject& obj) : fObj(obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001527 gInRendering.insert(&obj);
1528 }
1529
1530 ~CheckRecursiveRendering() {
1531 //SkASSERT(fObj.fInRendering);
1532 gInRendering.erase(&fObj);
1533 }
1534
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001535 static bool IsInRendering(const PdfObject& obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001536 return gInRendering.find(&obj) != gInRendering.end();
1537 }
1538};
1539
edisonn@google.com59543d32013-06-18 22:00:40 +00001540PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject& obj) {
1541 if (CheckRecursiveRendering::IsInRendering(*obj.podofo())) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001542 // Oops, corrupt PDF!
1543 return kIgnoreError_PdfResult;
1544 }
1545
edisonn@google.com59543d32013-06-18 22:00:40 +00001546 CheckRecursiveRendering checkRecursion(*obj.podofo());
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001547
1548 // TODO(edisonn): check type
edisonn@google.com68d15c82013-06-17 20:46:27 +00001549 SkPdfXObjectDictionary* skobj = NULL;
edisonn@google.com59543d32013-06-18 22:00:40 +00001550 if (!PodofoMapper::map(obj, &skobj)) return kIgnoreError_PdfResult;
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001551
1552 if (!skobj || !skobj->valid()) return kIgnoreError_PdfResult;
1553
1554 PdfResult ret = kIgnoreError_PdfResult;
1555 switch (skobj->getType())
1556 {
edisonn@google.com59543d32013-06-18 22:00:40 +00001557 case kImageDictionary_SkPdfObjectType:
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001558 ret = doXObject_Image(pdfContext, canvas, skobj->asImageDictionary());
edisonn@google.come4d11be2013-06-12 19:53:42 +00001559 break;
edisonn@google.com59543d32013-06-18 22:00:40 +00001560 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com68d15c82013-06-17 20:46:27 +00001561 ret = doXObject_Form(pdfContext, canvas, skobj->asType1FormDictionary());
edisonn@google.come4d11be2013-06-12 19:53:42 +00001562 break;
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001563 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
1564 //return doXObject_PS(skxobj.asPS());
1565 }
1566
1567 delete skobj;
1568 return ret;
1569}
1570
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001571PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1572 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1573 canvas->save();
1574 return kOK_PdfResult;
1575}
1576
1577PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1578 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1579 pdfContext->fStateStack.pop();
1580 canvas->restore();
1581 return kOK_PdfResult;
1582}
1583
1584PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1585 double array[6];
1586 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001587 array[5 - i] = pdfContext->fObjectStack.top()->asNumber()->value();
1588 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001589 }
1590
1591 // a b
1592 // c d
1593 // e f
1594
1595 // 0 1
1596 // 2 3
1597 // 4 5
1598
1599 // sx ky
1600 // kx sy
1601 // tx ty
1602 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1603
1604 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1605
1606#ifdef PDF_TRACE
1607 printf("cm ");
1608 for (int i = 0 ; i < 6 ; i++) {
1609 printf("%f ", array[i]);
1610 }
1611 printf("\n");
1612 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix);
1613#endif
1614
1615 return kOK_PdfResult;
1616}
1617
1618//leading TL Set the text leading, Tl
1619//, to leading, which is a number expressed in unscaled text
1620//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
1621PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001622 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001623
1624 pdfContext->fGraphicsState.fTextLeading = ty;
1625
1626 return kOK_PdfResult;
1627}
1628
1629PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001630 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1631 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001632
1633 double array[6] = {1, 0, 0, 1, tx, ty};
1634 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1635
1636 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1637 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1638
1639 return kPartial_PdfResult;
1640}
1641
1642PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001643 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1644 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001645
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001646 // TODO(edisonn): Create factory methods or constructors so podofo is hidden
edisonn@google.com59543d32013-06-18 22:00:40 +00001647 PdfObject _ty(PdfVariant(-ty));
1648 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &_ty));
1649
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001650 PdfOp_TL(pdfContext, canvas, looper);
1651
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001652 PdfObject vtx(PdfVariant(-(-tx))); // TODO(edisonn): Hmm, the compiler thinks I have here a function pointer if we use (tx), but not -(-tx)
edisonn@google.com59543d32013-06-18 22:00:40 +00001653 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &vtx));
1654
1655 PdfObject vty(PdfVariant(-(-ty)));
1656 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &vty));
1657
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001658 return PdfOp_Td(pdfContext, canvas, looper);
1659}
1660
1661PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001662 double f = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1663 double e = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1664 double d = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1665 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1666 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1667 double a = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001668
1669 double array[6];
1670 array[0] = a;
1671 array[1] = b;
1672 array[2] = c;
1673 array[3] = d;
1674 array[4] = e;
1675 array[5] = f;
1676
1677 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1678 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
1679
1680 // TODO(edisonn): Text positioning.
1681 pdfContext->fGraphicsState.fMatrixTm = matrix;
1682 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1683
1684 return kPartial_PdfResult;
1685}
1686
1687//— T* Move to the start of the next line. This operator has the same effect as the code
1688//0 Tl Td
1689//where Tl is the current leading parameter in the text state
1690PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001691 PdfObject zero(PdfVariant(0.0));
1692 PdfObject tl(PdfVariant(-(-pdfContext->fGraphicsState.fTextLeading)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001693
edisonn@google.com59543d32013-06-18 22:00:40 +00001694 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &zero));
1695 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &tl));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001696 return PdfOp_Td(pdfContext, canvas, looper);
1697}
1698
1699PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1700 if (pdfContext->fGraphicsState.fPathClosed) {
1701 pdfContext->fGraphicsState.fPath.reset();
1702 pdfContext->fGraphicsState.fPathClosed = false;
1703 }
1704
edisonn@google.com59543d32013-06-18 22:00:40 +00001705 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1706 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001707
1708 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1709 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1710
1711 return kOK_PdfResult;
1712}
1713
1714PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1715 if (pdfContext->fGraphicsState.fPathClosed) {
1716 pdfContext->fGraphicsState.fPath.reset();
1717 pdfContext->fGraphicsState.fPathClosed = false;
1718 }
1719
edisonn@google.com59543d32013-06-18 22:00:40 +00001720 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1721 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001722
1723 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1724 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1725
1726 return kOK_PdfResult;
1727}
1728
1729PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1730 if (pdfContext->fGraphicsState.fPathClosed) {
1731 pdfContext->fGraphicsState.fPath.reset();
1732 pdfContext->fGraphicsState.fPathClosed = false;
1733 }
1734
edisonn@google.com59543d32013-06-18 22:00:40 +00001735 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1736 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1737 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1738 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1739 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1740 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001741
1742 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1743 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1744 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1745
1746 pdfContext->fGraphicsState.fCurPosX = x3;
1747 pdfContext->fGraphicsState.fCurPosY = y3;
1748
1749 return kOK_PdfResult;
1750}
1751
1752PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1753 if (pdfContext->fGraphicsState.fPathClosed) {
1754 pdfContext->fGraphicsState.fPath.reset();
1755 pdfContext->fGraphicsState.fPathClosed = false;
1756 }
1757
edisonn@google.com59543d32013-06-18 22:00:40 +00001758 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1759 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1760 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1761 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001762 double y1 = pdfContext->fGraphicsState.fCurPosY;
1763 double x1 = pdfContext->fGraphicsState.fCurPosX;
1764
1765 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1766 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1767 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1768
1769 pdfContext->fGraphicsState.fCurPosX = x3;
1770 pdfContext->fGraphicsState.fCurPosY = y3;
1771
1772 return kOK_PdfResult;
1773}
1774
1775PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1776 if (pdfContext->fGraphicsState.fPathClosed) {
1777 pdfContext->fGraphicsState.fPath.reset();
1778 pdfContext->fGraphicsState.fPathClosed = false;
1779 }
1780
edisonn@google.com59543d32013-06-18 22:00:40 +00001781 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1782 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001783 double y2 = pdfContext->fGraphicsState.fCurPosY;
1784 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com59543d32013-06-18 22:00:40 +00001785 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1786 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001787
1788 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1789 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1790 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1791
1792 pdfContext->fGraphicsState.fCurPosX = x3;
1793 pdfContext->fGraphicsState.fCurPosY = y3;
1794
1795 return kOK_PdfResult;
1796}
1797
1798PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1799 if (pdfContext->fGraphicsState.fPathClosed) {
1800 pdfContext->fGraphicsState.fPath.reset();
1801 pdfContext->fGraphicsState.fPathClosed = false;
1802 }
1803
edisonn@google.com59543d32013-06-18 22:00:40 +00001804 double height = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1805 double width = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1806 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1807 double x = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001808
1809 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1810 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1811
1812 pdfContext->fGraphicsState.fCurPosX = x;
1813 pdfContext->fGraphicsState.fCurPosY = y + height;
1814
1815 return kOK_PdfResult;
1816}
1817
1818PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1819 pdfContext->fGraphicsState.fPath.close();
1820 pdfContext->fGraphicsState.fPathClosed = true;
1821 return kOK_PdfResult;
1822}
1823
1824PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
1825 SkPath path = pdfContext->fGraphicsState.fPath;
1826
1827 if (close) {
1828 path.close();
1829 }
1830
1831 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1832
1833 SkPaint paint;
1834
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001835 SkPoint line[2];
1836 if (fill && !stroke && path.isLine(line)) {
1837 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001838
1839 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001840 paint.setStrokeWidth(SkDoubleToScalar(0));
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001841
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001842 canvas->drawPath(path, paint);
1843 } else {
1844 if (fill) {
1845 paint.setStyle(SkPaint::kFill_Style);
1846 if (evenOdd) {
1847 path.setFillType(SkPath::kEvenOdd_FillType);
1848 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001849
1850 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1851
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001852 canvas->drawPath(path, paint);
1853 }
1854
1855 if (stroke) {
1856 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001857
1858 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1859
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001860 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1861 canvas->drawPath(path, paint);
1862 }
1863 }
1864
1865 pdfContext->fGraphicsState.fPath.reset();
1866 // todo zoom ... other stuff ?
1867
1868 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1869#ifndef PDF_DEBUG_NO_CLIPING
1870 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1871#endif
1872 }
1873
1874 //pdfContext->fGraphicsState.fClipPath.reset();
1875 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1876
1877 return kPartial_PdfResult;
1878
1879}
1880
1881PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1882 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1883}
1884
1885PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1886 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1887}
1888
1889PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1890 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1891}
1892
1893PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1894 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1895}
1896
1897PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1898 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1899}
1900
1901PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1902 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1903}
1904
1905PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1906 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1907}
1908
1909PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1910 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1911}
1912
1913PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1914 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1915}
1916
1917PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1918 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1919 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1920#ifndef PDF_DEBUG_NO_CLIPING
1921 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1922#endif
1923 }
1924
1925 //pdfContext->fGraphicsState.fClipPath.reset();
1926 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1927
1928 pdfContext->fGraphicsState.fPathClosed = true;
1929
1930 return kOK_PdfResult;
1931}
1932
1933PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1934 pdfContext->fGraphicsState.fTextBlock = true;
1935 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1936 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1937
1938 return kPartial_PdfResult;
1939}
1940
1941PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1942 if (!pdfContext->fGraphicsState.fTextBlock) {
1943 return kIgnoreError_PdfResult;
1944 }
1945 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1946 return kPartial_PdfResult;
1947}
1948
1949//font size Tf Set the text font, Tf
1950//, to font and the text font size, Tfs, to size. font is the name of a
1951//font resource in the Fontsubdictionary of the current resource dictionary; size is
1952//a number representing a scale factor. There is no initial value for either font or
1953//size; they must be specified explicitly using Tf before any text is shown.
1954PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001955 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1956 std::string fontName = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1957
1958#ifdef PDF_TRACE
1959 printf("font name: %s\n", fontName.c_str());
1960 std::string str;
1961 pdfContext->fGraphicsState.fResources->podofo()->ToString(str);
edisonn@google.comff278442013-06-21 21:03:15 +00001962 printf("Print Tf Resources: %s\n", str.c_str());
1963 pdfContext->fGraphicsState.fResources->Font()->podofo()->ToString(str);
1964 printf("Print Tf Resources/Font: %s\n", str.c_str());
edisonn@google.com59543d32013-06-18 22:00:40 +00001965#endif
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001966
edisonn@google.comff278442013-06-21 21:03:15 +00001967 SkPdfFontDictionary* fd = NULL;
1968 if (pdfContext->fGraphicsState.fResources->Font()) {
1969 SkPdfObject objFont = pdfContext->fGraphicsState.fResources->Font()->get(fontName.c_str());
1970 PodofoMapper::map(objFont, &fd);
1971
1972#ifdef PDF_TRACE
1973 objFont.podofo()->ToString(str);
1974 printf("Print Font loaded: %s\n", str.c_str());
1975 fd->podofo()->ToString(str);
1976 printf("Print Font loaded and resolved and upgraded: %s\n", str.c_str());
1977#endif
1978
1979 }
1980
1981 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(fd);
1982
1983 if (skfont) {
1984 pdfContext->fGraphicsState.fSkFont = skfont;
1985 }
1986
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001987 // TODO(edisonn): Load font from pdfContext->fGraphicsState.fObjectWithResources ?
edisonn@google.com59543d32013-06-18 22:00:40 +00001988 const PdfObject* pFont = resolveReferenceObject(&pdfContext->fPdfDoc.podofo(),
1989 pdfContext->fGraphicsState.fResources->Font()->get(fontName.c_str()).podofo());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001990 if( !pFont )
1991 {
1992 // TODO(edisonn): try to ignore the error, make sure we do not crash.
1993 return kIgnoreError_PdfResult;
1994 }
1995
edisonn@google.comff278442013-06-21 21:03:15 +00001996 PdfFont* font = pdfContext->fPdfDoc.podofo().GetFont( (PdfObject*)pFont );
1997 if (font) {
1998 pdfContext->fGraphicsState.fCurFont = font;
1999 } else {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002000 // TODO(edisonn): check ~/crasing, for one of the files PoDoFo throws exception
2001 // when calling pFont->Reference(), with Linked list corruption.
2002 return kIgnoreError_PdfResult;
2003 }
2004
2005 return kPartial_PdfResult;
2006}
2007
2008PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2009 if (!pdfContext->fGraphicsState.fTextBlock) {
2010 // TODO(edisonn): try to recover and draw it any way?
2011 return kIgnoreError_PdfResult;
2012 }
2013
2014 PdfResult ret = DrawText(pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +00002015 pdfContext->fObjectStack.top(),
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002016 canvas);
edisonn@google.com59543d32013-06-18 22:00:40 +00002017 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002018
2019 return ret;
2020}
2021
2022PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2023 if (!pdfContext->fGraphicsState.fTextBlock) {
2024 // TODO(edisonn): try to recover and draw it any way?
2025 return kIgnoreError_PdfResult;
2026 }
2027
2028 PdfOp_T_star(pdfContext, canvas, looper);
2029 // Do not pop, and push, just transfer the param to Tj
2030 return PdfOp_Tj(pdfContext, canvas, looper);
2031}
2032
2033PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2034 if (!pdfContext->fGraphicsState.fTextBlock) {
2035 // TODO(edisonn): try to recover and draw it any way?
2036 return kIgnoreError_PdfResult;
2037 }
2038
edisonn@google.com59543d32013-06-18 22:00:40 +00002039 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
2040 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
2041 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002042
edisonn@google.com59543d32013-06-18 22:00:40 +00002043 pdfContext->fObjectStack.push(aw);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002044 PdfOp_Tw(pdfContext, canvas, looper);
2045
edisonn@google.com59543d32013-06-18 22:00:40 +00002046 pdfContext->fObjectStack.push(ac);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002047 PdfOp_Tc(pdfContext, canvas, looper);
2048
edisonn@google.com59543d32013-06-18 22:00:40 +00002049 pdfContext->fObjectStack.push(str);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002050 PdfOp_quote(pdfContext, canvas, looper);
2051
2052 return kPartial_PdfResult;
2053}
2054
2055PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2056 if (!pdfContext->fGraphicsState.fTextBlock) {
2057 // TODO(edisonn): try to recover and draw it any way?
2058 return kIgnoreError_PdfResult;
2059 }
2060
edisonn@google.com59543d32013-06-18 22:00:40 +00002061 SkPdfArray* array = pdfContext->fObjectStack.top()->asArray();
2062 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002063
edisonn@google.com59543d32013-06-18 22:00:40 +00002064 for( int i=0; i<static_cast<int>(array->size()); i++ )
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002065 {
edisonn@google.comff278442013-06-21 21:03:15 +00002066 if( (*array)[i]->asString()) {
2067 SkPdfObject* obj = (*array)[i];
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002068 DrawText(pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +00002069 obj,
2070 canvas);
2071 } else if ((*array)[i]->asInteger() || (*array)[i]->asNumber()) {
2072 double dx = (*array)[i]->asNumber()->value();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002073 SkMatrix matrix;
2074 matrix.setAll(SkDoubleToScalar(1),
2075 SkDoubleToScalar(0),
2076 // TODO(edisonn): use writing mode, vertical/horizontal.
2077 SkDoubleToScalar(-dx), // amount is substracted!!!
2078 SkDoubleToScalar(0),
2079 SkDoubleToScalar(1),
2080 SkDoubleToScalar(0),
2081 SkDoubleToScalar(0),
2082 SkDoubleToScalar(0),
2083 SkDoubleToScalar(1));
2084
2085 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
2086 }
2087 }
2088 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
2089}
2090
2091PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002092 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002093 return kOK_PdfResult;
2094}
2095
2096PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2097 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2098}
2099
2100PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2101 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2102}
2103
2104PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
2105 double c[4];
2106 pdf_int64 v[4];
2107
2108 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
2109
2110 bool doubles = true;
2111 if (colorOperator->fColorSpace == "Indexed") {
2112 doubles = false;
2113 }
2114
2115#ifdef PDF_TRACE
2116 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.c_str(), n);
2117#endif
2118
2119 for (int i = n - 1; i >= 0 ; i--) {
2120 if (doubles) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002121 c[i] = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002122 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002123 v[i] = pdfContext->fObjectStack.top()->asInteger()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002124 }
2125 }
2126
2127 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
2128 if (colorOperator->fColorSpace == "DeviceRGB") {
2129 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
2130 }
2131 return kPartial_PdfResult;
2132}
2133
2134PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2135 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2136}
2137
2138PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2139 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2140}
2141
2142PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
2143 PdfString name;
2144
edisonn@google.com59543d32013-06-18 22:00:40 +00002145 if (pdfContext->fObjectStack.top()->asName()) {
2146 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002147 }
2148
2149 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
2150 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
2151
2152 return kPartial_PdfResult;
2153}
2154
2155PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2156 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2157}
2158
2159PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2160 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2161}
2162
2163PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002164 double gray = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002165 return kNYI_PdfResult;
2166}
2167
2168PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2169 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2170}
2171
2172PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2173 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2174}
2175
2176PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002177 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2178 double g = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2179 double r = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002180
2181 colorOperator->fColorSpace = "DeviceRGB";
2182 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
2183 return kOK_PdfResult;
2184}
2185
2186PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2187 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2188}
2189
2190PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2191 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2192}
2193
2194PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
2195 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com59543d32013-06-18 22:00:40 +00002196 double k = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2197 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2198 double m = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2199 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002200
2201 colorOperator->fColorSpace = "DeviceCMYK";
2202 // TODO(edisonn): Set color.
2203 return kNYI_PdfResult;
2204}
2205
2206PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2207 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2208}
2209
2210PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2211 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2212}
2213
2214PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2215 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
2216 pdfContext->fGraphicsState.fHasClipPathToApply = true;
2217
2218 return kOK_PdfResult;
2219}
2220
2221PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2222 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
2223
2224#ifdef PDF_TRACE
2225 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
2226 printf("CLIP IS RECT\n");
2227 }
2228#endif
2229
2230 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
2231 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
2232 pdfContext->fGraphicsState.fHasClipPathToApply = true;
2233
2234 return kPartial_PdfResult;
2235}
2236
2237PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2238 *looper = new PdfCompatibilitySectionLooper();
2239 return kOK_PdfResult;
2240}
2241
2242PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2243#ifdef ASSERT_BAD_PDF_OPS
2244 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
2245 // have the assert when testing good pdfs.
2246#endif
2247 return kIgnoreError_PdfResult;
2248}
2249
2250PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2251 *looper = new PdfInlineImageLooper();
2252 return kOK_PdfResult;
2253}
2254
2255PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2256#ifdef ASSERT_BAD_PDF_OPS
2257 SkASSERT(false); // must be processed in inline image looper, but let's
2258 // have the assert when testing good pdfs.
2259#endif
2260 return kIgnoreError_PdfResult;
2261}
2262
2263PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2264#ifdef ASSERT_BAD_PDF_OPS
2265 SkASSERT(false); // must be processed in inline image looper, but let's
2266 // have the assert when testing good pdfs.
2267#endif
2268 return kIgnoreError_PdfResult;
2269}
2270
2271//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
2272PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002273 double lineWidth = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002274 pdfContext->fGraphicsState.fLineWidth = lineWidth;
2275
2276 return kOK_PdfResult;
2277}
2278
2279//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
2280PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002281 pdfContext->fObjectStack.pop();
2282 //double lineCap = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002283
2284 return kNYI_PdfResult;
2285}
2286
2287//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
2288PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002289 pdfContext->fObjectStack.pop();
2290 //double lineJoin = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002291
2292 return kNYI_PdfResult;
2293}
2294
2295//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
2296PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002297 pdfContext->fObjectStack.pop();
2298 //double miterLimit = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002299
2300 return kNYI_PdfResult;
2301}
2302
2303//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
2304//page 155).
2305PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002306 pdfContext->fObjectStack.pop();
2307 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002308
2309 return kNYI_PdfResult;
2310}
2311
2312//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
2313PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002314 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002315
2316 return kNYI_PdfResult;
2317}
2318
2319//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
2320//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
2321//fies the output device’s default flatness tolerance.
2322PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002323 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002324
2325 return kNYI_PdfResult;
2326}
2327
2328//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2329//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
2330PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002331 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002332
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00002333#ifdef PDF_TRACE
2334 std::string str;
2335#endif
2336
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002337 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com59543d32013-06-18 22:00:40 +00002338 const SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002339
2340 if (extGStateDictionary == NULL) {
2341#ifdef PDF_TRACE
2342 printf("ExtGState is NULL!\n");
2343#endif
2344 return kIgnoreError_PdfResult;
2345 }
2346
edisonn@google.com59543d32013-06-18 22:00:40 +00002347 SkPdfObject value = extGStateDictionary->get(name.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002348
2349#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002350// value->ToString(str);
2351// printf("gs object value: %s\n", str.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002352#endif
2353
edisonn@google.com59543d32013-06-18 22:00:40 +00002354 SkPdfGraphicsStateDictionary* gs = NULL;
2355 PodofoMapper::map(value, &gs);
edisonn@google.com68d15c82013-06-17 20:46:27 +00002356
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002357 // TODO(edisonn): now load all those properties in graphic state.
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00002358 if (gs == NULL) {
2359 return kIgnoreError_PdfResult;
2360 }
2361
2362 if (gs->has_CA()) {
2363 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA();
2364 }
2365
2366 if (gs->has_ca()) {
2367 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca();
2368 }
2369
2370 if (gs->has_LW()) {
2371 pdfContext->fGraphicsState.fLineWidth = gs->LW();
2372 }
2373
2374
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002375
2376 return kNYI_PdfResult;
2377}
2378
2379//charSpace Tc Set the character spacing, Tc
2380//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2381//Initial value: 0.
2382PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002383 double charSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002384 pdfContext->fGraphicsState.fCharSpace = charSpace;
2385
2386 return kOK_PdfResult;
2387}
2388
2389//wordSpace Tw Set the word spacing, T
2390//w
2391//, to wordSpace, which is a number expressed in unscaled
2392//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2393//value: 0.
2394PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002395 double wordSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002396 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2397
2398 return kOK_PdfResult;
2399}
2400
2401//scale Tz Set the horizontal scaling, Th
2402//, to (scale ˜ 100). scale is a number specifying the
2403//percentage of the normal width. Initial value: 100 (normal width).
2404PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002405 double scale = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002406
2407 return kNYI_PdfResult;
2408}
2409
2410//render Tr Set the text rendering mode, T
2411//mode, to render, which is an integer. Initial value: 0.
2412PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002413 double render = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002414
2415 return kNYI_PdfResult;
2416}
2417
2418//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2419//units. Initial value: 0.
2420PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002421 double rise = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002422
2423 return kNYI_PdfResult;
2424}
2425
2426//wx wy d0
2427PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002428 pdfContext->fObjectStack.pop();
2429 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002430
2431 return kNYI_PdfResult;
2432}
2433
2434//wx wy llx lly urx ury d1
2435PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002436 pdfContext->fObjectStack.pop();
2437 pdfContext->fObjectStack.pop();
2438 pdfContext->fObjectStack.pop();
2439 pdfContext->fObjectStack.pop();
2440 pdfContext->fObjectStack.pop();
2441 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002442
2443 return kNYI_PdfResult;
2444}
2445
2446//name sh
2447PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002448 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002449
2450 return kNYI_PdfResult;
2451}
2452
2453//name Do
2454PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002455 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002456
edisonn@google.com59543d32013-06-18 22:00:40 +00002457 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002458
edisonn@google.com59543d32013-06-18 22:00:40 +00002459 if (xObject == NULL) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002460#ifdef PDF_TRACE
2461 printf("XObject is NULL!\n");
2462#endif
2463 return kIgnoreError_PdfResult;
2464 }
2465
edisonn@google.com59543d32013-06-18 22:00:40 +00002466 SkPdfObject value = xObject->get(name.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002467
2468#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002469// value->ToString(str);
2470// printf("Do object value: %s\n", str.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002471#endif
2472
edisonn@google.com59543d32013-06-18 22:00:40 +00002473 return doXObject(pdfContext, canvas, value);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002474}
2475
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002476//tag MP Designate a marked-content point. tag is a name object indicating the role or
2477//significance of the point.
2478PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002479 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002480
2481 return kNYI_PdfResult;
2482}
2483
2484//tag properties DP Designate a marked-content point with an associated property list. tag is a
2485//name object indicating the role or significance of the point; properties is
2486//either an inline dictionary containing the property list or a name object
2487//associated with it in the Properties subdictionary of the current resource
2488//dictionary (see Section 9.5.1, “Property Lists”).
2489PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002490 pdfContext->fObjectStack.pop();
2491 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002492
2493 return kNYI_PdfResult;
2494}
2495
2496//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2497//tag is a name object indicating the role or significance of the sequence.
2498PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002499 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002500
2501 return kNYI_PdfResult;
2502}
2503
2504//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2505//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2506//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”).
2507PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002508 pdfContext->fObjectStack.pop();
2509 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002510
2511 return kNYI_PdfResult;
2512}
2513
2514//— EMC End a marked-content sequence begun by a BMC or BDC operator.
2515PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2516 return kNYI_PdfResult;
2517}
2518
2519void initPdfOperatorRenderes() {
2520 static bool gInitialized = false;
2521 if (gInitialized) {
2522 return;
2523 }
2524
2525 gPdfOps["q"] = PdfOp_q;
2526 gPdfOps["Q"] = PdfOp_Q;
2527 gPdfOps["cm"] = PdfOp_cm;
2528
2529 gPdfOps["TD"] = PdfOp_TD;
2530 gPdfOps["Td"] = PdfOp_Td;
2531 gPdfOps["Tm"] = PdfOp_Tm;
2532 gPdfOps["T*"] = PdfOp_T_star;
2533
2534 gPdfOps["m"] = PdfOp_m;
2535 gPdfOps["l"] = PdfOp_l;
2536 gPdfOps["c"] = PdfOp_c;
2537 gPdfOps["v"] = PdfOp_v;
2538 gPdfOps["y"] = PdfOp_y;
2539 gPdfOps["h"] = PdfOp_h;
2540 gPdfOps["re"] = PdfOp_re;
2541
2542 gPdfOps["S"] = PdfOp_S;
2543 gPdfOps["s"] = PdfOp_s;
2544 gPdfOps["f"] = PdfOp_f;
2545 gPdfOps["F"] = PdfOp_F;
2546 gPdfOps["f*"] = PdfOp_f_star;
2547 gPdfOps["B"] = PdfOp_B;
2548 gPdfOps["B*"] = PdfOp_B_star;
2549 gPdfOps["b"] = PdfOp_b;
2550 gPdfOps["b*"] = PdfOp_b_star;
2551 gPdfOps["n"] = PdfOp_n;
2552
2553 gPdfOps["BT"] = PdfOp_BT;
2554 gPdfOps["ET"] = PdfOp_ET;
2555
2556 gPdfOps["Tj"] = PdfOp_Tj;
2557 gPdfOps["'"] = PdfOp_quote;
2558 gPdfOps["\""] = PdfOp_doublequote;
2559 gPdfOps["TJ"] = PdfOp_TJ;
2560
2561 gPdfOps["CS"] = PdfOp_CS;
2562 gPdfOps["cs"] = PdfOp_cs;
2563 gPdfOps["SC"] = PdfOp_SC;
2564 gPdfOps["SCN"] = PdfOp_SCN;
2565 gPdfOps["sc"] = PdfOp_sc;
2566 gPdfOps["scn"] = PdfOp_scn;
2567 gPdfOps["G"] = PdfOp_G;
2568 gPdfOps["g"] = PdfOp_g;
2569 gPdfOps["RG"] = PdfOp_RG;
2570 gPdfOps["rg"] = PdfOp_rg;
2571 gPdfOps["K"] = PdfOp_K;
2572 gPdfOps["k"] = PdfOp_k;
2573
2574 gPdfOps["W"] = PdfOp_W;
2575 gPdfOps["W*"] = PdfOp_W_star;
2576
2577 gPdfOps["BX"] = PdfOp_BX;
2578 gPdfOps["EX"] = PdfOp_EX;
2579
2580 gPdfOps["BI"] = PdfOp_BI;
2581 gPdfOps["ID"] = PdfOp_ID;
2582 gPdfOps["EI"] = PdfOp_EI;
2583
2584 gPdfOps["w"] = PdfOp_w;
2585 gPdfOps["J"] = PdfOp_J;
2586 gPdfOps["j"] = PdfOp_j;
2587 gPdfOps["M"] = PdfOp_M;
2588 gPdfOps["d"] = PdfOp_d;
2589 gPdfOps["ri"] = PdfOp_ri;
2590 gPdfOps["i"] = PdfOp_i;
2591 gPdfOps["gs"] = PdfOp_gs;
2592
2593 gPdfOps["Tc"] = PdfOp_Tc;
2594 gPdfOps["Tw"] = PdfOp_Tw;
2595 gPdfOps["Tz"] = PdfOp_Tz;
2596 gPdfOps["TL"] = PdfOp_TL;
2597 gPdfOps["Tf"] = PdfOp_Tf;
2598 gPdfOps["Tr"] = PdfOp_Tr;
2599 gPdfOps["Ts"] = PdfOp_Ts;
2600
2601 gPdfOps["d0"] = PdfOp_d0;
2602 gPdfOps["d1"] = PdfOp_d1;
2603
2604 gPdfOps["sh"] = PdfOp_sh;
2605
2606 gPdfOps["Do"] = PdfOp_Do;
2607
2608 gPdfOps["MP"] = PdfOp_MP;
2609 gPdfOps["DP"] = PdfOp_DP;
2610 gPdfOps["BMC"] = PdfOp_BMC;
2611 gPdfOps["BDC"] = PdfOp_BDC;
2612 gPdfOps["EMC"] = PdfOp_EMC;
2613
2614 gInitialized = true;
2615}
2616
2617void reportPdfRenderStats() {
2618 std::map<std::string, int>::iterator iter;
2619
2620 for (int i = 0 ; i < kCount_PdfResult; i++) {
2621 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++iter) {
2622 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_str(), iter->second);
2623 }
2624 }
2625}
2626
2627PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002628 if (token.fType == kKeyword_TokenType)
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002629 {
2630 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com59543d32013-06-18 22:00:40 +00002631 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[token.fKeyword];
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002632 if (pdfOperatorRenderer) {
2633 // caller, main work is done by pdfOperatorRenderer(...)
2634 PdfTokenLooper* childLooper = NULL;
edisonn@google.com59543d32013-06-18 22:00:40 +00002635 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)][token.fKeyword]++;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002636
2637 if (childLooper) {
2638 childLooper->setUp(this);
2639 childLooper->loop();
2640 delete childLooper;
2641 }
2642 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002643 gRenderStats[kUnsupported_PdfResult][token.fKeyword]++;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002644 }
2645 }
edisonn@google.com59543d32013-06-18 22:00:40 +00002646 else if (token.fType == kObject_TokenType)
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002647 {
edisonn@google.com59543d32013-06-18 22:00:40 +00002648 fPdfContext->fObjectStack.push( token.fObject );
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002649 }
edisonn@google.com59543d32013-06-18 22:00:40 +00002650 else if ( token.fType == kImageData_TokenType) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002651 // TODO(edisonn): implement inline image.
2652 }
2653 else {
2654 return kIgnoreError_PdfResult;
2655 }
2656 return kOK_PdfResult;
2657}
2658
2659void PdfMainLooper::loop() {
2660 PdfToken token;
2661 while (readToken(fTokenizer, &token)) {
2662 consumeToken(token);
2663 }
2664}
2665
2666PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
2667 //pdfContext.fInlineImage.fKeyValuePairs[key] = value;
2668 return kNYI_PdfResult;
2669}
2670
2671void PdfInlineImageLooper::loop() {
2672 PdfToken token;
2673 while (readToken(fTokenizer, &token)) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002674 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002675 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2676 looper->setUp(this);
2677 looper->loop();
2678 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002679 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002680 done();
2681 return;
2682 }
2683
2684 consumeToken(token);
2685 }
2686 }
2687 // TODO(edisonn): report error/warning, EOF without EI.
2688}
2689
2690PdfResult PdfInlineImageLooper::done() {
2691
2692 // TODO(edisonn): long to short names
2693 // TODO(edisonn): set properties in a map
2694 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
2695 // the stream.
2696
2697 SkBitmap bitmap;
2698 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
2699
2700 // TODO(edisonn): matrix use.
2701 // Draw dummy red square, to show the prezence of the inline image.
2702 fCanvas->drawBitmap(bitmap,
2703 SkDoubleToScalar(0),
2704 SkDoubleToScalar(0),
2705 NULL);
2706 return kNYI_PdfResult;
2707}
2708
2709PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2710 return fParent->consumeToken(token);
2711}
2712
2713void PdfCompatibilitySectionLooper::loop() {
2714 // TODO(edisonn): save stacks position, or create a new stack?
2715 // TODO(edisonn): what happens if we pop out more variables then when we started?
2716 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2717 // pop-ing too much will not affect outside the section.
2718 PdfToken token;
2719 while (readToken(fTokenizer, &token)) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002720 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002721 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2722 looper->setUp(this);
2723 looper->loop();
2724 delete looper;
2725 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002726 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002727 fParent->consumeToken(token);
2728 }
2729 }
2730 // TODO(edisonn): restore stack.
2731}
2732
2733// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2734// TODO(edisonn): Add API for Forms viewing and editing
2735// e.g. SkBitmap getPage(int page);
2736// int formsCount();
2737// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2738// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2739// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2740// resources needed, so the preview is fast.
2741// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2742// references automatically.
2743class SkPdfViewer : public SkRefCnt {
2744public:
2745
2746 bool load(const SkString inputFileName, SkPicture* out) {
2747
2748 initPdfOperatorRenderes();
2749
2750 try
2751 {
2752 std::cout << "Init: " << inputFileName.c_str() << std::endl;
2753
edisonn@google.com59543d32013-06-18 22:00:40 +00002754 SkPdfDoc doc(inputFileName.c_str());
2755 if( !doc.pages() )
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002756 {
2757 std::cout << "ERROR: Empty Document" << inputFileName.c_str() << std::endl;
2758 return false;
2759 } else {
2760
edisonn@google.com59543d32013-06-18 22:00:40 +00002761 for (int pn = 0; pn < doc.pages(); ++pn) {
2762 SkPdfPageObjectDictionary* page = doc.page(pn);
2763
2764 // TODO(edisonn): implement inheritance properties as per PDF spec
2765 //SkRect rect = page->MediaBox();
2766 SkRect rect = doc.MediaBox(pn);
2767
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002768#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002769 printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rect.width()), SkScalarToDouble(rect.height()));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002770#endif
2771
2772 // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to use?
2773
2774 SkBitmap bitmap;
2775#ifdef PDF_DEBUG_3X
edisonn@google.com59543d32013-06-18 22:00:40 +00002776 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()))
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002777#else
edisonn@google.com59543d32013-06-18 22:00:40 +00002778 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002779#endif
2780 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
2781 SkCanvas canvas(device);
2782
edisonn@google.comff278442013-06-21 21:03:15 +00002783 SkPdfTokenizer* tokenizer = doc.tokenizerOfPage(pn);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002784
edisonn@google.com59543d32013-06-18 22:00:40 +00002785 PdfContext pdfContext(doc);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002786 pdfContext.fOriginalMatrix = SkMatrix::I();
edisonn@google.com59543d32013-06-18 22:00:40 +00002787 pdfContext.fGraphicsState.fResources = NULL;
2788 PodofoMapper::map(*page->Resources(), &pdfContext.fGraphicsState.fResources);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002789
2790 gPdfContext = &pdfContext;
2791 gDumpBitmap = &bitmap;
2792 gDumpCanvas = &canvas;
2793
2794
2795 // TODO(edisonn): get matrix stuff right.
2796 // TODO(edisonn): add DPI/scale/zoom.
2797 SkScalar z = SkIntToScalar(0);
edisonn@google.com59543d32013-06-18 22:00:40 +00002798 SkScalar w = rect.width();
2799 SkScalar h = rect.height();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002800
2801 SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(w, z), SkPoint::Make(w, h), SkPoint::Make(z, h)};
2802// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2803
2804 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2805 // TODO(edisonn): add flagg for no clipping.
2806 // Use larger image to make sure we do not draw anything outside of page
2807 // could be used in tests.
2808
2809#ifdef PDF_DEBUG_3X
2810 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)};
2811#else
2812 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2813#endif
2814 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2815 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2816
2817 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2818 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2819
2820 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2821 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2822
2823 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2824 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2825
2826
2827 pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
2828 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
2829 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
2830
2831 canvas.setMatrix(pdfContext.fOriginalMatrix);
2832
2833#ifndef PDF_DEBUG_NO_PAGE_CLIPING
2834 canvas.clipRect(SkRect::MakeXYWH(z, z, w, h), SkRegion::kIntersect_Op, true);
2835#endif
2836
edisonn@google.com59543d32013-06-18 22:00:40 +00002837 PdfMainLooper looper(NULL, tokenizer, &pdfContext, &canvas);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002838 looper.loop();
2839
edisonn@google.comff278442013-06-21 21:03:15 +00002840 delete tokenizer;
2841
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002842 canvas.flush();
2843
2844 SkString out;
2845 out.appendf("%s-%i.png", inputFileName.c_str(), pn);
2846 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
2847 }
2848 return true;
2849 }
2850 }
2851 catch( PdfError & e )
2852 {
2853 std::cout << "ERROR: PDF can't be parsed!" << inputFileName.c_str() << std::endl;
2854 return false;
2855 }
2856
2857 return true;
2858 }
2859 bool write(void*) const { return false; }
2860};
2861
2862
2863
2864/**
2865 * Given list of directories and files to use as input, expects to find .pdf
2866 * files and it will convert them to .png files writing them in the same directory
2867 * one file for each page.
2868 *
2869 * Returns zero exit code if all .pdf files were converted successfully,
2870 * otherwise returns error code 1.
2871 */
2872
2873static const char PDF_FILE_EXTENSION[] = "pdf";
2874static const char PNG_FILE_EXTENSION[] = "png";
2875
2876// TODO(edisonn): add ability to write to a new directory.
2877static void usage(const char* argv0) {
2878 SkDebugf("PDF to PNG rendering tool\n");
2879 SkDebugf("\n"
2880"Usage: \n"
2881" %s <input>... -w <outputDir> \n"
2882, argv0);
2883 SkDebugf("\n\n");
2884 SkDebugf(
2885" input: A list of directories and files to use as input. Files are\n"
2886" expected to have the .skp extension.\n\n");
2887 SkDebugf(
2888" outputDir: directory to write the rendered pdfs.\n\n");
2889 SkDebugf("\n");
2890}
2891
2892/** Replaces the extension of a file.
2893 * @param path File name whose extension will be changed.
2894 * @param old_extension The old extension.
2895 * @param new_extension The new extension.
2896 * @returns false if the file did not has the expected extension.
2897 * if false is returned, contents of path are undefined.
2898 */
2899static bool replace_filename_extension(SkString* path,
2900 const char old_extension[],
2901 const char new_extension[]) {
2902 if (path->endsWith(old_extension)) {
2903 path->remove(path->size() - strlen(old_extension),
2904 strlen(old_extension));
2905 if (!path->endsWith(".")) {
2906 return false;
2907 }
2908 path->append(new_extension);
2909 return true;
2910 }
2911 return false;
2912}
2913
2914/** Builds the output filename. path = dir/name, and it replaces expected
2915 * .skp extension with .pdf extention.
2916 * @param path Output filename.
2917 * @param name The name of the file.
2918 * @returns false if the file did not has the expected extension.
2919 * if false is returned, contents of path are undefined.
2920 */
2921static bool make_output_filepath(SkString* path, const SkString& dir,
2922 const SkString& name) {
2923 sk_tools::make_filepath(path, dir, name);
2924 return replace_filename_extension(path,
2925 PDF_FILE_EXTENSION,
2926 PNG_FILE_EXTENSION);
2927}
2928
2929/** Write the output of pdf renderer to a file.
2930 * @param outputDir Output dir.
2931 * @param inputFilename The skp file that was read.
2932 * @param renderer The object responsible to write the pdf file.
2933 */
2934static bool write_output(const SkString& outputDir,
2935 const SkString& inputFilename,
2936 const SkPdfViewer& renderer) {
2937 if (outputDir.isEmpty()) {
2938 SkDynamicMemoryWStream stream;
2939 renderer.write(&stream);
2940 return true;
2941 }
2942
2943 SkString outputPath;
2944 if (!make_output_filepath(&outputPath, outputDir, inputFilename)) {
2945 return false;
2946 }
2947
2948 SkFILEWStream stream(outputPath.c_str());
2949 if (!stream.isValid()) {
2950 SkDebugf("Could not write to file %s\n", outputPath.c_str());
2951 return false;
2952 }
2953 renderer.write(&stream);
2954
2955 return true;
2956}
2957
2958/** Reads an skp file, renders it to pdf and writes the output to a pdf file
2959 * @param inputPath The skp file to be read.
2960 * @param outputDir Output dir.
2961 * @param renderer The object responsible to render the skp object into pdf.
2962 */
2963static bool parse_pdf(const SkString& inputPath, const SkString& outputDir,
2964 SkPdfViewer& renderer) {
2965 SkString inputFilename;
2966 sk_tools::get_basename(&inputFilename, inputPath);
2967
2968 SkFILEStream inputStream;
2969 inputStream.setPath(inputPath.c_str());
2970 if (!inputStream.isValid()) {
2971 SkDebugf("Could not open file %s\n", inputPath.c_str());
2972 return false;
2973 }
2974
2975 bool success = false;
2976
2977 success = renderer.load(inputPath, NULL);
2978
2979
2980// success = write_output(outputDir, inputFilename, renderer);
2981
2982 //renderer.end();
2983 return success;
2984}
2985
2986/** For each file in the directory or for the file passed in input, call
2987 * parse_pdf.
2988 * @param input A directory or an pdf file.
2989 * @param outputDir Output dir.
2990 * @param renderer The object responsible to render the skp object into pdf.
2991 */
2992static int process_input(const SkString& input, const SkString& outputDir,
2993 SkPdfViewer& renderer) {
2994 int failures = 0;
2995 if (sk_isdir(input.c_str())) {
2996 SkOSFile::Iter iter(input.c_str(), PDF_FILE_EXTENSION);
2997 SkString inputFilename;
2998 while (iter.next(&inputFilename)) {
2999 SkString inputPath;
3000 sk_tools::make_filepath(&inputPath, input, inputFilename);
3001 if (!parse_pdf(inputPath, outputDir, renderer)) {
3002 ++failures;
3003 }
3004 }
3005 } else {
3006 SkString inputPath(input);
3007 if (!parse_pdf(inputPath, outputDir, renderer)) {
3008 ++failures;
3009 }
3010 }
3011 return failures;
3012}
3013
3014static void parse_commandline(int argc, char* const argv[],
3015 SkTArray<SkString>* inputs,
3016 SkString* outputDir) {
3017 const char* argv0 = argv[0];
3018 char* const* stop = argv + argc;
3019
3020 for (++argv; argv < stop; ++argv) {
3021 if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
3022 usage(argv0);
3023 exit(-1);
3024 } else if (0 == strcmp(*argv, "-w")) {
3025 ++argv;
3026 if (argv >= stop) {
3027 SkDebugf("Missing outputDir for -w\n");
3028 usage(argv0);
3029 exit(-1);
3030 }
3031 *outputDir = SkString(*argv);
3032 } else {
3033 inputs->push_back(SkString(*argv));
3034 }
3035 }
3036
3037 if (inputs->count() < 1) {
3038 usage(argv0);
3039 exit(-1);
3040 }
3041}
3042
3043int tool_main(int argc, char** argv);
3044int tool_main(int argc, char** argv) {
3045 SkAutoGraphics ag;
3046 SkTArray<SkString> inputs;
3047
3048 SkAutoTUnref<SkPdfViewer>
3049 renderer(SkNEW(SkPdfViewer));
3050 SkASSERT(renderer.get());
3051
3052 SkString outputDir;
3053 parse_commandline(argc, argv, &inputs, &outputDir);
3054
3055 int failures = 0;
3056 for (int i = 0; i < inputs.count(); i ++) {
3057 failures += process_input(inputs[i], outputDir, *renderer);
3058 }
3059
3060 reportPdfRenderStats();
3061
3062 if (failures != 0) {
3063 SkDebugf("Failed to render %i PDFs.\n", failures);
3064 return 1;
3065 }
3066
3067 return 0;
3068}
3069
3070#if !defined SK_BUILD_FOR_IOS
3071int main(int argc, char * const argv[]) {
3072 return tool_main(argc, (char**) argv);
3073}
3074#endif