blob: 877849eff4a7adfd476028559328a9c3a3f24ddb [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
edisonn@google.com1be794f2013-06-21 21:43:09 +0000146bool skpdfmap(const PdfMemDocument& podofoDoc, const PdfObject& podofoObj, SkPdfObject** out);
edisonn@google.com1277cf02013-06-17 23:36:45 +0000147
edisonn@google.com59543d32013-06-18 22:00:40 +0000148#include "SkPdfHeaders_autogen.h"
149#include "SkPdfPodofoMapper_autogen.h"
150#include "SkPdfParser.h"
edisonn@google.comff278442013-06-21 21:03:15 +0000151#include "SkPdfFont.h"
152
153// TODO(edisonn): fix the mess with the files.
154#include "SkPdfFont.cpp"
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000155
edisonn@google.com1be794f2013-06-21 21:43:09 +0000156bool skpdfmap(const PdfMemDocument& podofoDoc, const PdfObject& podofoObj, SkPdfObject** out) {
157 return PodofoMapper::map(podofoDoc, podofoObj, out);
158}
159
160
edisonn@google.com1277cf02013-06-17 23:36:45 +0000161bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
162 const PdfDictionary& dict,
163 const char* key,
164 const char* abr,
165 SkPdfArray* data) {return false;}
166
167bool FileSpecFromDictionary(const PdfMemDocument* pdfDoc,
168 const PdfDictionary& dict,
169 const char* key,
170 const char* abr,
171 SkPdfFileSpec* data) {return false;}
172
173bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
174 const PdfDictionary& dict,
175 const char* key,
176 const char* abr,
edisonn@google.comff278442013-06-21 21:03:15 +0000177 SkPdfStream** data);
edisonn@google.com1277cf02013-06-17 23:36:45 +0000178
179bool TreeFromDictionary(const PdfMemDocument* pdfDoc,
180 const PdfDictionary& dict,
181 const char* key,
182 const char* abr,
183 SkPdfTree** data) {return false;}
184
185bool DateFromDictionary(const PdfMemDocument* pdfDoc,
186 const PdfDictionary& dict,
187 const char* key,
188 const char* abr,
189 SkPdfDate* data) {return false;}
190
edisonn@google.com1277cf02013-06-17 23:36:45 +0000191bool FunctionFromDictionary(const PdfMemDocument* pdfDoc,
192 const PdfDictionary& dict,
193 const char* key,
194 const char* abr,
195 SkPdfFunction* data) {return false;}
196
197
198
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000199/*
edisonn@google.com68d15c82013-06-17 20:46:27 +0000200 * TODO(edisonn):
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000201 * - all font types and all ppdf font features
202 * - word spacing
203 * - load font for baidu.pdf
204 * - load font for youtube.pdf
205 * - parser for pdf from the definition already available in pdfspec_autogen.py
edisonn@google.comff278442013-06-21 21:03:15 +0000206 * - all docs from ~/work
207 * - encapsulate podofo in the pdf api so the skpdf does not know anything about podofo ... in progress
208 * - load gs/ especially smask and already known prop (skp) ... in progress
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000209 * - wrapper on classes for customizations? e.g.
210 * SkPdfPageObjectVanila - has only the basic loaders/getters
211 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
212 * need to find a nice object model for all this with constructors and factories
213 * - deal with inheritable automatically ?
214 * - deal with specific type in spec directly, add all dictionary types to known types
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000215*/
216
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000217
218// TODO(edisonn): move in trace util.
219#ifdef PDF_TRACE
220static void SkTraceMatrix(const SkMatrix& matrix, const char* sz = "") {
221 printf("SkMatrix %s ", sz);
222 for (int i = 0 ; i < 9 ; i++) {
223 printf("%f ", SkScalarToDouble(matrix.get(i)));
224 }
225 printf("\n");
226}
227#else
228#define SkTraceMatrix(a,b)
229#endif
230
231using namespace std;
232using namespace PoDoFo;
233
234// Utilities
235static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
236 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
237
238 bitmap->allocPixels();
239 bitmap->eraseColor(color);
240}
241
242// TODO(edisonn): synonyms? DeviceRGB and RGB ...
243int GetColorSpaceComponents(const std::string& colorSpace) {
244 if (colorSpace == "DeviceCMYK") {
245 return 4;
246 } else if (colorSpace == "DeviceGray" ||
247 colorSpace == "CalGray" ||
248 colorSpace == "Indexed") {
249 return 1;
250 } else if (colorSpace == "DeviceRGB" ||
251 colorSpace == "CalRGB" ||
252 colorSpace == "Lab") {
253 return 3;
254 } else {
255 return 0;
256 }
257}
258
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000259const PdfObject* resolveReferenceObject(const PdfMemDocument* pdfDoc,
260 const PdfObject* obj,
edisonn@google.com59543d32013-06-18 22:00:40 +0000261 bool resolveOneElementArrays) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000262 while (obj && (obj->IsReference() || (resolveOneElementArrays &&
263 obj->IsArray() &&
264 obj->GetArray().GetSize() == 1))) {
265 if (obj->IsReference()) {
266 // We need to force the non const, the only update we will do is for recurssion checks.
267 PdfReference& ref = (PdfReference&)obj->GetReference();
268 obj = pdfDoc->GetObjects().GetObject(ref);
269 } else {
270 obj = &obj->GetArray()[0];
271 }
272 }
273
274 return obj;
275}
276
277static SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
278 SkMatrix matrix;
279 matrix.setAll(SkDoubleToScalar(array[0]),
280 SkDoubleToScalar(array[2]),
281 SkDoubleToScalar(array[4]),
282 SkDoubleToScalar(array[1]),
283 SkDoubleToScalar(array[3]),
284 SkDoubleToScalar(array[5]),
285 SkDoubleToScalar(0),
286 SkDoubleToScalar(0),
287 SkDoubleToScalar(1));
288
289 return matrix;
290}
291
292// TODO(edisonn): better class design.
293struct PdfColorOperator {
294 std::string fColorSpace; // TODO(edisonn): use SkString
295 SkColor fColor;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000296 double fOpacity; // ca or CA
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000297 // TODO(edisonn): add here other color space options.
298
299 void setRGBColor(SkColor color) {
300 // TODO(edisonn): ASSERT DeviceRGB is the color space.
301 fColor = color;
302 }
303 // TODO(edisonn): double check the default values for all fields.
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000304 PdfColorOperator() : fColor(SK_ColorBLACK), fOpacity(1) {}
305
306 void applyGraphicsState(SkPaint* paint) {
307 paint->setColor(SkColorSetA(fColor, fOpacity * 255));
308 }
309
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000310};
311
312// TODO(edisonn): better class design.
313struct PdfGraphicsState {
314 SkMatrix fMatrix;
315 SkMatrix fMatrixTm;
316 SkMatrix fMatrixTlm;
317
318 double fCurPosX;
319 double fCurPosY;
320
321 double fCurFontSize;
322 bool fTextBlock;
323 PdfFont* fCurFont;
edisonn@google.comff278442013-06-21 21:03:15 +0000324 SkPdfFont* fSkFont;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000325 SkPath fPath;
326 bool fPathClosed;
327
328 // Clip that is applied after the drawing is done!!!
329 bool fHasClipPathToApply;
330 SkPath fClipPath;
331
332 PdfColorOperator fStroking;
333 PdfColorOperator fNonStroking;
334
335 double fLineWidth;
336 double fTextLeading;
337 double fWordSpace;
338 double fCharSpace;
339
edisonn@google.com59543d32013-06-18 22:00:40 +0000340 SkPdfResourceDictionary* fResources;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000341
342 SkBitmap fSMask;
343
344 PdfGraphicsState() {
345 fCurPosX = 0.0;
346 fCurPosY = 0.0;
347 fCurFontSize = 0.0;
348 fTextBlock = false;
349 fCurFont = NULL;
350 fMatrix = SkMatrix::I();
351 fMatrixTm = SkMatrix::I();
352 fMatrixTlm = SkMatrix::I();
353 fPathClosed = true;
354 fLineWidth = 0;
355 fTextLeading = 0;
356 fWordSpace = 0;
357 fCharSpace = 0;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000358 fHasClipPathToApply = false;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000359 fResources = NULL;
edisonn@google.comff278442013-06-21 21:03:15 +0000360 fSkFont = NULL;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000361 }
362
363 void applyGraphicsState(SkPaint* paint, bool stroking) {
364 if (stroking) {
365 fStroking.applyGraphicsState(paint);
366 } else {
367 fNonStroking.applyGraphicsState(paint);
368 }
369
370 // TODO(edisonn): get this from pdfContext->options,
371 // or pdfContext->addPaintOptions(&paint);
372 paint->setAntiAlias(true);
373
374 // TODO(edisonn): dashing, miter, ...
375 paint->setStrokeWidth(SkDoubleToScalar(fLineWidth));
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000376 }
377};
378
379// TODO(edisonn): better class design.
380struct PdfInlineImage {
381 std::map<std::string, std::string> fKeyValuePairs;
382 std::string fImageData;
383
384};
385
386// TODO(edisonn): better class design.
387struct PdfContext {
edisonn@google.com59543d32013-06-18 22:00:40 +0000388 std::stack<SkPdfObject*> fObjectStack;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000389 std::stack<PdfGraphicsState> fStateStack;
390 PdfGraphicsState fGraphicsState;
edisonn@google.com59543d32013-06-18 22:00:40 +0000391 SkPdfDoc& fPdfDoc;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000392 SkMatrix fOriginalMatrix;
393
394 PdfInlineImage fInlineImage;
395
edisonn@google.com59543d32013-06-18 22:00:40 +0000396 PdfContext(SkPdfDoc& doc) : fPdfDoc(doc) {}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000397
398};
399
400// TODO(edisonn): temporary code, to report how much of the PDF we actually think we rendered.
401enum PdfResult {
402 kOK_PdfResult,
403 kPartial_PdfResult,
404 kNYI_PdfResult,
405 kIgnoreError_PdfResult,
406 kError_PdfResult,
407 kUnsupported_PdfResult,
408
409 kCount_PdfResult
410};
411
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000412PdfContext* gPdfContext = NULL;
413SkBitmap* gDumpBitmap = NULL;
414SkCanvas* gDumpCanvas = NULL;
415char gLastKeyword[100] = "";
416int gLastOpKeyword = -1;
edisonn@google.com59543d32013-06-18 22:00:40 +0000417char 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 +0000418int gReadOp = 0;
419
420
421
422bool hasVisualEffect(const char* pdfOp) {
423 return true;
424 if (*pdfOp == '\0') return false;
425
426 char markedPdfOp[100] = ",";
427 strcat(markedPdfOp, pdfOp);
428 strcat(markedPdfOp, ",");
429
430 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
431}
432
433// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.comff278442013-06-21 21:03:15 +0000434static bool readToken(SkPdfTokenizer* fTokenizer, PdfToken* token) {
435 bool ret = fTokenizer->readToken(token);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000436
437 gReadOp++;
438
439#ifdef PDF_TRACE_DIFF_IN_PNG
440 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
441 // the numbar and name of last operation, so the file name will reflect op that changed.
442 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
443 gDumpCanvas->flush();
444
445 SkBitmap bitmap;
446 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
447
448 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
449
450 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
451 SkCanvas canvas(device);
452
453 // draw context stuff here
454 SkPaint blueBorder;
455 blueBorder.setColor(SK_ColorBLUE);
456 blueBorder.setStyle(SkPaint::kStroke_Style);
457 blueBorder.setTextSize(SkDoubleToScalar(20));
458
459 SkString str;
460
461 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
462 if (clipStack) {
463 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
464 const SkClipStack::Element* elem;
465 double y = 0;
466 int total = 0;
467 while (elem = iter.next()) {
468 total++;
469 y += 30;
470
471 switch (elem->getType()) {
472 case SkClipStack::Element::kRect_Type:
473 canvas.drawRect(elem->getRect(), blueBorder);
474 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
475 break;
476 case SkClipStack::Element::kPath_Type:
477 canvas.drawPath(elem->getPath(), blueBorder);
478 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
479 break;
480 case SkClipStack::Element::kEmpty_Type:
481 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
482 break;
483 default:
484 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
485 break;
486 }
487 }
488
489 y += 30;
490 str.printf("Number of clips in stack: %i", total);
491 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
492 }
493
494 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
495 SkPath clipPath;
496 if (clipRegion.getBoundaryPath(&clipPath)) {
497 SkPaint redBorder;
498 redBorder.setColor(SK_ColorRED);
499 redBorder.setStyle(SkPaint::kStroke_Style);
500 canvas.drawPath(clipPath, redBorder);
501 }
502
503 canvas.flush();
504
505 SkString out;
506
507 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
508 // ... and other properties, to be able to debug th code easily
509
510 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
511 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
512 }
513
edisonn@google.com59543d32013-06-18 22:00:40 +0000514 if (token->fType == kKeyword_TokenType) {
515 strcpy(gLastKeyword, token->fKeyword);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000516 gLastOpKeyword = gReadOp;
517 } else {
518 strcpy(gLastKeyword, "");
519 }
520#endif
521
522 return ret;
523}
524
525// TODO(edisonn): Document PdfTokenLooper and subclasses.
526class PdfTokenLooper {
527protected:
528 PdfTokenLooper* fParent;
edisonn@google.comff278442013-06-21 21:03:15 +0000529 SkPdfTokenizer* fTokenizer;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000530 PdfContext* fPdfContext;
531 SkCanvas* fCanvas;
532
533public:
534 PdfTokenLooper(PdfTokenLooper* parent,
edisonn@google.comff278442013-06-21 21:03:15 +0000535 SkPdfTokenizer* tokenizer,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000536 PdfContext* pdfContext,
537 SkCanvas* canvas)
538 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
539
540 virtual PdfResult consumeToken(PdfToken& token) = 0;
541 virtual void loop() = 0;
542
543 void setUp(PdfTokenLooper* parent) {
544 fParent = parent;
545 fTokenizer = parent->fTokenizer;
546 fPdfContext = parent->fPdfContext;
547 fCanvas = parent->fCanvas;
548 }
549};
550
551class PdfMainLooper : public PdfTokenLooper {
552public:
553 PdfMainLooper(PdfTokenLooper* parent,
edisonn@google.comff278442013-06-21 21:03:15 +0000554 SkPdfTokenizer* tokenizer,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000555 PdfContext* pdfContext,
556 SkCanvas* canvas)
557 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
558
559 virtual PdfResult consumeToken(PdfToken& token);
560 virtual void loop();
561};
562
563class PdfInlineImageLooper : public PdfTokenLooper {
564public:
565 PdfInlineImageLooper()
edisonn@google.comff278442013-06-21 21:03:15 +0000566 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000567
568 virtual PdfResult consumeToken(PdfToken& token);
569 virtual void loop();
570 PdfResult done();
571};
572
573class PdfCompatibilitySectionLooper : public PdfTokenLooper {
574public:
575 PdfCompatibilitySectionLooper()
edisonn@google.comff278442013-06-21 21:03:15 +0000576 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000577
578 virtual PdfResult consumeToken(PdfToken& token);
579 virtual void loop();
580};
581
582typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
583
584map<std::string, PdfOperatorRenderer> gPdfOps;
585
586map<std::string, int> gRenderStats[kCount_PdfResult];
587
588char* gRenderStatsNames[kCount_PdfResult] = {
589 "Success",
590 "Partially implemented",
591 "Not yet implemented",
592 "Ignore Error",
593 "Error",
594 "Unsupported/Unknown"
595};
596
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000597static SkTypeface* SkTypefaceFromPdfFont(PdfFont* font) {
edisonn@google.comff278442013-06-21 21:03:15 +0000598 if (font == NULL) {
599 return SkTypeface::CreateFromName("Times New Roman", SkTypeface::kNormal);
600 }
601
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000602 PdfObject* fontObject = font->GetObject();
603
604 PdfObject* pBaseFont = NULL;
605 // TODO(edisonn): warning, PoDoFo has a bug in PdfFont constructor, does not call InitVars()
606 // for now fixed locally.
607 pBaseFont = fontObject->GetIndirectKey( "BaseFont" );
608 const char* pszBaseFontName = pBaseFont->GetName().GetName().c_str();
609
610#ifdef PDF_TRACE
611 std::string str;
612 fontObject->ToString(str);
613 printf("Base Font Name: %s\n", pszBaseFontName);
614 printf("Font Object Data: %s\n", str.c_str());
615#endif
616
617 SkTypeface* typeface = SkTypefaceFromPdfStandardFont(pszBaseFontName, font->IsBold(), font->IsItalic());
618
619 if (typeface != NULL) {
620 return typeface;
621 }
622
623 char name[1000];
624 // HACK
625 strncpy(name, pszBaseFontName, 1000);
626 char* comma = strstr(name, ",");
627 char* dash = strstr(name, "-");
628 if (comma) *comma = '\0';
629 if (dash) *dash = '\0';
630
631 typeface = SkTypeface::CreateFromName(
632 name,
633 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
634 (font->IsItalic() ? SkTypeface::kItalic : 0)));
635
636 if (typeface != NULL) {
637#ifdef PDF_TRACE
638 printf("HACKED FONT found %s\n", name);
639#endif
640 return typeface;
641 }
642
643#ifdef PDF_TRACE
644 printf("FONT_NOT_FOUND %s\n", pszBaseFontName);
645#endif
646
647 // TODO(edisonn): Report Warning, NYI
648 return SkTypeface::CreateFromName(
649 "Times New Roman",
650 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
651 (font->IsItalic() ? SkTypeface::kItalic : 0)));
652}
653
edisonn@google.comff278442013-06-21 21:03:15 +0000654
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000655// TODO(edisonn): move this code in podofo, so we don't have to fix the font.
656// This logic needs to be moved in PdfEncodingObjectFactory::CreateEncoding
657std::map<PdfFont*, PdfCMapEncoding*> gFontsFixed;
658PdfEncoding* FixPdfFont(PdfContext* pdfContext, PdfFont* fCurFont) {
659 // TODO(edisonn): and is Identity-H
660 if (gFontsFixed.find(fCurFont) == gFontsFixed.end()) {
661 if (fCurFont->GetObject()->IsDictionary() && fCurFont->GetObject()->GetDictionary().HasKey(PdfName("ToUnicode"))) {
662 PdfCMapEncoding* enc = new PdfCMapEncoding(
663 fCurFont->GetObject(),
edisonn@google.com59543d32013-06-18 22:00:40 +0000664 (PdfObject*)resolveReferenceObject(&pdfContext->fPdfDoc.podofo(),
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000665 fCurFont->GetObject()->GetDictionary().GetKey(PdfName("ToUnicode"))),
666 PdfCMapEncoding::eBaseEncoding_Identity); // todo, read the base encoding
667 gFontsFixed[fCurFont] = enc;
668 return enc;
669 }
670
671 return NULL;
672 }
673
674 return gFontsFixed[fCurFont];
675}
676
677PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +0000678 const SkPdfObject* str,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000679 SkCanvas* canvas)
680{
edisonn@google.comff278442013-06-21 21:03:15 +0000681
682 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
683 if (skfont == NULL) {
684 skfont = SkPdfFont::Default();
685 }
686
687 SkUnencodedText binary(str);
688
689 SkDecodedText decoded;
690 skfont->encoding()->decodeText(binary, &decoded);
691
692 SkUnicodeText unicode;
693 skfont->ToUnicode(decoded, &unicode);
694
695 SkPaint paint;
696 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
697 // Or maybe just not call setTextSize at all?
698 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
699 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
700 }
701
702// if (fCurFont && fCurFont->GetFontScale() != 0) {
703// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
704// }
705
706 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
707
708 canvas->save();
709
710#if 1
711 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
712
713 SkPoint point1;
714 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
715
716 SkMatrix mirror;
717 mirror.setTranslate(0, -point1.y());
718 // TODO(edisonn): fix rotated text, and skewed too
719 mirror.postScale(SK_Scalar1, -SK_Scalar1);
720 // TODO(edisonn): post rotate, skew
721 mirror.postTranslate(0, point1.y());
722
723 matrix.postConcat(mirror);
724
725 canvas->setMatrix(matrix);
726
727 SkTraceMatrix(matrix, "mirrored");
728#endif
729
730 skfont->drawText(unicode, &paint, canvas, &pdfContext->fGraphicsState.fMatrixTm);
731 canvas->restore();
732
733/*
734 PdfString& rString = str->podofo()->GetString();
735
736 //pdfContext->fGraphicsState.fSkFont->GetDecoding()->ToUnicode(rString);
737 //void* text;
738 //int len;
739 //SkPaint paint;
740 //pdfContext->fGraphicsState.fSkFont->drawText(text, len, paint, canvas, &pdfContext->fGraphicsState.fMatrixTm);
741
742 PdfFont* fCurFont = pdfContext->fGraphicsState.fCurFont;
743
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000744 if (!fCurFont)
745 {
746 // TODO(edisonn): ignore the error, use the default font?
edisonn@google.comff278442013-06-21 21:03:15 +0000747 // return kError_PdfResult;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000748 }
749
edisonn@google.comff278442013-06-21 21:03:15 +0000750 const PdfEncoding* enc = fCurFont ? FixPdfFont(pdfContext, fCurFont) : NULL;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000751 bool cMapUnicodeFont = enc != NULL;
edisonn@google.comff278442013-06-21 21:03:15 +0000752 if (!enc) enc = fCurFont ? fCurFont->GetEncoding() : NULL;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000753 if (!enc)
754 {
755 // TODO(edisonn): Can we recover from this error?
edisonn@google.comff278442013-06-21 21:03:15 +0000756 //return kError_PdfResult;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000757 }
758
759 PdfString r2 = rString;
760 PdfString unicode;
761
762 if (cMapUnicodeFont) {
763 r2 = PdfString((pdf_utf16be*)rString.GetString(), rString.GetLength() / 2);
764 }
765
edisonn@google.comff278442013-06-21 21:03:15 +0000766 unicode = enc ? enc->ConvertToUnicode( r2, fCurFont ) : r2.ToUnicode();
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000767
768#ifdef PDF_TRACE
769 printf("%i %i ? %c rString.len = %i\n", (int)rString.GetString()[0], (int)rString.GetString()[1], (int)rString.GetString()[1], rString.GetLength());
770 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());
771#endif
772
773 SkPaint paint;
774 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
775 // Or maybe just not call setTextSize at all?
776 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
777 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
778 }
edisonn@google.comff278442013-06-21 21:03:15 +0000779 if (fCurFont && fCurFont->GetFontScale() != 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000780 paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
781 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000782
783 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000784
785 paint.setTypeface(SkTypefaceFromPdfFont(fCurFont));
786
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000787 canvas->save();
788 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
789
790#if 0
791 // Reverse now the space, otherwise the text is upside down.
792 SkScalar z = SkIntToScalar(0);
793 SkScalar one = SkIntToScalar(1);
794
795 SkPoint normalSpace1[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
796 SkPoint mirrorSpace1[4];
797 pdfContext->fGraphicsState.fMatrixTm.mapPoints(mirrorSpace1, normalSpace1, 4);
798
799 SkPoint normalSpace2[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, -one), SkPoint::Make(z, -one)};
800 SkPoint mirrorSpace2[4];
801 pdfContext->fGraphicsState.fMatrixTm.mapPoints(mirrorSpace2, normalSpace2, 4);
802
803#ifdef PDF_TRACE
804 printf("mirror1[0], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[0].x()), SkScalarToDouble(mirrorSpace1[0].y()));
805 printf("mirror1[1], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[1].x()), SkScalarToDouble(mirrorSpace1[1].y()));
806 printf("mirror1[2], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[2].x()), SkScalarToDouble(mirrorSpace1[2].y()));
807 printf("mirror1[3], x = %f y = %f\n", SkScalarToDouble(mirrorSpace1[3].x()), SkScalarToDouble(mirrorSpace1[3].y()));
808 printf("mirror2[0], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[0].x()), SkScalarToDouble(mirrorSpace2[0].y()));
809 printf("mirror2[1], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[1].x()), SkScalarToDouble(mirrorSpace2[1].y()));
810 printf("mirror2[2], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[2].x()), SkScalarToDouble(mirrorSpace2[2].y()));
811 printf("mirror2[3], x = %f y = %f\n", SkScalarToDouble(mirrorSpace2[3].x()), SkScalarToDouble(mirrorSpace2[3].y()));
812#endif
813
814 SkMatrix mirror;
815 SkASSERT(mirror.setPolyToPoly(mirrorSpace1, mirrorSpace2, 4));
816
817 // TODO(edisonn): text positioning wrong right now. Need to get matrix operations right.
818 matrix.preConcat(mirror);
819 canvas->setMatrix(matrix);
820#endif
821
822 SkPoint point1;
823 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
824
825 SkMatrix mirror;
826 mirror.setTranslate(0, -point1.y());
827 // TODO(edisonn): fix rotated text, and skewed too
828 mirror.postScale(SK_Scalar1, -SK_Scalar1);
829 // TODO(edisonn): post rotate, skew
830 mirror.postTranslate(0, point1.y());
831
832 matrix.postConcat(mirror);
833
834 canvas->setMatrix(matrix);
835
836 SkTraceMatrix(matrix, "mirrored");
837
838#ifdef PDF_TRACE
839 SkPoint point;
840 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkDoubleToScalar(0), SkDoubleToScalar(0), &point);
841 printf("Original SkCanvas resolved coordinates, x = %f y = %f\n", SkScalarToDouble(point.x()), SkScalarToDouble(point.y()));
842 matrix.mapXY(SkDoubleToScalar(0), SkDoubleToScalar(0), &point);
843 printf("Mirored SkCanvas resolved coordinates, x = %f y = %f\n", SkScalarToDouble(point.x()), SkScalarToDouble(point.y()));
844#endif
845
846 // TODO(edisonn): remove this call once we load the font properly
847 // The extra * will show that we got at least the text positioning right
848 // even if font failed to be loaded
849// canvas->drawText(".", 1, SkDoubleToScalar(-5.0), SkDoubleToScalar(0.0), paint);
850
851
852
853 // TODO(edisonn): use character and word spacing .. add utility function
854 if (cMapUnicodeFont) {
855 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
856 SkScalar textWidth = paint.measureText(unicode.GetString(), unicode.GetLength());
857 pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
858 canvas->drawText(unicode.GetString(), unicode.GetLength(), SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
859 }
860 else {
861 paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
862 SkScalar textWidth = paint.measureText(unicode.GetStringUtf8().c_str(), strlen(unicode.GetStringUtf8().c_str()));
863 pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
864 canvas->drawText(unicode.GetStringUtf8().c_str(), strlen(unicode.GetStringUtf8().c_str()), SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
865 }
866
867// paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
868// unsigned char ch = *(unicode.GetString() + 3);
869// if ((ch & 0xC0) != 0x80 && ch < 0x80) {
870// printf("x%i", ch);
871// SkScalar textWidth = paint.measureText(&ch, 1);
872// pdfContext->fGraphicsState.fMatrixTm.preTranslate(textWidth, SkDoubleToScalar(0.0));
873// canvas->drawText(&ch, 1, SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), paint);
874// }
875
876 canvas->restore();
877
edisonn@google.comff278442013-06-21 21:03:15 +0000878*/
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000879 return kPartial_PdfResult;
880}
881
882// TODO(edisonn): create header files with declarations!
883PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
884PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
885PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
886PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
887
888// TODO(edisonn): deal with synonyms (/BPC == /BitsPerComponent), here or in GetKey?
889// Always pass long form in key, and have a map of long -> short key
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000890bool LongFromDictionary(const PdfMemDocument* pdfDoc,
891 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000892 const char* key,
893 long* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000894 const PdfObject* value = resolveReferenceObject(pdfDoc,
895 dict.GetKey(PdfName(key)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000896
897 if (value == NULL || !value->IsNumber()) {
898 return false;
899 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000900 if (data == NULL) {
901 return true;
902 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000903
904 *data = value->GetNumber();
905 return true;
906}
907
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000908bool LongFromDictionary(const PdfMemDocument* pdfDoc,
909 const PdfDictionary& dict,
910 const char* key,
911 const char* abr,
912 long* data) {
913 if (LongFromDictionary(pdfDoc, dict, key, data)) return true;
914 if (abr == NULL || *abr == '\0') return false;
915 return LongFromDictionary(pdfDoc, dict, abr, data);
916}
917
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000918bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
919 const PdfDictionary& dict,
920 const char* key,
921 double* data) {
922 const PdfObject* value = resolveReferenceObject(pdfDoc,
923 dict.GetKey(PdfName(key)));
924
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000925 if (value == NULL || (!value->IsReal() && !value->IsNumber())) {
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000926 return false;
927 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000928 if (data == NULL) {
929 return true;
930 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000931
932 *data = value->GetReal();
933 return true;
934}
935
936bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
937 const PdfDictionary& dict,
938 const char* key,
939 const char* abr,
940 double* data) {
941 if (DoubleFromDictionary(pdfDoc, dict, key, data)) return true;
942 if (abr == NULL || *abr == '\0') return false;
943 return DoubleFromDictionary(pdfDoc, dict, abr, data);
944}
945
946
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000947bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
948 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000949 const char* key,
950 bool* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000951 const PdfObject* value = resolveReferenceObject(pdfDoc,
952 dict.GetKey(PdfName(key)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000953
954 if (value == NULL || !value->IsBool()) {
955 return false;
956 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000957 if (data == NULL) {
958 return true;
959 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000960
961 *data = value->GetBool();
962 return true;
963}
964
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000965bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
966 const PdfDictionary& dict,
967 const char* key,
968 const char* abr,
969 bool* data) {
970 if (BoolFromDictionary(pdfDoc, dict, key, data)) return true;
971 if (abr == NULL || *abr == '\0') return false;
972 return BoolFromDictionary(pdfDoc, dict, abr, data);
973}
974
975bool NameFromDictionary(const PdfMemDocument* pdfDoc,
976 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000977 const char* key,
978 std::string* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000979 const PdfObject* value = resolveReferenceObject(pdfDoc,
980 dict.GetKey(PdfName(key)),
981 true);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000982 if (value == NULL || !value->IsName()) {
983 return false;
984 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000985 if (data == NULL) {
986 return true;
987 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000988
989 *data = value->GetName().GetName();
990 return true;
991}
992
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000993bool NameFromDictionary(const PdfMemDocument* pdfDoc,
994 const PdfDictionary& dict,
995 const char* key,
996 const char* abr,
997 std::string* data) {
998 if (NameFromDictionary(pdfDoc, dict, key, data)) return true;
999 if (abr == NULL || *abr == '\0') return false;
1000 return NameFromDictionary(pdfDoc, dict, abr, data);
1001}
1002
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001003bool StringFromDictionary(const PdfMemDocument* pdfDoc,
1004 const PdfDictionary& dict,
1005 const char* key,
1006 std::string* data) {
1007 const PdfObject* value = resolveReferenceObject(pdfDoc,
1008 dict.GetKey(PdfName(key)),
1009 true);
1010 if (value == NULL || (!value->IsString() && !value->IsHexString())) {
1011 return false;
1012 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001013 if (data == NULL) {
1014 return true;
1015 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001016
1017 *data = value->GetString().GetString();
1018 return true;
1019}
1020
1021bool StringFromDictionary(const PdfMemDocument* pdfDoc,
1022 const PdfDictionary& dict,
1023 const char* key,
1024 const char* abr,
1025 std::string* data) {
1026 if (StringFromDictionary(pdfDoc, dict, key, data)) return true;
1027 if (abr == NULL || *abr == '\0') return false;
1028 return StringFromDictionary(pdfDoc, dict, abr, data);
1029}
1030
1031bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
1032 const PdfDictionary& dict,
1033 const char* key,
1034 SkPdfDictionary** data) {
1035 const PdfObject* value = resolveReferenceObject(pdfDoc,
1036 dict.GetKey(PdfName(key)),
1037 true);
1038 if (value == NULL || !value->IsDictionary()) {
1039 return false;
1040 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001041 if (data == NULL) {
1042 return true;
1043 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001044
edisonn@google.com68d15c82013-06-17 20:46:27 +00001045 return PodofoMapper::map(*pdfDoc, *value, (SkPdfObject**)data);
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001046}
1047
1048bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
1049 const PdfDictionary& dict,
1050 const char* key,
1051 const char* abr,
1052 SkPdfDictionary** data) {
1053 if (DictionaryFromDictionary(pdfDoc, dict, key, data)) return true;
1054 if (abr == NULL || *abr == '\0') return false;
1055 return DictionaryFromDictionary(pdfDoc, dict, abr, data);
1056}
1057
edisonn@google.com68d15c82013-06-17 20:46:27 +00001058template <typename T>
1059bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
1060 const PdfDictionary& dict,
1061 const char* key,
1062 SkPdfDictionary** data) {
1063 const PdfObject* value = resolveReferenceObject(pdfDoc,
1064 dict.GetKey(PdfName(key)),
1065 true);
1066 if (value == NULL || !value->IsDictionary()) {
1067 return false;
1068 }
1069
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001070 if (data == NULL) {
1071 return true;
1072 }
1073
edisonn@google.com68d15c82013-06-17 20:46:27 +00001074 return PodofoMapper::map(*pdfDoc, *value, (T**)data);
1075}
1076
1077template <typename T>
1078bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
1079 const PdfDictionary& dict,
1080 const char* key,
1081 const char* abr,
1082 T** data) {
1083 if (DictionaryFromDictionary2<T>(pdfDoc, dict, key, data)) return true;
1084 if (abr == NULL || *abr == '\0') return false;
1085 return DictionaryFromDictionary2<T>(pdfDoc, dict, abr, data);
1086}
1087
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001088bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
1089 const PdfDictionary& dict,
1090 const char* key,
1091 SkPdfObject** data) {
1092 const PdfObject* value = resolveReferenceObject(pdfDoc,
1093 dict.GetKey(PdfName(key)),
1094 true);
1095 if (value == NULL) {
1096 return false;
1097 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001098 if (data == NULL) {
1099 return true;
1100 }
edisonn@google.com68d15c82013-06-17 20:46:27 +00001101 return PodofoMapper::map(*pdfDoc, *value, data);
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001102}
1103
1104bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
1105 const PdfDictionary& dict,
1106 const char* key,
1107 const char* abr,
1108 SkPdfObject** data) {
1109 if (ObjectFromDictionary(pdfDoc, dict, key, data)) return true;
1110 if (abr == NULL || *abr == '\0') return false;
1111 return ObjectFromDictionary(pdfDoc, dict, abr, data);
1112}
1113
edisonn@google.comff278442013-06-21 21:03:15 +00001114bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
1115 const PdfDictionary& dict,
1116 const char* key,
1117 SkPdfStream** data) {
1118 const PdfObject* value = resolveReferenceObject(pdfDoc,
1119 dict.GetKey(PdfName(key)),
1120 true);
1121 if (value == NULL) {
1122 return false;
1123 }
1124 if (data == NULL) {
1125 return true;
1126 }
1127 return PodofoMapper::map(*pdfDoc, *value, data);
1128}
1129
1130bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
1131 const PdfDictionary& dict,
1132 const char* key,
1133 const char* abr,
1134 SkPdfStream** data) {
1135 if (StreamFromDictionary(pdfDoc, dict, key, data)) return true;
1136 if (abr == NULL || *abr == '\0') return false;
1137 return StreamFromDictionary(pdfDoc, dict, abr, data);
1138}
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001139
edisonn@google.com68d15c82013-06-17 20:46:27 +00001140
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001141// TODO(edisonn): perf!!!
1142
1143static SkColorTable* getGrayColortable() {
1144 static SkColorTable* grayColortable = NULL;
1145 if (grayColortable == NULL) {
1146 SkPMColor* colors = new SkPMColor[256];
1147 for (int i = 0 ; i < 256; i++) {
1148 colors[i] = SkPreMultiplyARGB(255, i, i, i);
1149 }
1150 grayColortable = new SkColorTable(colors, 256);
1151 }
1152 return grayColortable;
1153}
1154
1155SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
1156 int width, int height, int bytesPerLine,
1157 int bpc, const std::string& colorSpace,
1158 bool transparencyMask) {
1159 SkBitmap bitmap;
1160
1161 int components = GetColorSpaceComponents(colorSpace);
1162//#define MAX_COMPONENTS 10
1163
1164 int bitsPerLine = width * components * bpc;
1165 // TODO(edisonn): assume start of lines are aligned at 32 bits?
1166 // Is there a faster way to load the uncompressed stream into a bitmap?
1167
1168 // minimal support for now
1169 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
1170 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
1171
1172 for (int h = 0 ; h < height; h++) {
1173 long i = width * (height - 1 - h);
1174 for (int w = 0 ; w < width; w++) {
1175 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
1176 uncompressedStream[3 * w + 1],
1177 uncompressedStream[3 * w + 2]);
1178 i++;
1179 }
1180 uncompressedStream += bytesPerLine;
1181 }
1182
1183 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
1184 bitmap.setPixels(uncompressedStreamArgb);
1185 }
1186 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
1187 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
1188
1189 for (int h = 0 ; h < height; h++) {
1190 long i = width * (height - 1 - h);
1191 for (int w = 0 ; w < width; w++) {
1192 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
1193 uncompressedStream[w];
1194 i++;
1195 }
1196 uncompressedStream += bytesPerLine;
1197 }
1198
1199 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
1200 width, height);
1201 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
1202 }
1203
1204 // TODO(edisonn): Report Warning, NYI, or error
1205 return bitmap;
1206}
1207
1208bool transferImageStreamToARGB(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
1209 int width, int bytesPerLine,
1210 int bpc, const std::string& colorSpace,
1211 SkColor** uncompressedStreamArgb,
1212 pdf_long* uncompressedStreamLengthInBytesArgb) {
1213 int components = GetColorSpaceComponents(colorSpace);
1214//#define MAX_COMPONENTS 10
1215
1216 int bitsPerLine = width * components * bpc;
1217 // TODO(edisonn): assume start of lines are aligned at 32 bits?
1218 int height = uncompressedStreamLength / bytesPerLine;
1219
1220 // minimal support for now
1221 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
1222 *uncompressedStreamLengthInBytesArgb = width * height * 4;
1223 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
1224
1225 for (int h = 0 ; h < height; h++) {
1226 long i = width * (height - 1 - h);
1227 for (int w = 0 ; w < width; w++) {
1228 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[3 * w],
1229 uncompressedStream[3 * w + 1],
1230 uncompressedStream[3 * w + 2]);
1231 i++;
1232 }
1233 uncompressedStream += bytesPerLine;
1234 }
1235 return true;
1236 }
1237
1238 if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
1239 *uncompressedStreamLengthInBytesArgb = width * height * 4;
1240 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
1241
1242 for (int h = 0 ; h < height; h++) {
1243 long i = width * (height - 1 - h);
1244 for (int w = 0 ; w < width; w++) {
1245 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[w],
1246 uncompressedStream[w],
1247 uncompressedStream[w]);
1248 i++;
1249 }
1250 uncompressedStream += bytesPerLine;
1251 }
1252 return true;
1253 }
1254
1255 return false;
1256}
1257
1258// utils
1259
1260// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
1261// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
1262// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
1263// skia format, through a table
1264
1265// this functions returns the image, it does not look at the smask.
1266
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001267SkBitmap getImageFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001268 if (image == NULL || !image->valid()) {
1269 // TODO(edisonn): report warning to be used in testing.
1270 return SkBitmap();
1271 }
1272
1273 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
1274// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
1275// obj.GetDictionary().GetKey(PdfName("Filter")));
1276// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
1277// value = resolveReferenceObject(pdfContext->fPdfDoc,
1278// &value->GetArray()[0]);
1279// }
1280// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
1281// SkStream stream = SkStream::
1282// SkImageDecoder::Factory()
1283// }
1284
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001285 long bpc = image->BitsPerComponent();
1286 long width = image->Width();
1287 long height = image->Height();
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001288 std::string colorSpace = "DeviceRGB";
edisonn@google.com1277cf02013-06-17 23:36:45 +00001289
1290 // TODO(edisonn): color space can be an array too!
1291 if (image->isColorSpaceAName()) {
1292 colorSpace = image->getColorSpaceAsName();
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001293 }
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001294
1295/*
1296 bool imageMask = image->imageMask();
1297
1298 if (imageMask) {
1299 if (bpc != 0 && bpc != 1) {
1300 // TODO(edisonn): report warning to be used in testing.
1301 return SkBitmap();
1302 }
1303 bpc = 1;
1304 }
1305*/
1306
1307 const PdfObject* obj = image->podofo();
1308
1309 char* uncompressedStream = NULL;
1310 pdf_long uncompressedStreamLength = 0;
1311
1312 PdfResult ret = kPartial_PdfResult;
1313 // TODO(edisonn): get rid of try/catch exceptions! We should not throw on user data!
1314 try {
1315 obj->GetStream()->GetFilteredCopy(&uncompressedStream, &uncompressedStreamLength);
1316 } catch (PdfError& e) {
1317 // TODO(edisonn): report warning to be used in testing.
1318 return SkBitmap();
1319 }
1320
1321 int bytesPerLine = uncompressedStreamLength / height;
1322#ifdef PDF_TRACE
1323 if (uncompressedStreamLength % height != 0) {
1324 printf("Warning uncompressedStreamLength % height != 0 !!!\n");
1325 }
1326#endif
1327
1328 SkBitmap bitmap = transferImageStreamToBitmap(
1329 (unsigned char*)uncompressedStream, uncompressedStreamLength,
1330 width, height, bytesPerLine,
1331 bpc, colorSpace,
1332 transparencyMask);
1333
1334 free(uncompressedStream);
1335
1336 return bitmap;
1337}
1338
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001339SkBitmap getSmaskFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* obj) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001340 const PdfObject* sMask = resolveReferenceObject(&pdfContext->fPdfDoc.podofo(),
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001341 obj->podofo()->GetDictionary().GetKey(PdfName("SMask")));
1342
1343#ifdef PDF_TRACE
1344 std::string str;
1345 if (sMask) {
1346 sMask->ToString(str);
1347 printf("/SMask of /Subtype /Image: %s\n", str.c_str());
1348 }
1349#endif
1350
1351 if (sMask) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001352 SkPdfImageDictionary skxobjmask(&pdfContext->fPdfDoc.podofo(), sMask);
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001353 return getImageFromObject(pdfContext, &skxobjmask, true);
1354 }
1355
1356 // TODO(edisonn): implement GS SMask. Default to empty right now.
1357 return pdfContext->fGraphicsState.fSMask;
1358}
1359
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001360PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfImageDictionary* skpdfimage) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001361 if (skpdfimage == NULL || !skpdfimage->valid()) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001362 return kIgnoreError_PdfResult;
1363 }
1364
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001365 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
1366 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001367
1368 canvas->save();
1369 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1370 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
1371
1372 if (sMask.empty()) {
1373 canvas->drawBitmapRect(image, dst, NULL);
1374 } else {
1375 canvas->saveLayer(&dst, NULL);
1376 canvas->drawBitmapRect(image, dst, NULL);
1377 SkPaint xfer;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001378 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001379 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
1380 canvas->drawBitmapRect(sMask, dst, &xfer);
1381 canvas->restore();
1382 }
1383
1384 canvas->restore();
1385
1386 return kPartial_PdfResult;
1387}
1388
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001389bool SkMatrixFromDictionary(PdfContext* pdfContext,
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001390 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001391 const char* key,
1392 SkMatrix* matrix) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001393 const PdfObject* value = resolveReferenceObject(&pdfContext->fPdfDoc.podofo(),
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001394 dict.GetKey(PdfName(key)));
1395
1396 if (value == NULL || !value->IsArray()) {
1397 return false;
1398 }
1399
1400 if (value->GetArray().GetSize() != 6) {
1401 return false;
1402 }
1403
1404 double array[6];
1405 for (int i = 0; i < 6; i++) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001406 const PdfObject* elem = resolveReferenceObject(&pdfContext->fPdfDoc.podofo(), &value->GetArray()[i]);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001407 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1408 return false;
1409 }
1410 array[i] = elem->GetReal();
1411 }
1412
1413 *matrix = SkMatrixFromPdfMatrix(array);
1414 return true;
1415}
1416
edisonn@google.com59543d32013-06-18 22:00:40 +00001417bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001418 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001419 const char* key,
1420 SkRect* rect) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001421 const PdfObject* value = resolveReferenceObject(pdfDoc,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001422 dict.GetKey(PdfName(key)));
1423
1424 if (value == NULL || !value->IsArray()) {
1425 return false;
1426 }
1427
1428 if (value->GetArray().GetSize() != 4) {
1429 return false;
1430 }
1431
1432 double array[4];
1433 for (int i = 0; i < 4; i++) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001434 const PdfObject* elem = resolveReferenceObject(pdfDoc, &value->GetArray()[i]);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001435 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1436 return false;
1437 }
1438 array[i] = elem->GetReal();
1439 }
1440
1441 *rect = SkRect::MakeLTRB(SkDoubleToScalar(array[0]),
1442 SkDoubleToScalar(array[1]),
1443 SkDoubleToScalar(array[2]),
1444 SkDoubleToScalar(array[3]));
1445 return true;
1446}
1447
edisonn@google.com59543d32013-06-18 22:00:40 +00001448bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
1449 const PdfDictionary& dict,
1450 const char* key,
1451 const char* abr,
1452 SkRect* data) {
1453 if (SkRectFromDictionary(pdfDoc, dict, key, data)) return true;
1454 if (abr == NULL || *abr == '\0') return false;
1455 return SkRectFromDictionary(pdfDoc, dict, abr, data);
1456
1457}
1458
1459
edisonn@google.com68d15c82013-06-17 20:46:27 +00001460SkPdfObject* get(const SkPdfObject* obj, const char* key, const char* abr = "") {
1461 SkPdfObject* ret = NULL;
1462 if (obj == NULL) return NULL;
1463 const SkPdfDictionary* dict = obj->asDictionary();
1464 if (dict == NULL) return NULL;
1465 if (!dict->podofo()->IsDictionary()) return NULL;
1466 ObjectFromDictionary(dict->doc(), dict->podofo()->GetDictionary(), key, abr, &ret);
1467 return ret;
1468}
1469
1470PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
1471 if (!skobj || !skobj->podofo() || !skobj->podofo()->HasStream() || skobj->podofo()->GetStream() == NULL || skobj->podofo()->GetStream()->GetLength() == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001472 return kOK_PdfResult;
1473 }
1474
1475 PdfOp_q(pdfContext, canvas, NULL);
1476 canvas->save();
1477
edisonn@google.com68d15c82013-06-17 20:46:27 +00001478 if (get(skobj, "Resources")) {
1479 SkPdfResourceDictionary* res = NULL;
1480
1481 PodofoMapper::map(*get(skobj, "Resources"), &res);
1482
1483 if (res) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001484 pdfContext->fGraphicsState.fResources = res;
edisonn@google.com68d15c82013-06-17 20:46:27 +00001485 }
1486 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001487
1488 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
1489
1490 SkMatrix matrix;
edisonn@google.com68d15c82013-06-17 20:46:27 +00001491 if (SkMatrixFromDictionary(pdfContext, skobj->podofo()->GetDictionary(), "Matrix", &matrix)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001492 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1493 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1494 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1495 // TODO(edisonn) reset matrixTm and matricTlm also?
1496 }
1497
1498 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1499
1500 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1501
1502 SkRect bbox;
edisonn@google.com59543d32013-06-18 22:00:40 +00001503 if (SkRectFromDictionary(&pdfContext->fPdfDoc.podofo(), skobj->podofo()->GetDictionary(), "BBox", &bbox)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001504 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
1505 }
1506
1507 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
1508 // For this PdfContentsTokenizer needs to be extended.
1509
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001510 PdfResult ret = kPartial_PdfResult;
edisonn@google.comff278442013-06-21 21:03:15 +00001511 SkPdfTokenizer tokenizer(skobj);
1512 PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1513 looper.loop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001514
1515 // TODO(edisonn): should we restore the variable stack at the same state?
1516 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1517 canvas->restore();
1518 PdfOp_Q(pdfContext, canvas, NULL);
1519 return ret;
1520}
1521
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001522PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject& obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001523 return kNYI_PdfResult;
1524}
1525
edisonn@google.com59543d32013-06-18 22:00:40 +00001526// TODO(edisonn): faster, have the property on the SkPdfObject itself?
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001527std::set<const PdfObject*> gInRendering;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001528
1529class CheckRecursiveRendering {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001530 const PdfObject& fObj;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001531public:
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001532 CheckRecursiveRendering(const PdfObject& obj) : fObj(obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001533 gInRendering.insert(&obj);
1534 }
1535
1536 ~CheckRecursiveRendering() {
1537 //SkASSERT(fObj.fInRendering);
1538 gInRendering.erase(&fObj);
1539 }
1540
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001541 static bool IsInRendering(const PdfObject& obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001542 return gInRendering.find(&obj) != gInRendering.end();
1543 }
1544};
1545
edisonn@google.com59543d32013-06-18 22:00:40 +00001546PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject& obj) {
1547 if (CheckRecursiveRendering::IsInRendering(*obj.podofo())) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001548 // Oops, corrupt PDF!
1549 return kIgnoreError_PdfResult;
1550 }
1551
edisonn@google.com59543d32013-06-18 22:00:40 +00001552 CheckRecursiveRendering checkRecursion(*obj.podofo());
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001553
1554 // TODO(edisonn): check type
edisonn@google.com68d15c82013-06-17 20:46:27 +00001555 SkPdfXObjectDictionary* skobj = NULL;
edisonn@google.com59543d32013-06-18 22:00:40 +00001556 if (!PodofoMapper::map(obj, &skobj)) return kIgnoreError_PdfResult;
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001557
1558 if (!skobj || !skobj->valid()) return kIgnoreError_PdfResult;
1559
1560 PdfResult ret = kIgnoreError_PdfResult;
1561 switch (skobj->getType())
1562 {
edisonn@google.com59543d32013-06-18 22:00:40 +00001563 case kImageDictionary_SkPdfObjectType:
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001564 ret = doXObject_Image(pdfContext, canvas, skobj->asImageDictionary());
edisonn@google.come4d11be2013-06-12 19:53:42 +00001565 break;
edisonn@google.com59543d32013-06-18 22:00:40 +00001566 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com68d15c82013-06-17 20:46:27 +00001567 ret = doXObject_Form(pdfContext, canvas, skobj->asType1FormDictionary());
edisonn@google.come4d11be2013-06-12 19:53:42 +00001568 break;
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001569 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
1570 //return doXObject_PS(skxobj.asPS());
1571 }
1572
1573 delete skobj;
1574 return ret;
1575}
1576
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001577PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1578 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1579 canvas->save();
1580 return kOK_PdfResult;
1581}
1582
1583PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1584 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1585 pdfContext->fStateStack.pop();
1586 canvas->restore();
1587 return kOK_PdfResult;
1588}
1589
1590PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1591 double array[6];
1592 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001593 array[5 - i] = pdfContext->fObjectStack.top()->asNumber()->value();
1594 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001595 }
1596
1597 // a b
1598 // c d
1599 // e f
1600
1601 // 0 1
1602 // 2 3
1603 // 4 5
1604
1605 // sx ky
1606 // kx sy
1607 // tx ty
1608 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1609
1610 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1611
1612#ifdef PDF_TRACE
1613 printf("cm ");
1614 for (int i = 0 ; i < 6 ; i++) {
1615 printf("%f ", array[i]);
1616 }
1617 printf("\n");
1618 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix);
1619#endif
1620
1621 return kOK_PdfResult;
1622}
1623
1624//leading TL Set the text leading, Tl
1625//, to leading, which is a number expressed in unscaled text
1626//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
1627PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001628 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001629
1630 pdfContext->fGraphicsState.fTextLeading = ty;
1631
1632 return kOK_PdfResult;
1633}
1634
1635PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001636 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1637 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001638
1639 double array[6] = {1, 0, 0, 1, tx, ty};
1640 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1641
1642 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1643 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1644
1645 return kPartial_PdfResult;
1646}
1647
1648PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001649 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1650 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001651
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001652 // TODO(edisonn): Create factory methods or constructors so podofo is hidden
edisonn@google.com59543d32013-06-18 22:00:40 +00001653 PdfObject _ty(PdfVariant(-ty));
1654 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &_ty));
1655
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001656 PdfOp_TL(pdfContext, canvas, looper);
1657
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001658 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 +00001659 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &vtx));
1660
1661 PdfObject vty(PdfVariant(-(-ty)));
1662 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &vty));
1663
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001664 return PdfOp_Td(pdfContext, canvas, looper);
1665}
1666
1667PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001668 double f = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1669 double e = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1670 double d = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1671 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1672 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1673 double a = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001674
1675 double array[6];
1676 array[0] = a;
1677 array[1] = b;
1678 array[2] = c;
1679 array[3] = d;
1680 array[4] = e;
1681 array[5] = f;
1682
1683 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1684 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
1685
1686 // TODO(edisonn): Text positioning.
1687 pdfContext->fGraphicsState.fMatrixTm = matrix;
1688 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1689
1690 return kPartial_PdfResult;
1691}
1692
1693//— T* Move to the start of the next line. This operator has the same effect as the code
1694//0 Tl Td
1695//where Tl is the current leading parameter in the text state
1696PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001697 PdfObject zero(PdfVariant(0.0));
1698 PdfObject tl(PdfVariant(-(-pdfContext->fGraphicsState.fTextLeading)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001699
edisonn@google.com59543d32013-06-18 22:00:40 +00001700 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &zero));
1701 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc.podofo(), &tl));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001702 return PdfOp_Td(pdfContext, canvas, looper);
1703}
1704
1705PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1706 if (pdfContext->fGraphicsState.fPathClosed) {
1707 pdfContext->fGraphicsState.fPath.reset();
1708 pdfContext->fGraphicsState.fPathClosed = false;
1709 }
1710
edisonn@google.com59543d32013-06-18 22:00:40 +00001711 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1712 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001713
1714 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1715 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1716
1717 return kOK_PdfResult;
1718}
1719
1720PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1721 if (pdfContext->fGraphicsState.fPathClosed) {
1722 pdfContext->fGraphicsState.fPath.reset();
1723 pdfContext->fGraphicsState.fPathClosed = false;
1724 }
1725
edisonn@google.com59543d32013-06-18 22:00:40 +00001726 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1727 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001728
1729 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1730 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1731
1732 return kOK_PdfResult;
1733}
1734
1735PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1736 if (pdfContext->fGraphicsState.fPathClosed) {
1737 pdfContext->fGraphicsState.fPath.reset();
1738 pdfContext->fGraphicsState.fPathClosed = false;
1739 }
1740
edisonn@google.com59543d32013-06-18 22:00:40 +00001741 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1742 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1743 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1744 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1745 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1746 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001747
1748 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1749 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1750 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1751
1752 pdfContext->fGraphicsState.fCurPosX = x3;
1753 pdfContext->fGraphicsState.fCurPosY = y3;
1754
1755 return kOK_PdfResult;
1756}
1757
1758PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1759 if (pdfContext->fGraphicsState.fPathClosed) {
1760 pdfContext->fGraphicsState.fPath.reset();
1761 pdfContext->fGraphicsState.fPathClosed = false;
1762 }
1763
edisonn@google.com59543d32013-06-18 22:00:40 +00001764 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1765 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1766 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1767 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001768 double y1 = pdfContext->fGraphicsState.fCurPosY;
1769 double x1 = pdfContext->fGraphicsState.fCurPosX;
1770
1771 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1772 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1773 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1774
1775 pdfContext->fGraphicsState.fCurPosX = x3;
1776 pdfContext->fGraphicsState.fCurPosY = y3;
1777
1778 return kOK_PdfResult;
1779}
1780
1781PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1782 if (pdfContext->fGraphicsState.fPathClosed) {
1783 pdfContext->fGraphicsState.fPath.reset();
1784 pdfContext->fGraphicsState.fPathClosed = false;
1785 }
1786
edisonn@google.com59543d32013-06-18 22:00:40 +00001787 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1788 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001789 double y2 = pdfContext->fGraphicsState.fCurPosY;
1790 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com59543d32013-06-18 22:00:40 +00001791 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1792 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001793
1794 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1795 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1796 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1797
1798 pdfContext->fGraphicsState.fCurPosX = x3;
1799 pdfContext->fGraphicsState.fCurPosY = y3;
1800
1801 return kOK_PdfResult;
1802}
1803
1804PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1805 if (pdfContext->fGraphicsState.fPathClosed) {
1806 pdfContext->fGraphicsState.fPath.reset();
1807 pdfContext->fGraphicsState.fPathClosed = false;
1808 }
1809
edisonn@google.com59543d32013-06-18 22:00:40 +00001810 double height = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1811 double width = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1812 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1813 double x = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001814
1815 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1816 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1817
1818 pdfContext->fGraphicsState.fCurPosX = x;
1819 pdfContext->fGraphicsState.fCurPosY = y + height;
1820
1821 return kOK_PdfResult;
1822}
1823
1824PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1825 pdfContext->fGraphicsState.fPath.close();
1826 pdfContext->fGraphicsState.fPathClosed = true;
1827 return kOK_PdfResult;
1828}
1829
1830PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
1831 SkPath path = pdfContext->fGraphicsState.fPath;
1832
1833 if (close) {
1834 path.close();
1835 }
1836
1837 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1838
1839 SkPaint paint;
1840
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001841 SkPoint line[2];
1842 if (fill && !stroke && path.isLine(line)) {
1843 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001844
1845 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001846 paint.setStrokeWidth(SkDoubleToScalar(0));
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001847
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001848 canvas->drawPath(path, paint);
1849 } else {
1850 if (fill) {
1851 paint.setStyle(SkPaint::kFill_Style);
1852 if (evenOdd) {
1853 path.setFillType(SkPath::kEvenOdd_FillType);
1854 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001855
1856 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1857
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001858 canvas->drawPath(path, paint);
1859 }
1860
1861 if (stroke) {
1862 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001863
1864 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1865
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001866 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1867 canvas->drawPath(path, paint);
1868 }
1869 }
1870
1871 pdfContext->fGraphicsState.fPath.reset();
1872 // todo zoom ... other stuff ?
1873
1874 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1875#ifndef PDF_DEBUG_NO_CLIPING
1876 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1877#endif
1878 }
1879
1880 //pdfContext->fGraphicsState.fClipPath.reset();
1881 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1882
1883 return kPartial_PdfResult;
1884
1885}
1886
1887PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1888 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1889}
1890
1891PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1892 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1893}
1894
1895PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1896 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1897}
1898
1899PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1900 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1901}
1902
1903PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1904 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1905}
1906
1907PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1908 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1909}
1910
1911PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1912 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1913}
1914
1915PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1916 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1917}
1918
1919PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1920 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1921}
1922
1923PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1924 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1925 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1926#ifndef PDF_DEBUG_NO_CLIPING
1927 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1928#endif
1929 }
1930
1931 //pdfContext->fGraphicsState.fClipPath.reset();
1932 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1933
1934 pdfContext->fGraphicsState.fPathClosed = true;
1935
1936 return kOK_PdfResult;
1937}
1938
1939PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1940 pdfContext->fGraphicsState.fTextBlock = true;
1941 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1942 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1943
1944 return kPartial_PdfResult;
1945}
1946
1947PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1948 if (!pdfContext->fGraphicsState.fTextBlock) {
1949 return kIgnoreError_PdfResult;
1950 }
1951 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1952 return kPartial_PdfResult;
1953}
1954
1955//font size Tf Set the text font, Tf
1956//, to font and the text font size, Tfs, to size. font is the name of a
1957//font resource in the Fontsubdictionary of the current resource dictionary; size is
1958//a number representing a scale factor. There is no initial value for either font or
1959//size; they must be specified explicitly using Tf before any text is shown.
1960PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001961 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1962 std::string fontName = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1963
1964#ifdef PDF_TRACE
1965 printf("font name: %s\n", fontName.c_str());
1966 std::string str;
1967 pdfContext->fGraphicsState.fResources->podofo()->ToString(str);
edisonn@google.comff278442013-06-21 21:03:15 +00001968 printf("Print Tf Resources: %s\n", str.c_str());
1969 pdfContext->fGraphicsState.fResources->Font()->podofo()->ToString(str);
1970 printf("Print Tf Resources/Font: %s\n", str.c_str());
edisonn@google.com59543d32013-06-18 22:00:40 +00001971#endif
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001972
edisonn@google.comff278442013-06-21 21:03:15 +00001973 SkPdfFontDictionary* fd = NULL;
1974 if (pdfContext->fGraphicsState.fResources->Font()) {
1975 SkPdfObject objFont = pdfContext->fGraphicsState.fResources->Font()->get(fontName.c_str());
1976 PodofoMapper::map(objFont, &fd);
1977
1978#ifdef PDF_TRACE
1979 objFont.podofo()->ToString(str);
1980 printf("Print Font loaded: %s\n", str.c_str());
1981 fd->podofo()->ToString(str);
1982 printf("Print Font loaded and resolved and upgraded: %s\n", str.c_str());
1983#endif
1984
1985 }
1986
1987 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(fd);
1988
1989 if (skfont) {
1990 pdfContext->fGraphicsState.fSkFont = skfont;
1991 }
1992
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001993 // TODO(edisonn): Load font from pdfContext->fGraphicsState.fObjectWithResources ?
edisonn@google.com59543d32013-06-18 22:00:40 +00001994 const PdfObject* pFont = resolveReferenceObject(&pdfContext->fPdfDoc.podofo(),
1995 pdfContext->fGraphicsState.fResources->Font()->get(fontName.c_str()).podofo());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001996 if( !pFont )
1997 {
1998 // TODO(edisonn): try to ignore the error, make sure we do not crash.
1999 return kIgnoreError_PdfResult;
2000 }
2001
edisonn@google.comff278442013-06-21 21:03:15 +00002002 PdfFont* font = pdfContext->fPdfDoc.podofo().GetFont( (PdfObject*)pFont );
2003 if (font) {
2004 pdfContext->fGraphicsState.fCurFont = font;
2005 } else {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002006 // TODO(edisonn): check ~/crasing, for one of the files PoDoFo throws exception
2007 // when calling pFont->Reference(), with Linked list corruption.
2008 return kIgnoreError_PdfResult;
2009 }
2010
2011 return kPartial_PdfResult;
2012}
2013
2014PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2015 if (!pdfContext->fGraphicsState.fTextBlock) {
2016 // TODO(edisonn): try to recover and draw it any way?
2017 return kIgnoreError_PdfResult;
2018 }
2019
2020 PdfResult ret = DrawText(pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +00002021 pdfContext->fObjectStack.top(),
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002022 canvas);
edisonn@google.com59543d32013-06-18 22:00:40 +00002023 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002024
2025 return ret;
2026}
2027
2028PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2029 if (!pdfContext->fGraphicsState.fTextBlock) {
2030 // TODO(edisonn): try to recover and draw it any way?
2031 return kIgnoreError_PdfResult;
2032 }
2033
2034 PdfOp_T_star(pdfContext, canvas, looper);
2035 // Do not pop, and push, just transfer the param to Tj
2036 return PdfOp_Tj(pdfContext, canvas, looper);
2037}
2038
2039PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2040 if (!pdfContext->fGraphicsState.fTextBlock) {
2041 // TODO(edisonn): try to recover and draw it any way?
2042 return kIgnoreError_PdfResult;
2043 }
2044
edisonn@google.com59543d32013-06-18 22:00:40 +00002045 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
2046 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
2047 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002048
edisonn@google.com59543d32013-06-18 22:00:40 +00002049 pdfContext->fObjectStack.push(aw);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002050 PdfOp_Tw(pdfContext, canvas, looper);
2051
edisonn@google.com59543d32013-06-18 22:00:40 +00002052 pdfContext->fObjectStack.push(ac);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002053 PdfOp_Tc(pdfContext, canvas, looper);
2054
edisonn@google.com59543d32013-06-18 22:00:40 +00002055 pdfContext->fObjectStack.push(str);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002056 PdfOp_quote(pdfContext, canvas, looper);
2057
2058 return kPartial_PdfResult;
2059}
2060
2061PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2062 if (!pdfContext->fGraphicsState.fTextBlock) {
2063 // TODO(edisonn): try to recover and draw it any way?
2064 return kIgnoreError_PdfResult;
2065 }
2066
edisonn@google.com59543d32013-06-18 22:00:40 +00002067 SkPdfArray* array = pdfContext->fObjectStack.top()->asArray();
2068 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002069
edisonn@google.com59543d32013-06-18 22:00:40 +00002070 for( int i=0; i<static_cast<int>(array->size()); i++ )
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002071 {
edisonn@google.comff278442013-06-21 21:03:15 +00002072 if( (*array)[i]->asString()) {
2073 SkPdfObject* obj = (*array)[i];
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002074 DrawText(pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +00002075 obj,
2076 canvas);
2077 } else if ((*array)[i]->asInteger() || (*array)[i]->asNumber()) {
2078 double dx = (*array)[i]->asNumber()->value();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002079 SkMatrix matrix;
2080 matrix.setAll(SkDoubleToScalar(1),
2081 SkDoubleToScalar(0),
2082 // TODO(edisonn): use writing mode, vertical/horizontal.
2083 SkDoubleToScalar(-dx), // amount is substracted!!!
2084 SkDoubleToScalar(0),
2085 SkDoubleToScalar(1),
2086 SkDoubleToScalar(0),
2087 SkDoubleToScalar(0),
2088 SkDoubleToScalar(0),
2089 SkDoubleToScalar(1));
2090
2091 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
2092 }
2093 }
2094 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
2095}
2096
2097PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002098 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002099 return kOK_PdfResult;
2100}
2101
2102PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2103 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2104}
2105
2106PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2107 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2108}
2109
2110PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
2111 double c[4];
2112 pdf_int64 v[4];
2113
2114 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
2115
2116 bool doubles = true;
2117 if (colorOperator->fColorSpace == "Indexed") {
2118 doubles = false;
2119 }
2120
2121#ifdef PDF_TRACE
2122 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.c_str(), n);
2123#endif
2124
2125 for (int i = n - 1; i >= 0 ; i--) {
2126 if (doubles) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002127 c[i] = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002128 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002129 v[i] = pdfContext->fObjectStack.top()->asInteger()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002130 }
2131 }
2132
2133 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
2134 if (colorOperator->fColorSpace == "DeviceRGB") {
2135 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
2136 }
2137 return kPartial_PdfResult;
2138}
2139
2140PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2141 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2142}
2143
2144PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2145 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2146}
2147
2148PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
2149 PdfString name;
2150
edisonn@google.com59543d32013-06-18 22:00:40 +00002151 if (pdfContext->fObjectStack.top()->asName()) {
2152 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002153 }
2154
2155 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
2156 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
2157
2158 return kPartial_PdfResult;
2159}
2160
2161PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2162 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2163}
2164
2165PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2166 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2167}
2168
2169PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002170 double gray = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002171 return kNYI_PdfResult;
2172}
2173
2174PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2175 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2176}
2177
2178PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2179 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2180}
2181
2182PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002183 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2184 double g = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2185 double r = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002186
2187 colorOperator->fColorSpace = "DeviceRGB";
2188 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
2189 return kOK_PdfResult;
2190}
2191
2192PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2193 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2194}
2195
2196PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2197 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2198}
2199
2200PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
2201 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com59543d32013-06-18 22:00:40 +00002202 double k = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2203 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2204 double m = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2205 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002206
2207 colorOperator->fColorSpace = "DeviceCMYK";
2208 // TODO(edisonn): Set color.
2209 return kNYI_PdfResult;
2210}
2211
2212PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2213 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2214}
2215
2216PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2217 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2218}
2219
2220PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2221 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
2222 pdfContext->fGraphicsState.fHasClipPathToApply = true;
2223
2224 return kOK_PdfResult;
2225}
2226
2227PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2228 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
2229
2230#ifdef PDF_TRACE
2231 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
2232 printf("CLIP IS RECT\n");
2233 }
2234#endif
2235
2236 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
2237 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
2238 pdfContext->fGraphicsState.fHasClipPathToApply = true;
2239
2240 return kPartial_PdfResult;
2241}
2242
2243PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2244 *looper = new PdfCompatibilitySectionLooper();
2245 return kOK_PdfResult;
2246}
2247
2248PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2249#ifdef ASSERT_BAD_PDF_OPS
2250 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
2251 // have the assert when testing good pdfs.
2252#endif
2253 return kIgnoreError_PdfResult;
2254}
2255
2256PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2257 *looper = new PdfInlineImageLooper();
2258 return kOK_PdfResult;
2259}
2260
2261PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2262#ifdef ASSERT_BAD_PDF_OPS
2263 SkASSERT(false); // must be processed in inline image looper, but let's
2264 // have the assert when testing good pdfs.
2265#endif
2266 return kIgnoreError_PdfResult;
2267}
2268
2269PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2270#ifdef ASSERT_BAD_PDF_OPS
2271 SkASSERT(false); // must be processed in inline image looper, but let's
2272 // have the assert when testing good pdfs.
2273#endif
2274 return kIgnoreError_PdfResult;
2275}
2276
2277//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
2278PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002279 double lineWidth = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002280 pdfContext->fGraphicsState.fLineWidth = lineWidth;
2281
2282 return kOK_PdfResult;
2283}
2284
2285//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
2286PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002287 pdfContext->fObjectStack.pop();
2288 //double lineCap = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002289
2290 return kNYI_PdfResult;
2291}
2292
2293//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
2294PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002295 pdfContext->fObjectStack.pop();
2296 //double lineJoin = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002297
2298 return kNYI_PdfResult;
2299}
2300
2301//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
2302PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002303 pdfContext->fObjectStack.pop();
2304 //double miterLimit = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002305
2306 return kNYI_PdfResult;
2307}
2308
2309//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
2310//page 155).
2311PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002312 pdfContext->fObjectStack.pop();
2313 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002314
2315 return kNYI_PdfResult;
2316}
2317
2318//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
2319PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002320 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002321
2322 return kNYI_PdfResult;
2323}
2324
2325//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
2326//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
2327//fies the output device’s default flatness tolerance.
2328PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002329 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002330
2331 return kNYI_PdfResult;
2332}
2333
2334//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2335//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
2336PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002337 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002338
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00002339#ifdef PDF_TRACE
2340 std::string str;
2341#endif
2342
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002343 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com59543d32013-06-18 22:00:40 +00002344 const SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002345
2346 if (extGStateDictionary == NULL) {
2347#ifdef PDF_TRACE
2348 printf("ExtGState is NULL!\n");
2349#endif
2350 return kIgnoreError_PdfResult;
2351 }
2352
edisonn@google.com59543d32013-06-18 22:00:40 +00002353 SkPdfObject value = extGStateDictionary->get(name.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002354
2355#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002356// value->ToString(str);
2357// printf("gs object value: %s\n", str.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002358#endif
2359
edisonn@google.com59543d32013-06-18 22:00:40 +00002360 SkPdfGraphicsStateDictionary* gs = NULL;
2361 PodofoMapper::map(value, &gs);
edisonn@google.com68d15c82013-06-17 20:46:27 +00002362
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002363 // TODO(edisonn): now load all those properties in graphic state.
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00002364 if (gs == NULL) {
2365 return kIgnoreError_PdfResult;
2366 }
2367
2368 if (gs->has_CA()) {
2369 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA();
2370 }
2371
2372 if (gs->has_ca()) {
2373 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca();
2374 }
2375
2376 if (gs->has_LW()) {
2377 pdfContext->fGraphicsState.fLineWidth = gs->LW();
2378 }
2379
2380
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002381
2382 return kNYI_PdfResult;
2383}
2384
2385//charSpace Tc Set the character spacing, Tc
2386//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2387//Initial value: 0.
2388PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002389 double charSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002390 pdfContext->fGraphicsState.fCharSpace = charSpace;
2391
2392 return kOK_PdfResult;
2393}
2394
2395//wordSpace Tw Set the word spacing, T
2396//w
2397//, to wordSpace, which is a number expressed in unscaled
2398//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2399//value: 0.
2400PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002401 double wordSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002402 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2403
2404 return kOK_PdfResult;
2405}
2406
2407//scale Tz Set the horizontal scaling, Th
2408//, to (scale ˜ 100). scale is a number specifying the
2409//percentage of the normal width. Initial value: 100 (normal width).
2410PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002411 double scale = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002412
2413 return kNYI_PdfResult;
2414}
2415
2416//render Tr Set the text rendering mode, T
2417//mode, to render, which is an integer. Initial value: 0.
2418PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002419 double render = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002420
2421 return kNYI_PdfResult;
2422}
2423
2424//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2425//units. Initial value: 0.
2426PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002427 double rise = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002428
2429 return kNYI_PdfResult;
2430}
2431
2432//wx wy d0
2433PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002434 pdfContext->fObjectStack.pop();
2435 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002436
2437 return kNYI_PdfResult;
2438}
2439
2440//wx wy llx lly urx ury d1
2441PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002442 pdfContext->fObjectStack.pop();
2443 pdfContext->fObjectStack.pop();
2444 pdfContext->fObjectStack.pop();
2445 pdfContext->fObjectStack.pop();
2446 pdfContext->fObjectStack.pop();
2447 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002448
2449 return kNYI_PdfResult;
2450}
2451
2452//name sh
2453PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002454 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002455
2456 return kNYI_PdfResult;
2457}
2458
2459//name Do
2460PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002461 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002462
edisonn@google.com59543d32013-06-18 22:00:40 +00002463 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002464
edisonn@google.com59543d32013-06-18 22:00:40 +00002465 if (xObject == NULL) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002466#ifdef PDF_TRACE
2467 printf("XObject is NULL!\n");
2468#endif
2469 return kIgnoreError_PdfResult;
2470 }
2471
edisonn@google.com59543d32013-06-18 22:00:40 +00002472 SkPdfObject value = xObject->get(name.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002473
2474#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002475// value->ToString(str);
2476// printf("Do object value: %s\n", str.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002477#endif
2478
edisonn@google.com59543d32013-06-18 22:00:40 +00002479 return doXObject(pdfContext, canvas, value);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002480}
2481
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002482//tag MP Designate a marked-content point. tag is a name object indicating the role or
2483//significance of the point.
2484PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002485 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002486
2487 return kNYI_PdfResult;
2488}
2489
2490//tag properties DP Designate a marked-content point with an associated property list. tag is a
2491//name object indicating the role or significance of the point; properties is
2492//either an inline dictionary containing the property list or a name object
2493//associated with it in the Properties subdictionary of the current resource
2494//dictionary (see Section 9.5.1, “Property Lists”).
2495PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002496 pdfContext->fObjectStack.pop();
2497 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002498
2499 return kNYI_PdfResult;
2500}
2501
2502//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2503//tag is a name object indicating the role or significance of the sequence.
2504PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002505 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002506
2507 return kNYI_PdfResult;
2508}
2509
2510//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2511//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2512//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”).
2513PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002514 pdfContext->fObjectStack.pop();
2515 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002516
2517 return kNYI_PdfResult;
2518}
2519
2520//— EMC End a marked-content sequence begun by a BMC or BDC operator.
2521PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2522 return kNYI_PdfResult;
2523}
2524
2525void initPdfOperatorRenderes() {
2526 static bool gInitialized = false;
2527 if (gInitialized) {
2528 return;
2529 }
2530
2531 gPdfOps["q"] = PdfOp_q;
2532 gPdfOps["Q"] = PdfOp_Q;
2533 gPdfOps["cm"] = PdfOp_cm;
2534
2535 gPdfOps["TD"] = PdfOp_TD;
2536 gPdfOps["Td"] = PdfOp_Td;
2537 gPdfOps["Tm"] = PdfOp_Tm;
2538 gPdfOps["T*"] = PdfOp_T_star;
2539
2540 gPdfOps["m"] = PdfOp_m;
2541 gPdfOps["l"] = PdfOp_l;
2542 gPdfOps["c"] = PdfOp_c;
2543 gPdfOps["v"] = PdfOp_v;
2544 gPdfOps["y"] = PdfOp_y;
2545 gPdfOps["h"] = PdfOp_h;
2546 gPdfOps["re"] = PdfOp_re;
2547
2548 gPdfOps["S"] = PdfOp_S;
2549 gPdfOps["s"] = PdfOp_s;
2550 gPdfOps["f"] = PdfOp_f;
2551 gPdfOps["F"] = PdfOp_F;
2552 gPdfOps["f*"] = PdfOp_f_star;
2553 gPdfOps["B"] = PdfOp_B;
2554 gPdfOps["B*"] = PdfOp_B_star;
2555 gPdfOps["b"] = PdfOp_b;
2556 gPdfOps["b*"] = PdfOp_b_star;
2557 gPdfOps["n"] = PdfOp_n;
2558
2559 gPdfOps["BT"] = PdfOp_BT;
2560 gPdfOps["ET"] = PdfOp_ET;
2561
2562 gPdfOps["Tj"] = PdfOp_Tj;
2563 gPdfOps["'"] = PdfOp_quote;
2564 gPdfOps["\""] = PdfOp_doublequote;
2565 gPdfOps["TJ"] = PdfOp_TJ;
2566
2567 gPdfOps["CS"] = PdfOp_CS;
2568 gPdfOps["cs"] = PdfOp_cs;
2569 gPdfOps["SC"] = PdfOp_SC;
2570 gPdfOps["SCN"] = PdfOp_SCN;
2571 gPdfOps["sc"] = PdfOp_sc;
2572 gPdfOps["scn"] = PdfOp_scn;
2573 gPdfOps["G"] = PdfOp_G;
2574 gPdfOps["g"] = PdfOp_g;
2575 gPdfOps["RG"] = PdfOp_RG;
2576 gPdfOps["rg"] = PdfOp_rg;
2577 gPdfOps["K"] = PdfOp_K;
2578 gPdfOps["k"] = PdfOp_k;
2579
2580 gPdfOps["W"] = PdfOp_W;
2581 gPdfOps["W*"] = PdfOp_W_star;
2582
2583 gPdfOps["BX"] = PdfOp_BX;
2584 gPdfOps["EX"] = PdfOp_EX;
2585
2586 gPdfOps["BI"] = PdfOp_BI;
2587 gPdfOps["ID"] = PdfOp_ID;
2588 gPdfOps["EI"] = PdfOp_EI;
2589
2590 gPdfOps["w"] = PdfOp_w;
2591 gPdfOps["J"] = PdfOp_J;
2592 gPdfOps["j"] = PdfOp_j;
2593 gPdfOps["M"] = PdfOp_M;
2594 gPdfOps["d"] = PdfOp_d;
2595 gPdfOps["ri"] = PdfOp_ri;
2596 gPdfOps["i"] = PdfOp_i;
2597 gPdfOps["gs"] = PdfOp_gs;
2598
2599 gPdfOps["Tc"] = PdfOp_Tc;
2600 gPdfOps["Tw"] = PdfOp_Tw;
2601 gPdfOps["Tz"] = PdfOp_Tz;
2602 gPdfOps["TL"] = PdfOp_TL;
2603 gPdfOps["Tf"] = PdfOp_Tf;
2604 gPdfOps["Tr"] = PdfOp_Tr;
2605 gPdfOps["Ts"] = PdfOp_Ts;
2606
2607 gPdfOps["d0"] = PdfOp_d0;
2608 gPdfOps["d1"] = PdfOp_d1;
2609
2610 gPdfOps["sh"] = PdfOp_sh;
2611
2612 gPdfOps["Do"] = PdfOp_Do;
2613
2614 gPdfOps["MP"] = PdfOp_MP;
2615 gPdfOps["DP"] = PdfOp_DP;
2616 gPdfOps["BMC"] = PdfOp_BMC;
2617 gPdfOps["BDC"] = PdfOp_BDC;
2618 gPdfOps["EMC"] = PdfOp_EMC;
2619
2620 gInitialized = true;
2621}
2622
2623void reportPdfRenderStats() {
2624 std::map<std::string, int>::iterator iter;
2625
2626 for (int i = 0 ; i < kCount_PdfResult; i++) {
2627 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++iter) {
2628 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_str(), iter->second);
2629 }
2630 }
2631}
2632
2633PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002634 if (token.fType == kKeyword_TokenType)
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002635 {
2636 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com59543d32013-06-18 22:00:40 +00002637 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[token.fKeyword];
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002638 if (pdfOperatorRenderer) {
2639 // caller, main work is done by pdfOperatorRenderer(...)
2640 PdfTokenLooper* childLooper = NULL;
edisonn@google.com59543d32013-06-18 22:00:40 +00002641 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)][token.fKeyword]++;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002642
2643 if (childLooper) {
2644 childLooper->setUp(this);
2645 childLooper->loop();
2646 delete childLooper;
2647 }
2648 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002649 gRenderStats[kUnsupported_PdfResult][token.fKeyword]++;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002650 }
2651 }
edisonn@google.com59543d32013-06-18 22:00:40 +00002652 else if (token.fType == kObject_TokenType)
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002653 {
edisonn@google.com59543d32013-06-18 22:00:40 +00002654 fPdfContext->fObjectStack.push( token.fObject );
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002655 }
edisonn@google.com59543d32013-06-18 22:00:40 +00002656 else if ( token.fType == kImageData_TokenType) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002657 // TODO(edisonn): implement inline image.
2658 }
2659 else {
2660 return kIgnoreError_PdfResult;
2661 }
2662 return kOK_PdfResult;
2663}
2664
2665void PdfMainLooper::loop() {
2666 PdfToken token;
2667 while (readToken(fTokenizer, &token)) {
2668 consumeToken(token);
2669 }
2670}
2671
2672PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
2673 //pdfContext.fInlineImage.fKeyValuePairs[key] = value;
2674 return kNYI_PdfResult;
2675}
2676
2677void PdfInlineImageLooper::loop() {
2678 PdfToken token;
2679 while (readToken(fTokenizer, &token)) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002680 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002681 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2682 looper->setUp(this);
2683 looper->loop();
2684 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002685 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002686 done();
2687 return;
2688 }
2689
2690 consumeToken(token);
2691 }
2692 }
2693 // TODO(edisonn): report error/warning, EOF without EI.
2694}
2695
2696PdfResult PdfInlineImageLooper::done() {
2697
2698 // TODO(edisonn): long to short names
2699 // TODO(edisonn): set properties in a map
2700 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
2701 // the stream.
2702
2703 SkBitmap bitmap;
2704 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
2705
2706 // TODO(edisonn): matrix use.
2707 // Draw dummy red square, to show the prezence of the inline image.
2708 fCanvas->drawBitmap(bitmap,
2709 SkDoubleToScalar(0),
2710 SkDoubleToScalar(0),
2711 NULL);
2712 return kNYI_PdfResult;
2713}
2714
2715PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2716 return fParent->consumeToken(token);
2717}
2718
2719void PdfCompatibilitySectionLooper::loop() {
2720 // TODO(edisonn): save stacks position, or create a new stack?
2721 // TODO(edisonn): what happens if we pop out more variables then when we started?
2722 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2723 // pop-ing too much will not affect outside the section.
2724 PdfToken token;
2725 while (readToken(fTokenizer, &token)) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002726 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002727 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2728 looper->setUp(this);
2729 looper->loop();
2730 delete looper;
2731 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002732 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002733 fParent->consumeToken(token);
2734 }
2735 }
2736 // TODO(edisonn): restore stack.
2737}
2738
2739// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2740// TODO(edisonn): Add API for Forms viewing and editing
2741// e.g. SkBitmap getPage(int page);
2742// int formsCount();
2743// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2744// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2745// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2746// resources needed, so the preview is fast.
2747// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2748// references automatically.
2749class SkPdfViewer : public SkRefCnt {
2750public:
2751
2752 bool load(const SkString inputFileName, SkPicture* out) {
2753
2754 initPdfOperatorRenderes();
2755
2756 try
2757 {
2758 std::cout << "Init: " << inputFileName.c_str() << std::endl;
2759
edisonn@google.com59543d32013-06-18 22:00:40 +00002760 SkPdfDoc doc(inputFileName.c_str());
2761 if( !doc.pages() )
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002762 {
2763 std::cout << "ERROR: Empty Document" << inputFileName.c_str() << std::endl;
2764 return false;
2765 } else {
2766
edisonn@google.com59543d32013-06-18 22:00:40 +00002767 for (int pn = 0; pn < doc.pages(); ++pn) {
2768 SkPdfPageObjectDictionary* page = doc.page(pn);
2769
2770 // TODO(edisonn): implement inheritance properties as per PDF spec
2771 //SkRect rect = page->MediaBox();
2772 SkRect rect = doc.MediaBox(pn);
2773
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002774#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002775 printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rect.width()), SkScalarToDouble(rect.height()));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002776#endif
2777
2778 // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to use?
2779
2780 SkBitmap bitmap;
2781#ifdef PDF_DEBUG_3X
edisonn@google.com59543d32013-06-18 22:00:40 +00002782 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()))
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002783#else
edisonn@google.com59543d32013-06-18 22:00:40 +00002784 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002785#endif
2786 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
2787 SkCanvas canvas(device);
2788
edisonn@google.comff278442013-06-21 21:03:15 +00002789 SkPdfTokenizer* tokenizer = doc.tokenizerOfPage(pn);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002790
edisonn@google.com59543d32013-06-18 22:00:40 +00002791 PdfContext pdfContext(doc);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002792 pdfContext.fOriginalMatrix = SkMatrix::I();
edisonn@google.com59543d32013-06-18 22:00:40 +00002793 pdfContext.fGraphicsState.fResources = NULL;
2794 PodofoMapper::map(*page->Resources(), &pdfContext.fGraphicsState.fResources);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002795
2796 gPdfContext = &pdfContext;
2797 gDumpBitmap = &bitmap;
2798 gDumpCanvas = &canvas;
2799
2800
2801 // TODO(edisonn): get matrix stuff right.
2802 // TODO(edisonn): add DPI/scale/zoom.
2803 SkScalar z = SkIntToScalar(0);
edisonn@google.com59543d32013-06-18 22:00:40 +00002804 SkScalar w = rect.width();
2805 SkScalar h = rect.height();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002806
2807 SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(w, z), SkPoint::Make(w, h), SkPoint::Make(z, h)};
2808// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2809
2810 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2811 // TODO(edisonn): add flagg for no clipping.
2812 // Use larger image to make sure we do not draw anything outside of page
2813 // could be used in tests.
2814
2815#ifdef PDF_DEBUG_3X
2816 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)};
2817#else
2818 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2819#endif
2820 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2821 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2822
2823 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2824 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2825
2826 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2827 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2828
2829 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2830 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2831
2832
2833 pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
2834 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
2835 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
2836
2837 canvas.setMatrix(pdfContext.fOriginalMatrix);
2838
2839#ifndef PDF_DEBUG_NO_PAGE_CLIPING
2840 canvas.clipRect(SkRect::MakeXYWH(z, z, w, h), SkRegion::kIntersect_Op, true);
2841#endif
2842
edisonn@google.com59543d32013-06-18 22:00:40 +00002843 PdfMainLooper looper(NULL, tokenizer, &pdfContext, &canvas);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002844 looper.loop();
2845
edisonn@google.comff278442013-06-21 21:03:15 +00002846 delete tokenizer;
2847
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002848 canvas.flush();
2849
2850 SkString out;
2851 out.appendf("%s-%i.png", inputFileName.c_str(), pn);
2852 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
2853 }
2854 return true;
2855 }
2856 }
2857 catch( PdfError & e )
2858 {
2859 std::cout << "ERROR: PDF can't be parsed!" << inputFileName.c_str() << std::endl;
2860 return false;
2861 }
2862
2863 return true;
2864 }
2865 bool write(void*) const { return false; }
2866};
2867
2868
2869
2870/**
2871 * Given list of directories and files to use as input, expects to find .pdf
2872 * files and it will convert them to .png files writing them in the same directory
2873 * one file for each page.
2874 *
2875 * Returns zero exit code if all .pdf files were converted successfully,
2876 * otherwise returns error code 1.
2877 */
2878
2879static const char PDF_FILE_EXTENSION[] = "pdf";
2880static const char PNG_FILE_EXTENSION[] = "png";
2881
2882// TODO(edisonn): add ability to write to a new directory.
2883static void usage(const char* argv0) {
2884 SkDebugf("PDF to PNG rendering tool\n");
2885 SkDebugf("\n"
2886"Usage: \n"
2887" %s <input>... -w <outputDir> \n"
2888, argv0);
2889 SkDebugf("\n\n");
2890 SkDebugf(
2891" input: A list of directories and files to use as input. Files are\n"
2892" expected to have the .skp extension.\n\n");
2893 SkDebugf(
2894" outputDir: directory to write the rendered pdfs.\n\n");
2895 SkDebugf("\n");
2896}
2897
2898/** Replaces the extension of a file.
2899 * @param path File name whose extension will be changed.
2900 * @param old_extension The old extension.
2901 * @param new_extension The new extension.
2902 * @returns false if the file did not has the expected extension.
2903 * if false is returned, contents of path are undefined.
2904 */
2905static bool replace_filename_extension(SkString* path,
2906 const char old_extension[],
2907 const char new_extension[]) {
2908 if (path->endsWith(old_extension)) {
2909 path->remove(path->size() - strlen(old_extension),
2910 strlen(old_extension));
2911 if (!path->endsWith(".")) {
2912 return false;
2913 }
2914 path->append(new_extension);
2915 return true;
2916 }
2917 return false;
2918}
2919
2920/** Builds the output filename. path = dir/name, and it replaces expected
2921 * .skp extension with .pdf extention.
2922 * @param path Output filename.
2923 * @param name The name of the file.
2924 * @returns false if the file did not has the expected extension.
2925 * if false is returned, contents of path are undefined.
2926 */
2927static bool make_output_filepath(SkString* path, const SkString& dir,
2928 const SkString& name) {
2929 sk_tools::make_filepath(path, dir, name);
2930 return replace_filename_extension(path,
2931 PDF_FILE_EXTENSION,
2932 PNG_FILE_EXTENSION);
2933}
2934
2935/** Write the output of pdf renderer to a file.
2936 * @param outputDir Output dir.
2937 * @param inputFilename The skp file that was read.
2938 * @param renderer The object responsible to write the pdf file.
2939 */
2940static bool write_output(const SkString& outputDir,
2941 const SkString& inputFilename,
2942 const SkPdfViewer& renderer) {
2943 if (outputDir.isEmpty()) {
2944 SkDynamicMemoryWStream stream;
2945 renderer.write(&stream);
2946 return true;
2947 }
2948
2949 SkString outputPath;
2950 if (!make_output_filepath(&outputPath, outputDir, inputFilename)) {
2951 return false;
2952 }
2953
2954 SkFILEWStream stream(outputPath.c_str());
2955 if (!stream.isValid()) {
2956 SkDebugf("Could not write to file %s\n", outputPath.c_str());
2957 return false;
2958 }
2959 renderer.write(&stream);
2960
2961 return true;
2962}
2963
2964/** Reads an skp file, renders it to pdf and writes the output to a pdf file
2965 * @param inputPath The skp file to be read.
2966 * @param outputDir Output dir.
2967 * @param renderer The object responsible to render the skp object into pdf.
2968 */
2969static bool parse_pdf(const SkString& inputPath, const SkString& outputDir,
2970 SkPdfViewer& renderer) {
2971 SkString inputFilename;
2972 sk_tools::get_basename(&inputFilename, inputPath);
2973
2974 SkFILEStream inputStream;
2975 inputStream.setPath(inputPath.c_str());
2976 if (!inputStream.isValid()) {
2977 SkDebugf("Could not open file %s\n", inputPath.c_str());
2978 return false;
2979 }
2980
2981 bool success = false;
2982
2983 success = renderer.load(inputPath, NULL);
2984
2985
2986// success = write_output(outputDir, inputFilename, renderer);
2987
2988 //renderer.end();
2989 return success;
2990}
2991
2992/** For each file in the directory or for the file passed in input, call
2993 * parse_pdf.
2994 * @param input A directory or an pdf file.
2995 * @param outputDir Output dir.
2996 * @param renderer The object responsible to render the skp object into pdf.
2997 */
2998static int process_input(const SkString& input, const SkString& outputDir,
2999 SkPdfViewer& renderer) {
3000 int failures = 0;
3001 if (sk_isdir(input.c_str())) {
3002 SkOSFile::Iter iter(input.c_str(), PDF_FILE_EXTENSION);
3003 SkString inputFilename;
3004 while (iter.next(&inputFilename)) {
3005 SkString inputPath;
3006 sk_tools::make_filepath(&inputPath, input, inputFilename);
3007 if (!parse_pdf(inputPath, outputDir, renderer)) {
3008 ++failures;
3009 }
3010 }
3011 } else {
3012 SkString inputPath(input);
3013 if (!parse_pdf(inputPath, outputDir, renderer)) {
3014 ++failures;
3015 }
3016 }
3017 return failures;
3018}
3019
3020static void parse_commandline(int argc, char* const argv[],
3021 SkTArray<SkString>* inputs,
3022 SkString* outputDir) {
3023 const char* argv0 = argv[0];
3024 char* const* stop = argv + argc;
3025
3026 for (++argv; argv < stop; ++argv) {
3027 if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
3028 usage(argv0);
3029 exit(-1);
3030 } else if (0 == strcmp(*argv, "-w")) {
3031 ++argv;
3032 if (argv >= stop) {
3033 SkDebugf("Missing outputDir for -w\n");
3034 usage(argv0);
3035 exit(-1);
3036 }
3037 *outputDir = SkString(*argv);
3038 } else {
3039 inputs->push_back(SkString(*argv));
3040 }
3041 }
3042
3043 if (inputs->count() < 1) {
3044 usage(argv0);
3045 exit(-1);
3046 }
3047}
3048
3049int tool_main(int argc, char** argv);
3050int tool_main(int argc, char** argv) {
3051 SkAutoGraphics ag;
3052 SkTArray<SkString> inputs;
3053
3054 SkAutoTUnref<SkPdfViewer>
3055 renderer(SkNEW(SkPdfViewer));
3056 SkASSERT(renderer.get());
3057
3058 SkString outputDir;
3059 parse_commandline(argc, argv, &inputs, &outputDir);
3060
3061 int failures = 0;
3062 for (int i = 0; i < inputs.count(); i ++) {
3063 failures += process_input(inputs[i], outputDir, *renderer);
3064 }
3065
3066 reportPdfRenderStats();
3067
3068 if (failures != 0) {
3069 SkDebugf("Failed to render %i PDFs.\n", failures);
3070 return 1;
3071 }
3072
3073 return 0;
3074}
3075
3076#if !defined SK_BUILD_FOR_IOS
3077int main(int argc, char * const argv[]) {
3078 return tool_main(argc, (char**) argv);
3079}
3080#endif