blob: 61997fa5ccef746e47ffdcac7795c71d76e18619 [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.comb857a0c2013-06-25 20:45:40 +000031// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
32// keep for each object pos in file
33// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
edisonn@google.comff278442013-06-21 21:03:15 +000034
edisonn@google.comb857a0c2013-06-25 20:45:40 +000035// TODO(edisonn): security - validate all the user input, all pdf!
edisonn@google.comff278442013-06-21 21:03:15 +000036
37
edisonn@google.com59543d32013-06-18 22:00:40 +000038#include "SkPdfHeaders_autogen.h"
39#include "SkPdfPodofoMapper_autogen.h"
40#include "SkPdfParser.h"
edisonn@google.comb857a0c2013-06-25 20:45:40 +000041
42#include "SkPdfBasics.h"
43#include "SkPdfUtils.h"
44
edisonn@google.comff278442013-06-21 21:03:15 +000045#include "SkPdfFont.h"
46
47// TODO(edisonn): fix the mess with the files.
48#include "SkPdfFont.cpp"
edisonn@google.comb857a0c2013-06-25 20:45:40 +000049#include "SkPdfBasics.cpp"
50#include "SkPdfUtils.cpp"
edisonn@google.com01cd4d52013-06-10 20:44:45 +000051
edisonn@google.com1be794f2013-06-21 21:43:09 +000052bool skpdfmap(const PdfMemDocument& podofoDoc, const PdfObject& podofoObj, SkPdfObject** out) {
53 return PodofoMapper::map(podofoDoc, podofoObj, out);
54}
55
56
edisonn@google.com01cd4d52013-06-10 20:44:45 +000057/*
edisonn@google.com68d15c82013-06-17 20:46:27 +000058 * TODO(edisonn):
edisonn@google.comafe5e9e2013-06-19 17:42:17 +000059 * - all font types and all ppdf font features
60 * - word spacing
61 * - load font for baidu.pdf
62 * - load font for youtube.pdf
63 * - parser for pdf from the definition already available in pdfspec_autogen.py
edisonn@google.comff278442013-06-21 21:03:15 +000064 * - all docs from ~/work
65 * - encapsulate podofo in the pdf api so the skpdf does not know anything about podofo ... in progress
66 * - load gs/ especially smask and already known prop (skp) ... in progress
edisonn@google.comafe5e9e2013-06-19 17:42:17 +000067 * - wrapper on classes for customizations? e.g.
68 * SkPdfPageObjectVanila - has only the basic loaders/getters
69 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
70 * need to find a nice object model for all this with constructors and factories
71 * - deal with inheritable automatically ?
72 * - deal with specific type in spec directly, add all dictionary types to known types
edisonn@google.com01cd4d52013-06-10 20:44:45 +000073*/
74
edisonn@google.com01cd4d52013-06-10 20:44:45 +000075
76// TODO(edisonn): move in trace util.
77#ifdef PDF_TRACE
78static void SkTraceMatrix(const SkMatrix& matrix, const char* sz = "") {
79 printf("SkMatrix %s ", sz);
80 for (int i = 0 ; i < 9 ; i++) {
81 printf("%f ", SkScalarToDouble(matrix.get(i)));
82 }
83 printf("\n");
84}
edisonn@google.comb857a0c2013-06-25 20:45:40 +000085
86static void SkTraceRect(const SkRect& rect, const char* sz = "") {
87 printf("SkRect %s ", sz);
88 printf("x = %f ", SkScalarToDouble(rect.x()));
89 printf("y = %f ", SkScalarToDouble(rect.y()));
90 printf("w = %f ", SkScalarToDouble(rect.width()));
91 printf("h = %f ", SkScalarToDouble(rect.height()));
92 printf("\n");
93}
94
edisonn@google.com01cd4d52013-06-10 20:44:45 +000095#else
96#define SkTraceMatrix(a,b)
edisonn@google.comb857a0c2013-06-25 20:45:40 +000097#define SkTraceRect(a,b)
edisonn@google.com01cd4d52013-06-10 20:44:45 +000098#endif
99
100using namespace std;
101using namespace PoDoFo;
102
103// Utilities
104static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
105 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
106
107 bitmap->allocPixels();
108 bitmap->eraseColor(color);
109}
110
111// TODO(edisonn): synonyms? DeviceRGB and RGB ...
112int GetColorSpaceComponents(const std::string& colorSpace) {
113 if (colorSpace == "DeviceCMYK") {
114 return 4;
115 } else if (colorSpace == "DeviceGray" ||
116 colorSpace == "CalGray" ||
117 colorSpace == "Indexed") {
118 return 1;
119 } else if (colorSpace == "DeviceRGB" ||
120 colorSpace == "CalRGB" ||
121 colorSpace == "Lab") {
122 return 3;
123 } else {
124 return 0;
125 }
126}
127
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000128const PdfObject* resolveReferenceObject(const PdfMemDocument* pdfDoc,
129 const PdfObject* obj,
edisonn@google.com59543d32013-06-18 22:00:40 +0000130 bool resolveOneElementArrays) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000131 while (obj && (obj->IsReference() || (resolveOneElementArrays &&
132 obj->IsArray() &&
133 obj->GetArray().GetSize() == 1))) {
134 if (obj->IsReference()) {
135 // We need to force the non const, the only update we will do is for recurssion checks.
136 PdfReference& ref = (PdfReference&)obj->GetReference();
137 obj = pdfDoc->GetObjects().GetObject(ref);
138 } else {
139 obj = &obj->GetArray()[0];
140 }
141 }
142
143 return obj;
144}
145
146static SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
147 SkMatrix matrix;
148 matrix.setAll(SkDoubleToScalar(array[0]),
149 SkDoubleToScalar(array[2]),
150 SkDoubleToScalar(array[4]),
151 SkDoubleToScalar(array[1]),
152 SkDoubleToScalar(array[3]),
153 SkDoubleToScalar(array[5]),
154 SkDoubleToScalar(0),
155 SkDoubleToScalar(0),
156 SkDoubleToScalar(1));
157
158 return matrix;
159}
160
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000161SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
162 double array[6];
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000163
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000164 // TODO(edisonn): security issue, ret if size() != 6
165 for (int i = 0; i < 6; i++) {
166 const PdfObject* elem = resolveReferenceObject(pdfArray->doc(), (*pdfArray)[i]->podofo());
167 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
168 return SkMatrix::I(); // TODO(edisonn): report issue
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000169 }
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000170 array[i] = elem->GetReal();
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000171 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000172
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000173 return SkMatrixFromPdfMatrix(array);
174}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000175
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000176PdfContext* gPdfContext = NULL;
177SkBitmap* gDumpBitmap = NULL;
178SkCanvas* gDumpCanvas = NULL;
179char gLastKeyword[100] = "";
180int gLastOpKeyword = -1;
edisonn@google.com59543d32013-06-18 22:00:40 +0000181char 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 +0000182int gReadOp = 0;
183
184
185
186bool hasVisualEffect(const char* pdfOp) {
187 return true;
188 if (*pdfOp == '\0') return false;
189
190 char markedPdfOp[100] = ",";
191 strcat(markedPdfOp, pdfOp);
192 strcat(markedPdfOp, ",");
193
194 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
195}
196
197// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.comff278442013-06-21 21:03:15 +0000198static bool readToken(SkPdfTokenizer* fTokenizer, PdfToken* token) {
199 bool ret = fTokenizer->readToken(token);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000200
201 gReadOp++;
202
203#ifdef PDF_TRACE_DIFF_IN_PNG
204 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
205 // the numbar and name of last operation, so the file name will reflect op that changed.
206 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
207 gDumpCanvas->flush();
208
209 SkBitmap bitmap;
210 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
211
212 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
213
214 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
215 SkCanvas canvas(device);
216
217 // draw context stuff here
218 SkPaint blueBorder;
219 blueBorder.setColor(SK_ColorBLUE);
220 blueBorder.setStyle(SkPaint::kStroke_Style);
221 blueBorder.setTextSize(SkDoubleToScalar(20));
222
223 SkString str;
224
225 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
226 if (clipStack) {
227 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
228 const SkClipStack::Element* elem;
229 double y = 0;
230 int total = 0;
231 while (elem = iter.next()) {
232 total++;
233 y += 30;
234
235 switch (elem->getType()) {
236 case SkClipStack::Element::kRect_Type:
237 canvas.drawRect(elem->getRect(), blueBorder);
238 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
239 break;
240 case SkClipStack::Element::kPath_Type:
241 canvas.drawPath(elem->getPath(), blueBorder);
242 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
243 break;
244 case SkClipStack::Element::kEmpty_Type:
245 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
246 break;
247 default:
248 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
249 break;
250 }
251 }
252
253 y += 30;
254 str.printf("Number of clips in stack: %i", total);
255 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
256 }
257
258 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
259 SkPath clipPath;
260 if (clipRegion.getBoundaryPath(&clipPath)) {
261 SkPaint redBorder;
262 redBorder.setColor(SK_ColorRED);
263 redBorder.setStyle(SkPaint::kStroke_Style);
264 canvas.drawPath(clipPath, redBorder);
265 }
266
267 canvas.flush();
268
269 SkString out;
270
271 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
272 // ... and other properties, to be able to debug th code easily
273
274 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
275 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
276 }
277
edisonn@google.com59543d32013-06-18 22:00:40 +0000278 if (token->fType == kKeyword_TokenType) {
279 strcpy(gLastKeyword, token->fKeyword);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000280 gLastOpKeyword = gReadOp;
281 } else {
282 strcpy(gLastKeyword, "");
283 }
284#endif
285
286 return ret;
287}
288
289// TODO(edisonn): Document PdfTokenLooper and subclasses.
290class PdfTokenLooper {
291protected:
292 PdfTokenLooper* fParent;
edisonn@google.comff278442013-06-21 21:03:15 +0000293 SkPdfTokenizer* fTokenizer;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000294 PdfContext* fPdfContext;
295 SkCanvas* fCanvas;
296
297public:
298 PdfTokenLooper(PdfTokenLooper* parent,
edisonn@google.comff278442013-06-21 21:03:15 +0000299 SkPdfTokenizer* tokenizer,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000300 PdfContext* pdfContext,
301 SkCanvas* canvas)
302 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
303
304 virtual PdfResult consumeToken(PdfToken& token) = 0;
305 virtual void loop() = 0;
306
307 void setUp(PdfTokenLooper* parent) {
308 fParent = parent;
309 fTokenizer = parent->fTokenizer;
310 fPdfContext = parent->fPdfContext;
311 fCanvas = parent->fCanvas;
312 }
313};
314
315class PdfMainLooper : public PdfTokenLooper {
316public:
317 PdfMainLooper(PdfTokenLooper* parent,
edisonn@google.comff278442013-06-21 21:03:15 +0000318 SkPdfTokenizer* tokenizer,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000319 PdfContext* pdfContext,
320 SkCanvas* canvas)
321 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
322
323 virtual PdfResult consumeToken(PdfToken& token);
324 virtual void loop();
325};
326
327class PdfInlineImageLooper : public PdfTokenLooper {
328public:
329 PdfInlineImageLooper()
edisonn@google.comff278442013-06-21 21:03:15 +0000330 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000331
332 virtual PdfResult consumeToken(PdfToken& token);
333 virtual void loop();
334 PdfResult done();
335};
336
337class PdfCompatibilitySectionLooper : public PdfTokenLooper {
338public:
339 PdfCompatibilitySectionLooper()
edisonn@google.comff278442013-06-21 21:03:15 +0000340 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000341
342 virtual PdfResult consumeToken(PdfToken& token);
343 virtual void loop();
344};
345
346typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
347
348map<std::string, PdfOperatorRenderer> gPdfOps;
349
350map<std::string, int> gRenderStats[kCount_PdfResult];
351
352char* gRenderStatsNames[kCount_PdfResult] = {
353 "Success",
354 "Partially implemented",
355 "Not yet implemented",
356 "Ignore Error",
357 "Error",
358 "Unsupported/Unknown"
359};
360
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000361static SkTypeface* SkTypefaceFromPdfFont(PdfFont* font) {
edisonn@google.comff278442013-06-21 21:03:15 +0000362 if (font == NULL) {
363 return SkTypeface::CreateFromName("Times New Roman", SkTypeface::kNormal);
364 }
365
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000366 PdfObject* fontObject = font->GetObject();
367
368 PdfObject* pBaseFont = NULL;
369 // TODO(edisonn): warning, PoDoFo has a bug in PdfFont constructor, does not call InitVars()
370 // for now fixed locally.
371 pBaseFont = fontObject->GetIndirectKey( "BaseFont" );
372 const char* pszBaseFontName = pBaseFont->GetName().GetName().c_str();
373
374#ifdef PDF_TRACE
375 std::string str;
376 fontObject->ToString(str);
377 printf("Base Font Name: %s\n", pszBaseFontName);
378 printf("Font Object Data: %s\n", str.c_str());
379#endif
380
381 SkTypeface* typeface = SkTypefaceFromPdfStandardFont(pszBaseFontName, font->IsBold(), font->IsItalic());
382
383 if (typeface != NULL) {
384 return typeface;
385 }
386
387 char name[1000];
388 // HACK
389 strncpy(name, pszBaseFontName, 1000);
390 char* comma = strstr(name, ",");
391 char* dash = strstr(name, "-");
392 if (comma) *comma = '\0';
393 if (dash) *dash = '\0';
394
395 typeface = SkTypeface::CreateFromName(
396 name,
397 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
398 (font->IsItalic() ? SkTypeface::kItalic : 0)));
399
400 if (typeface != NULL) {
401#ifdef PDF_TRACE
402 printf("HACKED FONT found %s\n", name);
403#endif
404 return typeface;
405 }
406
407#ifdef PDF_TRACE
408 printf("FONT_NOT_FOUND %s\n", pszBaseFontName);
409#endif
410
411 // TODO(edisonn): Report Warning, NYI
412 return SkTypeface::CreateFromName(
413 "Times New Roman",
414 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
415 (font->IsItalic() ? SkTypeface::kItalic : 0)));
416}
417
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000418PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +0000419 const SkPdfObject* str,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000420 SkCanvas* canvas)
421{
edisonn@google.comff278442013-06-21 21:03:15 +0000422
423 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
424 if (skfont == NULL) {
425 skfont = SkPdfFont::Default();
426 }
427
428 SkUnencodedText binary(str);
429
430 SkDecodedText decoded;
edisonn@google.comff278442013-06-21 21:03:15 +0000431
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000432 if (skfont->encoding() == NULL) {
433 // TODO(edisonn): report warning
434 return kNYI_PdfResult;
435 }
436
437 skfont->encoding()->decodeText(binary, &decoded);
edisonn@google.comff278442013-06-21 21:03:15 +0000438
439 SkPaint paint;
440 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
441 // Or maybe just not call setTextSize at all?
442 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
443 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
444 }
445
446// if (fCurFont && fCurFont->GetFontScale() != 0) {
447// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
448// }
449
450 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
451
452 canvas->save();
453
454#if 1
455 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
456
457 SkPoint point1;
458 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
459
460 SkMatrix mirror;
461 mirror.setTranslate(0, -point1.y());
462 // TODO(edisonn): fix rotated text, and skewed too
463 mirror.postScale(SK_Scalar1, -SK_Scalar1);
464 // TODO(edisonn): post rotate, skew
465 mirror.postTranslate(0, point1.y());
466
467 matrix.postConcat(mirror);
468
469 canvas->setMatrix(matrix);
470
471 SkTraceMatrix(matrix, "mirrored");
472#endif
473
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000474 skfont->drawText(decoded, &paint, pdfContext, canvas, &pdfContext->fGraphicsState.fMatrixTm);
edisonn@google.comff278442013-06-21 21:03:15 +0000475 canvas->restore();
476
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000477 return kPartial_PdfResult;
478}
479
480// TODO(edisonn): create header files with declarations!
481PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
482PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
483PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
484PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
485
486// TODO(edisonn): deal with synonyms (/BPC == /BitsPerComponent), here or in GetKey?
487// Always pass long form in key, and have a map of long -> short key
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000488bool LongFromDictionary(const PdfMemDocument* pdfDoc,
489 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000490 const char* key,
491 long* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000492 const PdfObject* value = resolveReferenceObject(pdfDoc,
493 dict.GetKey(PdfName(key)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000494
495 if (value == NULL || !value->IsNumber()) {
496 return false;
497 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000498 if (data == NULL) {
499 return true;
500 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000501
502 *data = value->GetNumber();
503 return true;
504}
505
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000506bool LongFromDictionary(const PdfMemDocument* pdfDoc,
507 const PdfDictionary& dict,
508 const char* key,
509 const char* abr,
510 long* data) {
511 if (LongFromDictionary(pdfDoc, dict, key, data)) return true;
512 if (abr == NULL || *abr == '\0') return false;
513 return LongFromDictionary(pdfDoc, dict, abr, data);
514}
515
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000516bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
517 const PdfDictionary& dict,
518 const char* key,
519 double* data) {
520 const PdfObject* value = resolveReferenceObject(pdfDoc,
521 dict.GetKey(PdfName(key)));
522
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000523 if (value == NULL || (!value->IsReal() && !value->IsNumber())) {
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000524 return false;
525 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000526 if (data == NULL) {
527 return true;
528 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000529
530 *data = value->GetReal();
531 return true;
532}
533
534bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
535 const PdfDictionary& dict,
536 const char* key,
537 const char* abr,
538 double* data) {
539 if (DoubleFromDictionary(pdfDoc, dict, key, data)) return true;
540 if (abr == NULL || *abr == '\0') return false;
541 return DoubleFromDictionary(pdfDoc, dict, abr, data);
542}
543
544
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000545bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
546 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000547 const char* key,
548 bool* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000549 const PdfObject* value = resolveReferenceObject(pdfDoc,
550 dict.GetKey(PdfName(key)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000551
552 if (value == NULL || !value->IsBool()) {
553 return false;
554 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000555 if (data == NULL) {
556 return true;
557 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000558
559 *data = value->GetBool();
560 return true;
561}
562
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000563bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
564 const PdfDictionary& dict,
565 const char* key,
566 const char* abr,
567 bool* data) {
568 if (BoolFromDictionary(pdfDoc, dict, key, data)) return true;
569 if (abr == NULL || *abr == '\0') return false;
570 return BoolFromDictionary(pdfDoc, dict, abr, data);
571}
572
573bool NameFromDictionary(const PdfMemDocument* pdfDoc,
574 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000575 const char* key,
576 std::string* data) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000577 const PdfObject* value = resolveReferenceObject(pdfDoc,
578 dict.GetKey(PdfName(key)),
579 true);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000580 if (value == NULL || !value->IsName()) {
581 return false;
582 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000583 if (data == NULL) {
584 return true;
585 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000586
587 *data = value->GetName().GetName();
588 return true;
589}
590
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000591bool NameFromDictionary(const PdfMemDocument* pdfDoc,
592 const PdfDictionary& dict,
593 const char* key,
594 const char* abr,
595 std::string* data) {
596 if (NameFromDictionary(pdfDoc, dict, key, data)) return true;
597 if (abr == NULL || *abr == '\0') return false;
598 return NameFromDictionary(pdfDoc, dict, abr, data);
599}
600
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000601bool StringFromDictionary(const PdfMemDocument* pdfDoc,
602 const PdfDictionary& dict,
603 const char* key,
604 std::string* data) {
605 const PdfObject* value = resolveReferenceObject(pdfDoc,
606 dict.GetKey(PdfName(key)),
607 true);
608 if (value == NULL || (!value->IsString() && !value->IsHexString())) {
609 return false;
610 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000611 if (data == NULL) {
612 return true;
613 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000614
615 *data = value->GetString().GetString();
616 return true;
617}
618
619bool StringFromDictionary(const PdfMemDocument* pdfDoc,
620 const PdfDictionary& dict,
621 const char* key,
622 const char* abr,
623 std::string* data) {
624 if (StringFromDictionary(pdfDoc, dict, key, data)) return true;
625 if (abr == NULL || *abr == '\0') return false;
626 return StringFromDictionary(pdfDoc, dict, abr, data);
627}
628
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000629bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
630 const PdfDictionary& dict,
631 const char* key,
632 SkPdfArray** data) {
633 const PdfObject* value = resolveReferenceObject(pdfDoc,
634 dict.GetKey(PdfName(key)),
635 true);
636 if (value == NULL || !value->IsArray()) {
637 return false;
638 }
639 if (data == NULL) {
640 return true;
641 }
642
643 return PodofoMapper::map(*pdfDoc, *value, (SkPdfObject**)data);
644}
645
646
647bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
648 const PdfDictionary& dict,
649 const char* key,
650 const char* abr,
651 SkPdfArray** data) {
652 if (ArrayFromDictionary(pdfDoc, dict, key, data)) return true;
653 if (abr == NULL || *abr == '\0') return false;
654 return ArrayFromDictionary(pdfDoc, dict, abr, data);
655}
656
657
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000658bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
659 const PdfDictionary& dict,
660 const char* key,
661 SkPdfDictionary** data) {
662 const PdfObject* value = resolveReferenceObject(pdfDoc,
663 dict.GetKey(PdfName(key)),
664 true);
665 if (value == NULL || !value->IsDictionary()) {
666 return false;
667 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000668 if (data == NULL) {
669 return true;
670 }
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000671
edisonn@google.com68d15c82013-06-17 20:46:27 +0000672 return PodofoMapper::map(*pdfDoc, *value, (SkPdfObject**)data);
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000673}
674
675bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
676 const PdfDictionary& dict,
677 const char* key,
678 const char* abr,
679 SkPdfDictionary** data) {
680 if (DictionaryFromDictionary(pdfDoc, dict, key, data)) return true;
681 if (abr == NULL || *abr == '\0') return false;
682 return DictionaryFromDictionary(pdfDoc, dict, abr, data);
683}
684
edisonn@google.com68d15c82013-06-17 20:46:27 +0000685template <typename T>
686bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
687 const PdfDictionary& dict,
688 const char* key,
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000689 T** data) {
edisonn@google.com68d15c82013-06-17 20:46:27 +0000690 const PdfObject* value = resolveReferenceObject(pdfDoc,
691 dict.GetKey(PdfName(key)),
692 true);
693 if (value == NULL || !value->IsDictionary()) {
694 return false;
695 }
696
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000697 if (data == NULL) {
698 return true;
699 }
700
edisonn@google.com68d15c82013-06-17 20:46:27 +0000701 return PodofoMapper::map(*pdfDoc, *value, (T**)data);
702}
703
704template <typename T>
705bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
706 const PdfDictionary& dict,
707 const char* key,
708 const char* abr,
709 T** data) {
710 if (DictionaryFromDictionary2<T>(pdfDoc, dict, key, data)) return true;
711 if (abr == NULL || *abr == '\0') return false;
712 return DictionaryFromDictionary2<T>(pdfDoc, dict, abr, data);
713}
714
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000715bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
716 const PdfDictionary& dict,
717 const char* key,
718 SkPdfObject** data) {
719 const PdfObject* value = resolveReferenceObject(pdfDoc,
720 dict.GetKey(PdfName(key)),
721 true);
722 if (value == NULL) {
723 return false;
724 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +0000725 if (data == NULL) {
726 return true;
727 }
edisonn@google.com68d15c82013-06-17 20:46:27 +0000728 return PodofoMapper::map(*pdfDoc, *value, data);
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000729}
730
731bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
732 const PdfDictionary& dict,
733 const char* key,
734 const char* abr,
735 SkPdfObject** data) {
736 if (ObjectFromDictionary(pdfDoc, dict, key, data)) return true;
737 if (abr == NULL || *abr == '\0') return false;
738 return ObjectFromDictionary(pdfDoc, dict, abr, data);
739}
740
edisonn@google.comff278442013-06-21 21:03:15 +0000741bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
742 const PdfDictionary& dict,
743 const char* key,
744 SkPdfStream** data) {
745 const PdfObject* value = resolveReferenceObject(pdfDoc,
746 dict.GetKey(PdfName(key)),
747 true);
748 if (value == NULL) {
749 return false;
750 }
751 if (data == NULL) {
752 return true;
753 }
754 return PodofoMapper::map(*pdfDoc, *value, data);
755}
756
757bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
758 const PdfDictionary& dict,
759 const char* key,
760 const char* abr,
761 SkPdfStream** data) {
762 if (StreamFromDictionary(pdfDoc, dict, key, data)) return true;
763 if (abr == NULL || *abr == '\0') return false;
764 return StreamFromDictionary(pdfDoc, dict, abr, data);
765}
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000766
edisonn@google.com68d15c82013-06-17 20:46:27 +0000767
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000768// TODO(edisonn): perf!!!
769
770static SkColorTable* getGrayColortable() {
771 static SkColorTable* grayColortable = NULL;
772 if (grayColortable == NULL) {
773 SkPMColor* colors = new SkPMColor[256];
774 for (int i = 0 ; i < 256; i++) {
775 colors[i] = SkPreMultiplyARGB(255, i, i, i);
776 }
777 grayColortable = new SkColorTable(colors, 256);
778 }
779 return grayColortable;
780}
781
782SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
783 int width, int height, int bytesPerLine,
784 int bpc, const std::string& colorSpace,
785 bool transparencyMask) {
786 SkBitmap bitmap;
787
788 int components = GetColorSpaceComponents(colorSpace);
789//#define MAX_COMPONENTS 10
790
791 int bitsPerLine = width * components * bpc;
792 // TODO(edisonn): assume start of lines are aligned at 32 bits?
793 // Is there a faster way to load the uncompressed stream into a bitmap?
794
795 // minimal support for now
796 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
797 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
798
799 for (int h = 0 ; h < height; h++) {
800 long i = width * (height - 1 - h);
801 for (int w = 0 ; w < width; w++) {
802 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
803 uncompressedStream[3 * w + 1],
804 uncompressedStream[3 * w + 2]);
805 i++;
806 }
807 uncompressedStream += bytesPerLine;
808 }
809
810 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
811 bitmap.setPixels(uncompressedStreamArgb);
812 }
813 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
814 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
815
816 for (int h = 0 ; h < height; h++) {
817 long i = width * (height - 1 - h);
818 for (int w = 0 ; w < width; w++) {
819 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
820 uncompressedStream[w];
821 i++;
822 }
823 uncompressedStream += bytesPerLine;
824 }
825
826 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
827 width, height);
828 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
829 }
830
831 // TODO(edisonn): Report Warning, NYI, or error
832 return bitmap;
833}
834
835bool transferImageStreamToARGB(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
836 int width, int bytesPerLine,
837 int bpc, const std::string& colorSpace,
838 SkColor** uncompressedStreamArgb,
839 pdf_long* uncompressedStreamLengthInBytesArgb) {
840 int components = GetColorSpaceComponents(colorSpace);
841//#define MAX_COMPONENTS 10
842
843 int bitsPerLine = width * components * bpc;
844 // TODO(edisonn): assume start of lines are aligned at 32 bits?
845 int height = uncompressedStreamLength / bytesPerLine;
846
847 // minimal support for now
848 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
849 *uncompressedStreamLengthInBytesArgb = width * height * 4;
850 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
851
852 for (int h = 0 ; h < height; h++) {
853 long i = width * (height - 1 - h);
854 for (int w = 0 ; w < width; w++) {
855 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[3 * w],
856 uncompressedStream[3 * w + 1],
857 uncompressedStream[3 * w + 2]);
858 i++;
859 }
860 uncompressedStream += bytesPerLine;
861 }
862 return true;
863 }
864
865 if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
866 *uncompressedStreamLengthInBytesArgb = width * height * 4;
867 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
868
869 for (int h = 0 ; h < height; h++) {
870 long i = width * (height - 1 - h);
871 for (int w = 0 ; w < width; w++) {
872 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[w],
873 uncompressedStream[w],
874 uncompressedStream[w]);
875 i++;
876 }
877 uncompressedStream += bytesPerLine;
878 }
879 return true;
880 }
881
882 return false;
883}
884
885// utils
886
887// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
888// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
889// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
890// skia format, through a table
891
892// this functions returns the image, it does not look at the smask.
893
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000894SkBitmap getImageFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000895 if (image == NULL || !image->valid()) {
896 // TODO(edisonn): report warning to be used in testing.
897 return SkBitmap();
898 }
899
900 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
901// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
902// obj.GetDictionary().GetKey(PdfName("Filter")));
903// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
904// value = resolveReferenceObject(pdfContext->fPdfDoc,
905// &value->GetArray()[0]);
906// }
907// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
908// SkStream stream = SkStream::
909// SkImageDecoder::Factory()
910// }
911
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000912 long bpc = image->BitsPerComponent();
913 long width = image->Width();
914 long height = image->Height();
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000915 std::string colorSpace = "DeviceRGB";
edisonn@google.com1277cf02013-06-17 23:36:45 +0000916
917 // TODO(edisonn): color space can be an array too!
918 if (image->isColorSpaceAName()) {
919 colorSpace = image->getColorSpaceAsName();
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000920 }
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000921
922/*
923 bool imageMask = image->imageMask();
924
925 if (imageMask) {
926 if (bpc != 0 && bpc != 1) {
927 // TODO(edisonn): report warning to be used in testing.
928 return SkBitmap();
929 }
930 bpc = 1;
931 }
932*/
933
934 const PdfObject* obj = image->podofo();
935
936 char* uncompressedStream = NULL;
937 pdf_long uncompressedStreamLength = 0;
938
939 PdfResult ret = kPartial_PdfResult;
940 // TODO(edisonn): get rid of try/catch exceptions! We should not throw on user data!
941 try {
942 obj->GetStream()->GetFilteredCopy(&uncompressedStream, &uncompressedStreamLength);
943 } catch (PdfError& e) {
944 // TODO(edisonn): report warning to be used in testing.
945 return SkBitmap();
946 }
947
948 int bytesPerLine = uncompressedStreamLength / height;
949#ifdef PDF_TRACE
950 if (uncompressedStreamLength % height != 0) {
951 printf("Warning uncompressedStreamLength % height != 0 !!!\n");
952 }
953#endif
954
955 SkBitmap bitmap = transferImageStreamToBitmap(
956 (unsigned char*)uncompressedStream, uncompressedStreamLength,
957 width, height, bytesPerLine,
958 bpc, colorSpace,
959 transparencyMask);
960
961 free(uncompressedStream);
962
963 return bitmap;
964}
965
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000966SkBitmap getSmaskFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* obj) {
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000967 const PdfObject* sMask = resolveReferenceObject(&pdfContext->fPdfDoc->podofo(),
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000968 obj->podofo()->GetDictionary().GetKey(PdfName("SMask")));
969
970#ifdef PDF_TRACE
971 std::string str;
972 if (sMask) {
973 sMask->ToString(str);
974 printf("/SMask of /Subtype /Image: %s\n", str.c_str());
975 }
976#endif
977
978 if (sMask) {
edisonn@google.comb857a0c2013-06-25 20:45:40 +0000979 SkPdfImageDictionary skxobjmask(&pdfContext->fPdfDoc->podofo(), sMask);
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000980 return getImageFromObject(pdfContext, &skxobjmask, true);
981 }
982
983 // TODO(edisonn): implement GS SMask. Default to empty right now.
984 return pdfContext->fGraphicsState.fSMask;
985}
986
edisonn@google.coma2fab9d2013-06-14 19:22:19 +0000987PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfImageDictionary* skpdfimage) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000988 if (skpdfimage == NULL || !skpdfimage->valid()) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000989 return kIgnoreError_PdfResult;
990 }
991
edisonn@google.comaf3daa02013-06-12 19:07:45 +0000992 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
993 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000994
995 canvas->save();
996 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
997 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
998
999 if (sMask.empty()) {
1000 canvas->drawBitmapRect(image, dst, NULL);
1001 } else {
1002 canvas->saveLayer(&dst, NULL);
1003 canvas->drawBitmapRect(image, dst, NULL);
1004 SkPaint xfer;
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001005 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001006 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
1007 canvas->drawBitmapRect(sMask, dst, &xfer);
1008 canvas->restore();
1009 }
1010
1011 canvas->restore();
1012
1013 return kPartial_PdfResult;
1014}
1015
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001016bool SkMatrixFromDictionary(const PdfMemDocument* pdfDoc,
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001017 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001018 const char* key,
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001019 SkMatrix** matrix) {
1020 const PdfObject* value = resolveReferenceObject(pdfDoc,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001021 dict.GetKey(PdfName(key)));
1022
1023 if (value == NULL || !value->IsArray()) {
1024 return false;
1025 }
1026
1027 if (value->GetArray().GetSize() != 6) {
1028 return false;
1029 }
1030
1031 double array[6];
1032 for (int i = 0; i < 6; i++) {
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001033 const PdfObject* elem = resolveReferenceObject(pdfDoc, &value->GetArray()[i]);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001034 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1035 return false;
1036 }
1037 array[i] = elem->GetReal();
1038 }
1039
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001040 *matrix = new SkMatrix();
1041 **matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001042 return true;
1043}
1044
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001045bool SkMatrixFromDictionary(const PdfMemDocument* pdfDoc,
1046 const PdfDictionary& dict,
1047 const char* key,
1048 const char* abr,
1049 SkMatrix** data) {
1050 if (SkMatrixFromDictionary(pdfDoc, dict, key, data)) return true;
1051 if (abr == NULL || *abr == '\0') return false;
1052 return SkMatrixFromDictionary(pdfDoc, dict, abr, data);
1053
1054}
1055
edisonn@google.com59543d32013-06-18 22:00:40 +00001056bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001057 const PdfDictionary& dict,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001058 const char* key,
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001059 SkRect** rect) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001060 const PdfObject* value = resolveReferenceObject(pdfDoc,
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001061 dict.GetKey(PdfName(key)));
1062
1063 if (value == NULL || !value->IsArray()) {
1064 return false;
1065 }
1066
1067 if (value->GetArray().GetSize() != 4) {
1068 return false;
1069 }
1070
1071 double array[4];
1072 for (int i = 0; i < 4; i++) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001073 const PdfObject* elem = resolveReferenceObject(pdfDoc, &value->GetArray()[i]);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001074 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1075 return false;
1076 }
1077 array[i] = elem->GetReal();
1078 }
1079
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001080 *rect = new SkRect();
1081 **rect = SkRect::MakeLTRB(SkDoubleToScalar(array[0]),
1082 SkDoubleToScalar(array[1]),
1083 SkDoubleToScalar(array[2]),
1084 SkDoubleToScalar(array[3]));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001085 return true;
1086}
1087
edisonn@google.com59543d32013-06-18 22:00:40 +00001088bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
1089 const PdfDictionary& dict,
1090 const char* key,
1091 const char* abr,
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001092 SkRect** data) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001093 if (SkRectFromDictionary(pdfDoc, dict, key, data)) return true;
1094 if (abr == NULL || *abr == '\0') return false;
1095 return SkRectFromDictionary(pdfDoc, dict, abr, data);
1096
1097}
1098
1099
edisonn@google.com68d15c82013-06-17 20:46:27 +00001100SkPdfObject* get(const SkPdfObject* obj, const char* key, const char* abr = "") {
1101 SkPdfObject* ret = NULL;
1102 if (obj == NULL) return NULL;
1103 const SkPdfDictionary* dict = obj->asDictionary();
1104 if (dict == NULL) return NULL;
1105 if (!dict->podofo()->IsDictionary()) return NULL;
1106 ObjectFromDictionary(dict->doc(), dict->podofo()->GetDictionary(), key, abr, &ret);
1107 return ret;
1108}
1109
1110PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
1111 if (!skobj || !skobj->podofo() || !skobj->podofo()->HasStream() || skobj->podofo()->GetStream() == NULL || skobj->podofo()->GetStream()->GetLength() == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001112 return kOK_PdfResult;
1113 }
1114
1115 PdfOp_q(pdfContext, canvas, NULL);
1116 canvas->save();
1117
edisonn@google.com68d15c82013-06-17 20:46:27 +00001118
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001119 if (skobj->Resources()) {
1120 pdfContext->fGraphicsState.fResources = skobj->Resources();
edisonn@google.com68d15c82013-06-17 20:46:27 +00001121 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001122
1123 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
1124
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001125 if (skobj->Matrix()) {
1126 pdfContext->fGraphicsState.fMatrix.preConcat(*skobj->Matrix());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001127 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1128 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1129 // TODO(edisonn) reset matrixTm and matricTlm also?
1130 }
1131
1132 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1133
1134 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1135
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001136 if (skobj->BBox()) {
1137 canvas->clipRect(*skobj->BBox(), SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001138 }
1139
1140 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
1141 // For this PdfContentsTokenizer needs to be extended.
1142
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001143 PdfResult ret = kPartial_PdfResult;
edisonn@google.comff278442013-06-21 21:03:15 +00001144 SkPdfTokenizer tokenizer(skobj);
1145 PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1146 looper.loop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001147
1148 // TODO(edisonn): should we restore the variable stack at the same state?
1149 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1150 canvas->restore();
1151 PdfOp_Q(pdfContext, canvas, NULL);
1152 return ret;
1153}
1154
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001155PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject& obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001156 return kNYI_PdfResult;
1157}
1158
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001159PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
1160 if (!skobj || !skobj->podofo() || !skobj->podofo()->HasStream() || skobj->podofo()->GetStream() == NULL || skobj->podofo()->GetStream()->GetLength() == 0) {
1161 return kOK_PdfResult;
1162 }
1163
1164 PdfOp_q(pdfContext, canvas, NULL);
1165 canvas->save();
1166
1167 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1168 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
1169
1170 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm;
1171 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1172
1173 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1174
1175 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1176
1177 SkRect rm = bBox;
1178 pdfContext->fGraphicsState.fMatrix.mapRect(&rm);
1179
1180 SkTraceRect(rm, "bbox mapped");
1181
1182 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
1183
1184 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
1185 // For this PdfContentsTokenizer needs to be extended.
1186
1187 PdfResult ret = kPartial_PdfResult;
1188 SkPdfTokenizer tokenizer(skobj);
1189 PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1190 looper.loop();
1191
1192 // TODO(edisonn): should we restore the variable stack at the same state?
1193 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1194 canvas->restore();
1195 PdfOp_Q(pdfContext, canvas, NULL);
1196 return ret;
1197}
1198
1199
edisonn@google.com59543d32013-06-18 22:00:40 +00001200// TODO(edisonn): faster, have the property on the SkPdfObject itself?
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001201std::set<const PdfObject*> gInRendering;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001202
1203class CheckRecursiveRendering {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001204 const PdfObject& fObj;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001205public:
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001206 CheckRecursiveRendering(const PdfObject& obj) : fObj(obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001207 gInRendering.insert(&obj);
1208 }
1209
1210 ~CheckRecursiveRendering() {
1211 //SkASSERT(fObj.fInRendering);
1212 gInRendering.erase(&fObj);
1213 }
1214
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001215 static bool IsInRendering(const PdfObject& obj) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001216 return gInRendering.find(&obj) != gInRendering.end();
1217 }
1218};
1219
edisonn@google.com59543d32013-06-18 22:00:40 +00001220PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject& obj) {
1221 if (CheckRecursiveRendering::IsInRendering(*obj.podofo())) {
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001222 // Oops, corrupt PDF!
1223 return kIgnoreError_PdfResult;
1224 }
1225
edisonn@google.com59543d32013-06-18 22:00:40 +00001226 CheckRecursiveRendering checkRecursion(*obj.podofo());
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001227
1228 // TODO(edisonn): check type
edisonn@google.com68d15c82013-06-17 20:46:27 +00001229 SkPdfXObjectDictionary* skobj = NULL;
edisonn@google.com59543d32013-06-18 22:00:40 +00001230 if (!PodofoMapper::map(obj, &skobj)) return kIgnoreError_PdfResult;
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001231
1232 if (!skobj || !skobj->valid()) return kIgnoreError_PdfResult;
1233
1234 PdfResult ret = kIgnoreError_PdfResult;
1235 switch (skobj->getType())
1236 {
edisonn@google.com59543d32013-06-18 22:00:40 +00001237 case kImageDictionary_SkPdfObjectType:
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001238 ret = doXObject_Image(pdfContext, canvas, skobj->asImageDictionary());
edisonn@google.come4d11be2013-06-12 19:53:42 +00001239 break;
edisonn@google.com59543d32013-06-18 22:00:40 +00001240 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com68d15c82013-06-17 20:46:27 +00001241 ret = doXObject_Form(pdfContext, canvas, skobj->asType1FormDictionary());
edisonn@google.come4d11be2013-06-12 19:53:42 +00001242 break;
edisonn@google.comaf3daa02013-06-12 19:07:45 +00001243 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
1244 //return doXObject_PS(skxobj.asPS());
1245 }
1246
1247 delete skobj;
1248 return ret;
1249}
1250
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001251PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1252 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1253 canvas->save();
1254 return kOK_PdfResult;
1255}
1256
1257PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1258 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1259 pdfContext->fStateStack.pop();
1260 canvas->restore();
1261 return kOK_PdfResult;
1262}
1263
1264PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1265 double array[6];
1266 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001267 array[5 - i] = pdfContext->fObjectStack.top()->asNumber()->value();
1268 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001269 }
1270
1271 // a b
1272 // c d
1273 // e f
1274
1275 // 0 1
1276 // 2 3
1277 // 4 5
1278
1279 // sx ky
1280 // kx sy
1281 // tx ty
1282 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1283
1284 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1285
1286#ifdef PDF_TRACE
1287 printf("cm ");
1288 for (int i = 0 ; i < 6 ; i++) {
1289 printf("%f ", array[i]);
1290 }
1291 printf("\n");
1292 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix);
1293#endif
1294
1295 return kOK_PdfResult;
1296}
1297
1298//leading TL Set the text leading, Tl
1299//, to leading, which is a number expressed in unscaled text
1300//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
1301PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001302 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001303
1304 pdfContext->fGraphicsState.fTextLeading = ty;
1305
1306 return kOK_PdfResult;
1307}
1308
1309PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001310 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1311 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001312
1313 double array[6] = {1, 0, 0, 1, tx, ty};
1314 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1315
1316 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1317 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1318
1319 return kPartial_PdfResult;
1320}
1321
1322PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001323 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1324 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001325
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001326 // TODO(edisonn): Create factory methods or constructors so podofo is hidden
edisonn@google.com59543d32013-06-18 22:00:40 +00001327 PdfObject _ty(PdfVariant(-ty));
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001328 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &_ty));
edisonn@google.com59543d32013-06-18 22:00:40 +00001329
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001330 PdfOp_TL(pdfContext, canvas, looper);
1331
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001332 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.comb857a0c2013-06-25 20:45:40 +00001333 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &vtx));
edisonn@google.com59543d32013-06-18 22:00:40 +00001334
1335 PdfObject vty(PdfVariant(-(-ty)));
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001336 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &vty));
edisonn@google.com59543d32013-06-18 22:00:40 +00001337
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001338 return PdfOp_Td(pdfContext, canvas, looper);
1339}
1340
1341PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001342 double f = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1343 double e = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1344 double d = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1345 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1346 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1347 double a = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001348
1349 double array[6];
1350 array[0] = a;
1351 array[1] = b;
1352 array[2] = c;
1353 array[3] = d;
1354 array[4] = e;
1355 array[5] = f;
1356
1357 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1358 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
1359
1360 // TODO(edisonn): Text positioning.
1361 pdfContext->fGraphicsState.fMatrixTm = matrix;
1362 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1363
1364 return kPartial_PdfResult;
1365}
1366
1367//— T* Move to the start of the next line. This operator has the same effect as the code
1368//0 Tl Td
1369//where Tl is the current leading parameter in the text state
1370PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001371 PdfObject zero(PdfVariant(0.0));
1372 PdfObject tl(PdfVariant(-(-pdfContext->fGraphicsState.fTextLeading)));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001373
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001374 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &zero));
1375 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo(), &tl));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001376 return PdfOp_Td(pdfContext, canvas, looper);
1377}
1378
1379PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1380 if (pdfContext->fGraphicsState.fPathClosed) {
1381 pdfContext->fGraphicsState.fPath.reset();
1382 pdfContext->fGraphicsState.fPathClosed = false;
1383 }
1384
edisonn@google.com59543d32013-06-18 22:00:40 +00001385 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1386 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001387
1388 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1389 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1390
1391 return kOK_PdfResult;
1392}
1393
1394PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1395 if (pdfContext->fGraphicsState.fPathClosed) {
1396 pdfContext->fGraphicsState.fPath.reset();
1397 pdfContext->fGraphicsState.fPathClosed = false;
1398 }
1399
edisonn@google.com59543d32013-06-18 22:00:40 +00001400 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1401 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001402
1403 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1404 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1405
1406 return kOK_PdfResult;
1407}
1408
1409PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1410 if (pdfContext->fGraphicsState.fPathClosed) {
1411 pdfContext->fGraphicsState.fPath.reset();
1412 pdfContext->fGraphicsState.fPathClosed = false;
1413 }
1414
edisonn@google.com59543d32013-06-18 22:00:40 +00001415 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1416 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1417 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1418 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1419 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1420 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001421
1422 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1423 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1424 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1425
1426 pdfContext->fGraphicsState.fCurPosX = x3;
1427 pdfContext->fGraphicsState.fCurPosY = y3;
1428
1429 return kOK_PdfResult;
1430}
1431
1432PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1433 if (pdfContext->fGraphicsState.fPathClosed) {
1434 pdfContext->fGraphicsState.fPath.reset();
1435 pdfContext->fGraphicsState.fPathClosed = false;
1436 }
1437
edisonn@google.com59543d32013-06-18 22:00:40 +00001438 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1439 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1440 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1441 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001442 double y1 = pdfContext->fGraphicsState.fCurPosY;
1443 double x1 = pdfContext->fGraphicsState.fCurPosX;
1444
1445 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1446 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1447 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1448
1449 pdfContext->fGraphicsState.fCurPosX = x3;
1450 pdfContext->fGraphicsState.fCurPosY = y3;
1451
1452 return kOK_PdfResult;
1453}
1454
1455PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1456 if (pdfContext->fGraphicsState.fPathClosed) {
1457 pdfContext->fGraphicsState.fPath.reset();
1458 pdfContext->fGraphicsState.fPathClosed = false;
1459 }
1460
edisonn@google.com59543d32013-06-18 22:00:40 +00001461 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1462 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001463 double y2 = pdfContext->fGraphicsState.fCurPosY;
1464 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com59543d32013-06-18 22:00:40 +00001465 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1466 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001467
1468 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1469 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1470 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1471
1472 pdfContext->fGraphicsState.fCurPosX = x3;
1473 pdfContext->fGraphicsState.fCurPosY = y3;
1474
1475 return kOK_PdfResult;
1476}
1477
1478PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1479 if (pdfContext->fGraphicsState.fPathClosed) {
1480 pdfContext->fGraphicsState.fPath.reset();
1481 pdfContext->fGraphicsState.fPathClosed = false;
1482 }
1483
edisonn@google.com59543d32013-06-18 22:00:40 +00001484 double height = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1485 double width = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1486 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1487 double x = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001488
1489 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1490 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1491
1492 pdfContext->fGraphicsState.fCurPosX = x;
1493 pdfContext->fGraphicsState.fCurPosY = y + height;
1494
1495 return kOK_PdfResult;
1496}
1497
1498PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1499 pdfContext->fGraphicsState.fPath.close();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001500 return kOK_PdfResult;
1501}
1502
1503PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
1504 SkPath path = pdfContext->fGraphicsState.fPath;
1505
1506 if (close) {
1507 path.close();
1508 }
1509
1510 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1511
1512 SkPaint paint;
1513
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001514 SkPoint line[2];
1515 if (fill && !stroke && path.isLine(line)) {
1516 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001517
1518 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001519 paint.setStrokeWidth(SkDoubleToScalar(0));
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001520
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001521 canvas->drawPath(path, paint);
1522 } else {
1523 if (fill) {
1524 paint.setStyle(SkPaint::kFill_Style);
1525 if (evenOdd) {
1526 path.setFillType(SkPath::kEvenOdd_FillType);
1527 }
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001528
1529 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1530
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001531 canvas->drawPath(path, paint);
1532 }
1533
1534 if (stroke) {
1535 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00001536
1537 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1538
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001539 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1540 canvas->drawPath(path, paint);
1541 }
1542 }
1543
1544 pdfContext->fGraphicsState.fPath.reset();
1545 // todo zoom ... other stuff ?
1546
1547 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1548#ifndef PDF_DEBUG_NO_CLIPING
1549 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1550#endif
1551 }
1552
1553 //pdfContext->fGraphicsState.fClipPath.reset();
1554 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1555
1556 return kPartial_PdfResult;
1557
1558}
1559
1560PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1561 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1562}
1563
1564PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1565 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1566}
1567
1568PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1569 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1570}
1571
1572PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1573 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1574}
1575
1576PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1577 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1578}
1579
1580PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1581 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1582}
1583
1584PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1585 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1586}
1587
1588PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1589 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1590}
1591
1592PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1593 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1594}
1595
1596PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1597 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1598 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1599#ifndef PDF_DEBUG_NO_CLIPING
1600 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1601#endif
1602 }
1603
1604 //pdfContext->fGraphicsState.fClipPath.reset();
1605 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1606
1607 pdfContext->fGraphicsState.fPathClosed = true;
1608
1609 return kOK_PdfResult;
1610}
1611
1612PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1613 pdfContext->fGraphicsState.fTextBlock = true;
1614 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1615 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1616
1617 return kPartial_PdfResult;
1618}
1619
1620PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1621 if (!pdfContext->fGraphicsState.fTextBlock) {
1622 return kIgnoreError_PdfResult;
1623 }
1624 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1625 return kPartial_PdfResult;
1626}
1627
1628//font size Tf Set the text font, Tf
1629//, to font and the text font size, Tfs, to size. font is the name of a
1630//font resource in the Fontsubdictionary of the current resource dictionary; size is
1631//a number representing a scale factor. There is no initial value for either font or
1632//size; they must be specified explicitly using Tf before any text is shown.
1633PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001634 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1635 std::string fontName = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1636
1637#ifdef PDF_TRACE
1638 printf("font name: %s\n", fontName.c_str());
1639 std::string str;
1640 pdfContext->fGraphicsState.fResources->podofo()->ToString(str);
edisonn@google.comff278442013-06-21 21:03:15 +00001641 printf("Print Tf Resources: %s\n", str.c_str());
1642 pdfContext->fGraphicsState.fResources->Font()->podofo()->ToString(str);
1643 printf("Print Tf Resources/Font: %s\n", str.c_str());
edisonn@google.com59543d32013-06-18 22:00:40 +00001644#endif
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001645
edisonn@google.comff278442013-06-21 21:03:15 +00001646 SkPdfFontDictionary* fd = NULL;
1647 if (pdfContext->fGraphicsState.fResources->Font()) {
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001648 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font()->get(fontName.c_str());
1649 PodofoMapper::map(*objFont, &fd);
edisonn@google.comff278442013-06-21 21:03:15 +00001650
1651#ifdef PDF_TRACE
edisonn@google.comb857a0c2013-06-25 20:45:40 +00001652 objFont->podofo()->ToString(str);
edisonn@google.comff278442013-06-21 21:03:15 +00001653 printf("Print Font loaded: %s\n", str.c_str());
1654 fd->podofo()->ToString(str);
1655 printf("Print Font loaded and resolved and upgraded: %s\n", str.c_str());
1656#endif
1657
1658 }
1659
1660 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(fd);
1661
1662 if (skfont) {
1663 pdfContext->fGraphicsState.fSkFont = skfont;
1664 }
1665
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001666 return kPartial_PdfResult;
1667}
1668
1669PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1670 if (!pdfContext->fGraphicsState.fTextBlock) {
1671 // TODO(edisonn): try to recover and draw it any way?
1672 return kIgnoreError_PdfResult;
1673 }
1674
1675 PdfResult ret = DrawText(pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +00001676 pdfContext->fObjectStack.top(),
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001677 canvas);
edisonn@google.com59543d32013-06-18 22:00:40 +00001678 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001679
1680 return ret;
1681}
1682
1683PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1684 if (!pdfContext->fGraphicsState.fTextBlock) {
1685 // TODO(edisonn): try to recover and draw it any way?
1686 return kIgnoreError_PdfResult;
1687 }
1688
1689 PdfOp_T_star(pdfContext, canvas, looper);
1690 // Do not pop, and push, just transfer the param to Tj
1691 return PdfOp_Tj(pdfContext, canvas, looper);
1692}
1693
1694PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1695 if (!pdfContext->fGraphicsState.fTextBlock) {
1696 // TODO(edisonn): try to recover and draw it any way?
1697 return kIgnoreError_PdfResult;
1698 }
1699
edisonn@google.com59543d32013-06-18 22:00:40 +00001700 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1701 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1702 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001703
edisonn@google.com59543d32013-06-18 22:00:40 +00001704 pdfContext->fObjectStack.push(aw);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001705 PdfOp_Tw(pdfContext, canvas, looper);
1706
edisonn@google.com59543d32013-06-18 22:00:40 +00001707 pdfContext->fObjectStack.push(ac);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001708 PdfOp_Tc(pdfContext, canvas, looper);
1709
edisonn@google.com59543d32013-06-18 22:00:40 +00001710 pdfContext->fObjectStack.push(str);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001711 PdfOp_quote(pdfContext, canvas, looper);
1712
1713 return kPartial_PdfResult;
1714}
1715
1716PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1717 if (!pdfContext->fGraphicsState.fTextBlock) {
1718 // TODO(edisonn): try to recover and draw it any way?
1719 return kIgnoreError_PdfResult;
1720 }
1721
edisonn@google.com59543d32013-06-18 22:00:40 +00001722 SkPdfArray* array = pdfContext->fObjectStack.top()->asArray();
1723 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001724
edisonn@google.com59543d32013-06-18 22:00:40 +00001725 for( int i=0; i<static_cast<int>(array->size()); i++ )
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001726 {
edisonn@google.comff278442013-06-21 21:03:15 +00001727 if( (*array)[i]->asString()) {
1728 SkPdfObject* obj = (*array)[i];
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001729 DrawText(pdfContext,
edisonn@google.comff278442013-06-21 21:03:15 +00001730 obj,
1731 canvas);
1732 } else if ((*array)[i]->asInteger() || (*array)[i]->asNumber()) {
1733 double dx = (*array)[i]->asNumber()->value();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001734 SkMatrix matrix;
1735 matrix.setAll(SkDoubleToScalar(1),
1736 SkDoubleToScalar(0),
1737 // TODO(edisonn): use writing mode, vertical/horizontal.
1738 SkDoubleToScalar(-dx), // amount is substracted!!!
1739 SkDoubleToScalar(0),
1740 SkDoubleToScalar(1),
1741 SkDoubleToScalar(0),
1742 SkDoubleToScalar(0),
1743 SkDoubleToScalar(0),
1744 SkDoubleToScalar(1));
1745
1746 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1747 }
1748 }
1749 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1750}
1751
1752PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001753 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001754 return kOK_PdfResult;
1755}
1756
1757PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1758 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1759}
1760
1761PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1762 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1763}
1764
1765PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1766 double c[4];
1767 pdf_int64 v[4];
1768
1769 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1770
1771 bool doubles = true;
1772 if (colorOperator->fColorSpace == "Indexed") {
1773 doubles = false;
1774 }
1775
1776#ifdef PDF_TRACE
1777 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.c_str(), n);
1778#endif
1779
1780 for (int i = n - 1; i >= 0 ; i--) {
1781 if (doubles) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001782 c[i] = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001783 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00001784 v[i] = pdfContext->fObjectStack.top()->asInteger()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001785 }
1786 }
1787
1788 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
1789 if (colorOperator->fColorSpace == "DeviceRGB") {
1790 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
1791 }
1792 return kPartial_PdfResult;
1793}
1794
1795PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1796 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1797}
1798
1799PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1800 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1801}
1802
1803PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1804 PdfString name;
1805
edisonn@google.com59543d32013-06-18 22:00:40 +00001806 if (pdfContext->fObjectStack.top()->asName()) {
1807 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001808 }
1809
1810 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1811 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1812
1813 return kPartial_PdfResult;
1814}
1815
1816PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1817 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1818}
1819
1820PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1821 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1822}
1823
1824PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001825 double gray = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001826 return kNYI_PdfResult;
1827}
1828
1829PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1830 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1831}
1832
1833PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1834 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1835}
1836
1837PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001838 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1839 double g = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1840 double r = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001841
1842 colorOperator->fColorSpace = "DeviceRGB";
1843 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
1844 return kOK_PdfResult;
1845}
1846
1847PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1848 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1849}
1850
1851PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1852 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1853}
1854
1855PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1856 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com59543d32013-06-18 22:00:40 +00001857 double k = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1858 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1859 double m = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1860 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001861
1862 colorOperator->fColorSpace = "DeviceCMYK";
1863 // TODO(edisonn): Set color.
1864 return kNYI_PdfResult;
1865}
1866
1867PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1868 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1869}
1870
1871PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1872 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1873}
1874
1875PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1876 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1877 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1878
1879 return kOK_PdfResult;
1880}
1881
1882PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1883 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1884
1885#ifdef PDF_TRACE
1886 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
1887 printf("CLIP IS RECT\n");
1888 }
1889#endif
1890
1891 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
1892 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1893 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1894
1895 return kPartial_PdfResult;
1896}
1897
1898PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1899 *looper = new PdfCompatibilitySectionLooper();
1900 return kOK_PdfResult;
1901}
1902
1903PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1904#ifdef ASSERT_BAD_PDF_OPS
1905 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1906 // have the assert when testing good pdfs.
1907#endif
1908 return kIgnoreError_PdfResult;
1909}
1910
1911PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1912 *looper = new PdfInlineImageLooper();
1913 return kOK_PdfResult;
1914}
1915
1916PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1917#ifdef ASSERT_BAD_PDF_OPS
1918 SkASSERT(false); // must be processed in inline image looper, but let's
1919 // have the assert when testing good pdfs.
1920#endif
1921 return kIgnoreError_PdfResult;
1922}
1923
1924PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1925#ifdef ASSERT_BAD_PDF_OPS
1926 SkASSERT(false); // must be processed in inline image looper, but let's
1927 // have the assert when testing good pdfs.
1928#endif
1929 return kIgnoreError_PdfResult;
1930}
1931
1932//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1933PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001934 double lineWidth = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001935 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1936
1937 return kOK_PdfResult;
1938}
1939
1940//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
1941PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001942 pdfContext->fObjectStack.pop();
1943 //double lineCap = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001944
1945 return kNYI_PdfResult;
1946}
1947
1948//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
1949PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001950 pdfContext->fObjectStack.pop();
1951 //double lineJoin = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001952
1953 return kNYI_PdfResult;
1954}
1955
1956//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
1957PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001958 pdfContext->fObjectStack.pop();
1959 //double miterLimit = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001960
1961 return kNYI_PdfResult;
1962}
1963
1964//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1965//page 155).
1966PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001967 pdfContext->fObjectStack.pop();
1968 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001969
1970 return kNYI_PdfResult;
1971}
1972
1973//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
1974PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001975 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001976
1977 return kNYI_PdfResult;
1978}
1979
1980//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1981//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1982//fies the output device’s default flatness tolerance.
1983PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001984 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001985
1986 return kNYI_PdfResult;
1987}
1988
1989//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
1990//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
1991PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00001992 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001993
edisonn@google.coma2fab9d2013-06-14 19:22:19 +00001994#ifdef PDF_TRACE
1995 std::string str;
1996#endif
1997
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001998 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com59543d32013-06-18 22:00:40 +00001999 const SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002000
2001 if (extGStateDictionary == NULL) {
2002#ifdef PDF_TRACE
2003 printf("ExtGState is NULL!\n");
2004#endif
2005 return kIgnoreError_PdfResult;
2006 }
2007
edisonn@google.comb857a0c2013-06-25 20:45:40 +00002008 SkPdfObject* value = extGStateDictionary->get(name.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002009
2010#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002011// value->ToString(str);
2012// printf("gs object value: %s\n", str.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002013#endif
2014
edisonn@google.com59543d32013-06-18 22:00:40 +00002015 SkPdfGraphicsStateDictionary* gs = NULL;
edisonn@google.comb857a0c2013-06-25 20:45:40 +00002016 PodofoMapper::map(*value, &gs);
edisonn@google.com68d15c82013-06-17 20:46:27 +00002017
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002018 // TODO(edisonn): now load all those properties in graphic state.
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00002019 if (gs == NULL) {
2020 return kIgnoreError_PdfResult;
2021 }
2022
2023 if (gs->has_CA()) {
2024 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA();
2025 }
2026
2027 if (gs->has_ca()) {
2028 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca();
2029 }
2030
2031 if (gs->has_LW()) {
2032 pdfContext->fGraphicsState.fLineWidth = gs->LW();
2033 }
2034
2035
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002036
2037 return kNYI_PdfResult;
2038}
2039
2040//charSpace Tc Set the character spacing, Tc
2041//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2042//Initial value: 0.
2043PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002044 double charSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002045 pdfContext->fGraphicsState.fCharSpace = charSpace;
2046
2047 return kOK_PdfResult;
2048}
2049
2050//wordSpace Tw Set the word spacing, T
2051//w
2052//, to wordSpace, which is a number expressed in unscaled
2053//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2054//value: 0.
2055PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002056 double wordSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002057 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2058
2059 return kOK_PdfResult;
2060}
2061
2062//scale Tz Set the horizontal scaling, Th
2063//, to (scale ˜ 100). scale is a number specifying the
2064//percentage of the normal width. Initial value: 100 (normal width).
2065PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002066 double scale = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002067
2068 return kNYI_PdfResult;
2069}
2070
2071//render Tr Set the text rendering mode, T
2072//mode, to render, which is an integer. Initial value: 0.
2073PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002074 double render = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002075
2076 return kNYI_PdfResult;
2077}
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002078//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2079//units. Initial value: 0.
2080PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002081 double rise = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002082
2083 return kNYI_PdfResult;
2084}
2085
2086//wx wy d0
2087PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002088 pdfContext->fObjectStack.pop();
2089 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002090
2091 return kNYI_PdfResult;
2092}
2093
2094//wx wy llx lly urx ury d1
2095PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002096 pdfContext->fObjectStack.pop();
2097 pdfContext->fObjectStack.pop();
2098 pdfContext->fObjectStack.pop();
2099 pdfContext->fObjectStack.pop();
2100 pdfContext->fObjectStack.pop();
2101 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002102
2103 return kNYI_PdfResult;
2104}
2105
2106//name sh
2107PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002108 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002109
2110 return kNYI_PdfResult;
2111}
2112
2113//name Do
2114PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002115 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002116
edisonn@google.com59543d32013-06-18 22:00:40 +00002117 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002118
edisonn@google.com59543d32013-06-18 22:00:40 +00002119 if (xObject == NULL) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002120#ifdef PDF_TRACE
2121 printf("XObject is NULL!\n");
2122#endif
2123 return kIgnoreError_PdfResult;
2124 }
2125
edisonn@google.comb857a0c2013-06-25 20:45:40 +00002126 SkPdfObject* value = xObject->get(name.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002127
2128#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002129// value->ToString(str);
2130// printf("Do object value: %s\n", str.c_str());
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002131#endif
2132
edisonn@google.comb857a0c2013-06-25 20:45:40 +00002133 return doXObject(pdfContext, canvas, *value);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002134}
2135
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002136//tag MP Designate a marked-content point. tag is a name object indicating the role or
2137//significance of the point.
2138PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002139 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002140
2141 return kNYI_PdfResult;
2142}
2143
2144//tag properties DP Designate a marked-content point with an associated property list. tag is a
2145//name object indicating the role or significance of the point; properties is
2146//either an inline dictionary containing the property list or a name object
2147//associated with it in the Properties subdictionary of the current resource
2148//dictionary (see Section 9.5.1, “Property Lists”).
2149PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002150 pdfContext->fObjectStack.pop();
2151 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002152
2153 return kNYI_PdfResult;
2154}
2155
2156//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2157//tag is a name object indicating the role or significance of the sequence.
2158PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002159 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002160
2161 return kNYI_PdfResult;
2162}
2163
2164//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2165//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2166//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”).
2167PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002168 pdfContext->fObjectStack.pop();
2169 pdfContext->fObjectStack.pop();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002170
2171 return kNYI_PdfResult;
2172}
2173
2174//— EMC End a marked-content sequence begun by a BMC or BDC operator.
2175PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2176 return kNYI_PdfResult;
2177}
2178
2179void initPdfOperatorRenderes() {
2180 static bool gInitialized = false;
2181 if (gInitialized) {
2182 return;
2183 }
2184
2185 gPdfOps["q"] = PdfOp_q;
2186 gPdfOps["Q"] = PdfOp_Q;
2187 gPdfOps["cm"] = PdfOp_cm;
2188
2189 gPdfOps["TD"] = PdfOp_TD;
2190 gPdfOps["Td"] = PdfOp_Td;
2191 gPdfOps["Tm"] = PdfOp_Tm;
2192 gPdfOps["T*"] = PdfOp_T_star;
2193
2194 gPdfOps["m"] = PdfOp_m;
2195 gPdfOps["l"] = PdfOp_l;
2196 gPdfOps["c"] = PdfOp_c;
2197 gPdfOps["v"] = PdfOp_v;
2198 gPdfOps["y"] = PdfOp_y;
2199 gPdfOps["h"] = PdfOp_h;
2200 gPdfOps["re"] = PdfOp_re;
2201
2202 gPdfOps["S"] = PdfOp_S;
2203 gPdfOps["s"] = PdfOp_s;
2204 gPdfOps["f"] = PdfOp_f;
2205 gPdfOps["F"] = PdfOp_F;
2206 gPdfOps["f*"] = PdfOp_f_star;
2207 gPdfOps["B"] = PdfOp_B;
2208 gPdfOps["B*"] = PdfOp_B_star;
2209 gPdfOps["b"] = PdfOp_b;
2210 gPdfOps["b*"] = PdfOp_b_star;
2211 gPdfOps["n"] = PdfOp_n;
2212
2213 gPdfOps["BT"] = PdfOp_BT;
2214 gPdfOps["ET"] = PdfOp_ET;
2215
2216 gPdfOps["Tj"] = PdfOp_Tj;
2217 gPdfOps["'"] = PdfOp_quote;
2218 gPdfOps["\""] = PdfOp_doublequote;
2219 gPdfOps["TJ"] = PdfOp_TJ;
2220
2221 gPdfOps["CS"] = PdfOp_CS;
2222 gPdfOps["cs"] = PdfOp_cs;
2223 gPdfOps["SC"] = PdfOp_SC;
2224 gPdfOps["SCN"] = PdfOp_SCN;
2225 gPdfOps["sc"] = PdfOp_sc;
2226 gPdfOps["scn"] = PdfOp_scn;
2227 gPdfOps["G"] = PdfOp_G;
2228 gPdfOps["g"] = PdfOp_g;
2229 gPdfOps["RG"] = PdfOp_RG;
2230 gPdfOps["rg"] = PdfOp_rg;
2231 gPdfOps["K"] = PdfOp_K;
2232 gPdfOps["k"] = PdfOp_k;
2233
2234 gPdfOps["W"] = PdfOp_W;
2235 gPdfOps["W*"] = PdfOp_W_star;
2236
2237 gPdfOps["BX"] = PdfOp_BX;
2238 gPdfOps["EX"] = PdfOp_EX;
2239
2240 gPdfOps["BI"] = PdfOp_BI;
2241 gPdfOps["ID"] = PdfOp_ID;
2242 gPdfOps["EI"] = PdfOp_EI;
2243
2244 gPdfOps["w"] = PdfOp_w;
2245 gPdfOps["J"] = PdfOp_J;
2246 gPdfOps["j"] = PdfOp_j;
2247 gPdfOps["M"] = PdfOp_M;
2248 gPdfOps["d"] = PdfOp_d;
2249 gPdfOps["ri"] = PdfOp_ri;
2250 gPdfOps["i"] = PdfOp_i;
2251 gPdfOps["gs"] = PdfOp_gs;
2252
2253 gPdfOps["Tc"] = PdfOp_Tc;
2254 gPdfOps["Tw"] = PdfOp_Tw;
2255 gPdfOps["Tz"] = PdfOp_Tz;
2256 gPdfOps["TL"] = PdfOp_TL;
2257 gPdfOps["Tf"] = PdfOp_Tf;
2258 gPdfOps["Tr"] = PdfOp_Tr;
2259 gPdfOps["Ts"] = PdfOp_Ts;
2260
2261 gPdfOps["d0"] = PdfOp_d0;
2262 gPdfOps["d1"] = PdfOp_d1;
2263
2264 gPdfOps["sh"] = PdfOp_sh;
2265
2266 gPdfOps["Do"] = PdfOp_Do;
2267
2268 gPdfOps["MP"] = PdfOp_MP;
2269 gPdfOps["DP"] = PdfOp_DP;
2270 gPdfOps["BMC"] = PdfOp_BMC;
2271 gPdfOps["BDC"] = PdfOp_BDC;
2272 gPdfOps["EMC"] = PdfOp_EMC;
2273
2274 gInitialized = true;
2275}
2276
2277void reportPdfRenderStats() {
2278 std::map<std::string, int>::iterator iter;
2279
2280 for (int i = 0 ; i < kCount_PdfResult; i++) {
2281 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++iter) {
2282 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_str(), iter->second);
2283 }
2284 }
2285}
2286
2287PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002288 if (token.fType == kKeyword_TokenType)
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002289 {
2290 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com59543d32013-06-18 22:00:40 +00002291 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[token.fKeyword];
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002292 if (pdfOperatorRenderer) {
2293 // caller, main work is done by pdfOperatorRenderer(...)
2294 PdfTokenLooper* childLooper = NULL;
edisonn@google.com59543d32013-06-18 22:00:40 +00002295 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)][token.fKeyword]++;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002296
2297 if (childLooper) {
2298 childLooper->setUp(this);
2299 childLooper->loop();
2300 delete childLooper;
2301 }
2302 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002303 gRenderStats[kUnsupported_PdfResult][token.fKeyword]++;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002304 }
2305 }
edisonn@google.com59543d32013-06-18 22:00:40 +00002306 else if (token.fType == kObject_TokenType)
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002307 {
edisonn@google.com59543d32013-06-18 22:00:40 +00002308 fPdfContext->fObjectStack.push( token.fObject );
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002309 }
edisonn@google.com59543d32013-06-18 22:00:40 +00002310 else if ( token.fType == kImageData_TokenType) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002311 // TODO(edisonn): implement inline image.
2312 }
2313 else {
2314 return kIgnoreError_PdfResult;
2315 }
2316 return kOK_PdfResult;
2317}
2318
2319void PdfMainLooper::loop() {
2320 PdfToken token;
2321 while (readToken(fTokenizer, &token)) {
2322 consumeToken(token);
2323 }
2324}
2325
2326PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
2327 //pdfContext.fInlineImage.fKeyValuePairs[key] = value;
2328 return kNYI_PdfResult;
2329}
2330
2331void PdfInlineImageLooper::loop() {
2332 PdfToken token;
2333 while (readToken(fTokenizer, &token)) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002334 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002335 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2336 looper->setUp(this);
2337 looper->loop();
2338 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002339 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002340 done();
2341 return;
2342 }
2343
2344 consumeToken(token);
2345 }
2346 }
2347 // TODO(edisonn): report error/warning, EOF without EI.
2348}
2349
2350PdfResult PdfInlineImageLooper::done() {
2351
2352 // TODO(edisonn): long to short names
2353 // TODO(edisonn): set properties in a map
2354 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
2355 // the stream.
2356
2357 SkBitmap bitmap;
2358 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
2359
2360 // TODO(edisonn): matrix use.
2361 // Draw dummy red square, to show the prezence of the inline image.
2362 fCanvas->drawBitmap(bitmap,
2363 SkDoubleToScalar(0),
2364 SkDoubleToScalar(0),
2365 NULL);
2366 return kNYI_PdfResult;
2367}
2368
2369PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2370 return fParent->consumeToken(token);
2371}
2372
2373void PdfCompatibilitySectionLooper::loop() {
2374 // TODO(edisonn): save stacks position, or create a new stack?
2375 // TODO(edisonn): what happens if we pop out more variables then when we started?
2376 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2377 // pop-ing too much will not affect outside the section.
2378 PdfToken token;
2379 while (readToken(fTokenizer, &token)) {
edisonn@google.com59543d32013-06-18 22:00:40 +00002380 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002381 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2382 looper->setUp(this);
2383 looper->loop();
2384 delete looper;
2385 } else {
edisonn@google.com59543d32013-06-18 22:00:40 +00002386 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002387 fParent->consumeToken(token);
2388 }
2389 }
2390 // TODO(edisonn): restore stack.
2391}
2392
2393// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2394// TODO(edisonn): Add API for Forms viewing and editing
2395// e.g. SkBitmap getPage(int page);
2396// int formsCount();
2397// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2398// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2399// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2400// resources needed, so the preview is fast.
2401// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2402// references automatically.
2403class SkPdfViewer : public SkRefCnt {
2404public:
2405
2406 bool load(const SkString inputFileName, SkPicture* out) {
2407
2408 initPdfOperatorRenderes();
2409
2410 try
2411 {
2412 std::cout << "Init: " << inputFileName.c_str() << std::endl;
2413
edisonn@google.com59543d32013-06-18 22:00:40 +00002414 SkPdfDoc doc(inputFileName.c_str());
2415 if( !doc.pages() )
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002416 {
2417 std::cout << "ERROR: Empty Document" << inputFileName.c_str() << std::endl;
2418 return false;
2419 } else {
2420
edisonn@google.com59543d32013-06-18 22:00:40 +00002421 for (int pn = 0; pn < doc.pages(); ++pn) {
2422 SkPdfPageObjectDictionary* page = doc.page(pn);
2423
2424 // TODO(edisonn): implement inheritance properties as per PDF spec
2425 //SkRect rect = page->MediaBox();
2426 SkRect rect = doc.MediaBox(pn);
2427
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002428#ifdef PDF_TRACE
edisonn@google.com59543d32013-06-18 22:00:40 +00002429 printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rect.width()), SkScalarToDouble(rect.height()));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002430#endif
2431
2432 // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to use?
2433
2434 SkBitmap bitmap;
2435#ifdef PDF_DEBUG_3X
edisonn@google.comb857a0c2013-06-25 20:45:40 +00002436 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002437#else
edisonn@google.com59543d32013-06-18 22:00:40 +00002438 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002439#endif
2440 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
2441 SkCanvas canvas(device);
2442
edisonn@google.comff278442013-06-21 21:03:15 +00002443 SkPdfTokenizer* tokenizer = doc.tokenizerOfPage(pn);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002444
edisonn@google.comb857a0c2013-06-25 20:45:40 +00002445 PdfContext pdfContext(&doc);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002446 pdfContext.fOriginalMatrix = SkMatrix::I();
edisonn@google.com59543d32013-06-18 22:00:40 +00002447 pdfContext.fGraphicsState.fResources = NULL;
2448 PodofoMapper::map(*page->Resources(), &pdfContext.fGraphicsState.fResources);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002449
2450 gPdfContext = &pdfContext;
2451 gDumpBitmap = &bitmap;
2452 gDumpCanvas = &canvas;
2453
2454
2455 // TODO(edisonn): get matrix stuff right.
2456 // TODO(edisonn): add DPI/scale/zoom.
2457 SkScalar z = SkIntToScalar(0);
edisonn@google.com59543d32013-06-18 22:00:40 +00002458 SkScalar w = rect.width();
2459 SkScalar h = rect.height();
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002460
2461 SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(w, z), SkPoint::Make(w, h), SkPoint::Make(z, h)};
2462// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2463
2464 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2465 // TODO(edisonn): add flagg for no clipping.
2466 // Use larger image to make sure we do not draw anything outside of page
2467 // could be used in tests.
2468
2469#ifdef PDF_DEBUG_3X
2470 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)};
2471#else
2472 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2473#endif
2474 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2475 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2476
2477 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2478 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2479
2480 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2481 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2482
2483 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2484 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2485
2486
2487 pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
2488 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
2489 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
2490
2491 canvas.setMatrix(pdfContext.fOriginalMatrix);
2492
2493#ifndef PDF_DEBUG_NO_PAGE_CLIPING
2494 canvas.clipRect(SkRect::MakeXYWH(z, z, w, h), SkRegion::kIntersect_Op, true);
2495#endif
2496
edisonn@google.com59543d32013-06-18 22:00:40 +00002497 PdfMainLooper looper(NULL, tokenizer, &pdfContext, &canvas);
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002498 looper.loop();
2499
edisonn@google.comff278442013-06-21 21:03:15 +00002500 delete tokenizer;
2501
edisonn@google.com01cd4d52013-06-10 20:44:45 +00002502 canvas.flush();
2503
2504 SkString out;
2505 out.appendf("%s-%i.png", inputFileName.c_str(), pn);
2506 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
2507 }
2508 return true;
2509 }
2510 }
2511 catch( PdfError & e )
2512 {
2513 std::cout << "ERROR: PDF can't be parsed!" << inputFileName.c_str() << std::endl;
2514 return false;
2515 }
2516
2517 return true;
2518 }
2519 bool write(void*) const { return false; }
2520};
2521
2522
2523
2524/**
2525 * Given list of directories and files to use as input, expects to find .pdf
2526 * files and it will convert them to .png files writing them in the same directory
2527 * one file for each page.
2528 *
2529 * Returns zero exit code if all .pdf files were converted successfully,
2530 * otherwise returns error code 1.
2531 */
2532
2533static const char PDF_FILE_EXTENSION[] = "pdf";
2534static const char PNG_FILE_EXTENSION[] = "png";
2535
2536// TODO(edisonn): add ability to write to a new directory.
2537static void usage(const char* argv0) {
2538 SkDebugf("PDF to PNG rendering tool\n");
2539 SkDebugf("\n"
2540"Usage: \n"
2541" %s <input>... -w <outputDir> \n"
2542, argv0);
2543 SkDebugf("\n\n");
2544 SkDebugf(
2545" input: A list of directories and files to use as input. Files are\n"
2546" expected to have the .skp extension.\n\n");
2547 SkDebugf(
2548" outputDir: directory to write the rendered pdfs.\n\n");
2549 SkDebugf("\n");
2550}
2551
2552/** Replaces the extension of a file.
2553 * @param path File name whose extension will be changed.
2554 * @param old_extension The old extension.
2555 * @param new_extension The new extension.
2556 * @returns false if the file did not has the expected extension.
2557 * if false is returned, contents of path are undefined.
2558 */
2559static bool replace_filename_extension(SkString* path,
2560 const char old_extension[],
2561 const char new_extension[]) {
2562 if (path->endsWith(old_extension)) {
2563 path->remove(path->size() - strlen(old_extension),
2564 strlen(old_extension));
2565 if (!path->endsWith(".")) {
2566 return false;
2567 }
2568 path->append(new_extension);
2569 return true;
2570 }
2571 return false;
2572}
2573
2574/** Builds the output filename. path = dir/name, and it replaces expected
2575 * .skp extension with .pdf extention.
2576 * @param path Output filename.
2577 * @param name The name of the file.
2578 * @returns false if the file did not has the expected extension.
2579 * if false is returned, contents of path are undefined.
2580 */
2581static bool make_output_filepath(SkString* path, const SkString& dir,
2582 const SkString& name) {
2583 sk_tools::make_filepath(path, dir, name);
2584 return replace_filename_extension(path,
2585 PDF_FILE_EXTENSION,
2586 PNG_FILE_EXTENSION);
2587}
2588
2589/** Write the output of pdf renderer to a file.
2590 * @param outputDir Output dir.
2591 * @param inputFilename The skp file that was read.
2592 * @param renderer The object responsible to write the pdf file.
2593 */
2594static bool write_output(const SkString& outputDir,
2595 const SkString& inputFilename,
2596 const SkPdfViewer& renderer) {
2597 if (outputDir.isEmpty()) {
2598 SkDynamicMemoryWStream stream;
2599 renderer.write(&stream);
2600 return true;
2601 }
2602
2603 SkString outputPath;
2604 if (!make_output_filepath(&outputPath, outputDir, inputFilename)) {
2605 return false;
2606 }
2607
2608 SkFILEWStream stream(outputPath.c_str());
2609 if (!stream.isValid()) {
2610 SkDebugf("Could not write to file %s\n", outputPath.c_str());
2611 return false;
2612 }
2613 renderer.write(&stream);
2614
2615 return true;
2616}
2617
2618/** Reads an skp file, renders it to pdf and writes the output to a pdf file
2619 * @param inputPath The skp file to be read.
2620 * @param outputDir Output dir.
2621 * @param renderer The object responsible to render the skp object into pdf.
2622 */
2623static bool parse_pdf(const SkString& inputPath, const SkString& outputDir,
2624 SkPdfViewer& renderer) {
2625 SkString inputFilename;
2626 sk_tools::get_basename(&inputFilename, inputPath);
2627
2628 SkFILEStream inputStream;
2629 inputStream.setPath(inputPath.c_str());
2630 if (!inputStream.isValid()) {
2631 SkDebugf("Could not open file %s\n", inputPath.c_str());
2632 return false;
2633 }
2634
2635 bool success = false;
2636
2637 success = renderer.load(inputPath, NULL);
2638
2639
2640// success = write_output(outputDir, inputFilename, renderer);
2641
2642 //renderer.end();
2643 return success;
2644}
2645
2646/** For each file in the directory or for the file passed in input, call
2647 * parse_pdf.
2648 * @param input A directory or an pdf file.
2649 * @param outputDir Output dir.
2650 * @param renderer The object responsible to render the skp object into pdf.
2651 */
2652static int process_input(const SkString& input, const SkString& outputDir,
2653 SkPdfViewer& renderer) {
2654 int failures = 0;
2655 if (sk_isdir(input.c_str())) {
2656 SkOSFile::Iter iter(input.c_str(), PDF_FILE_EXTENSION);
2657 SkString inputFilename;
2658 while (iter.next(&inputFilename)) {
2659 SkString inputPath;
2660 sk_tools::make_filepath(&inputPath, input, inputFilename);
2661 if (!parse_pdf(inputPath, outputDir, renderer)) {
2662 ++failures;
2663 }
2664 }
2665 } else {
2666 SkString inputPath(input);
2667 if (!parse_pdf(inputPath, outputDir, renderer)) {
2668 ++failures;
2669 }
2670 }
2671 return failures;
2672}
2673
2674static void parse_commandline(int argc, char* const argv[],
2675 SkTArray<SkString>* inputs,
2676 SkString* outputDir) {
2677 const char* argv0 = argv[0];
2678 char* const* stop = argv + argc;
2679
2680 for (++argv; argv < stop; ++argv) {
2681 if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
2682 usage(argv0);
2683 exit(-1);
2684 } else if (0 == strcmp(*argv, "-w")) {
2685 ++argv;
2686 if (argv >= stop) {
2687 SkDebugf("Missing outputDir for -w\n");
2688 usage(argv0);
2689 exit(-1);
2690 }
2691 *outputDir = SkString(*argv);
2692 } else {
2693 inputs->push_back(SkString(*argv));
2694 }
2695 }
2696
2697 if (inputs->count() < 1) {
2698 usage(argv0);
2699 exit(-1);
2700 }
2701}
2702
2703int tool_main(int argc, char** argv);
2704int tool_main(int argc, char** argv) {
2705 SkAutoGraphics ag;
2706 SkTArray<SkString> inputs;
2707
2708 SkAutoTUnref<SkPdfViewer>
2709 renderer(SkNEW(SkPdfViewer));
2710 SkASSERT(renderer.get());
2711
2712 SkString outputDir;
2713 parse_commandline(argc, argv, &inputs, &outputDir);
2714
2715 int failures = 0;
2716 for (int i = 0; i < inputs.count(); i ++) {
2717 failures += process_input(inputs[i], outputDir, *renderer);
2718 }
2719
2720 reportPdfRenderStats();
2721
2722 if (failures != 0) {
2723 SkDebugf("Failed to render %i PDFs.\n", failures);
2724 return 1;
2725 }
2726
2727 return 0;
2728}
2729
2730#if !defined SK_BUILD_FOR_IOS
2731int main(int argc, char * const argv[]) {
2732 return tool_main(argc, (char**) argv);
2733}
2734#endif