blob: f52d9a020fd4961b448b8be8a38dc3c779af6ce7 [file] [log] [blame]
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkCanvas.h"
9#include "SkDevice.h"
10#include "SkForceLinking.h"
11#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"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000019
20#include <iostream>
21#include <cstdio>
22#include <stack>
edisonn@google.com571c70b2013-07-10 17:09:50 +000023#include <set>
edisonn@google.com131d4ee2013-06-26 17:48:12 +000024
25__SK_FORCE_IMAGE_DECODER_LINKING;
26
27// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
28// keep for each object pos in file
29// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
30
31// TODO(edisonn): security - validate all the user input, all pdf!
32
edisonn@google.com6e49c342013-06-27 20:03:43 +000033// TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor changes in text positioning and font
34// this way, we look more at other features and layout in diffs
edisonn@google.com131d4ee2013-06-26 17:48:12 +000035
edisonn@google.com3aac1f92013-07-02 22:42:53 +000036// TODO(edisonn): move trace dump in the get functions, and mapper ones too so it ghappens automatically
37/*
38#ifdef PDF_TRACE
39 std::string str;
edisonn@google.com571c70b2013-07-10 17:09:50 +000040 pdfContext->fGraphicsState.fResources->native()->ToString(str);
edisonn@google.com3aac1f92013-07-02 22:42:53 +000041 printf("Print Tf Resources: %s\n", str.c_str());
42#endif
43 */
44
edisonn@google.com131d4ee2013-06-26 17:48:12 +000045#include "SkPdfHeaders_autogen.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +000046#include "SkPdfMapper_autogen.h"
edisonn@google.com222382b2013-07-10 22:33:10 +000047#include "SkPdfRenderer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000048
49#include "SkPdfBasics.h"
50#include "SkPdfUtils.h"
51
52#include "SkPdfFont.h"
53
edisonn@google.com131d4ee2013-06-26 17:48:12 +000054/*
55 * TODO(edisonn):
56 * - all font types and all ppdf font features
57 * - word spacing
58 * - load font for baidu.pdf
59 * - load font for youtube.pdf
60 * - parser for pdf from the definition already available in pdfspec_autogen.py
61 * - all docs from ~/work
edisonn@google.com571c70b2013-07-10 17:09:50 +000062 * - encapsulate native in the pdf api so the skpdf does not know anything about native ... in progress
edisonn@google.com131d4ee2013-06-26 17:48:12 +000063 * - load gs/ especially smask and already known prop (skp) ... in progress
64 * - wrapper on classes for customizations? e.g.
65 * SkPdfPageObjectVanila - has only the basic loaders/getters
66 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
67 * need to find a nice object model for all this with constructors and factories
68 * - deal with inheritable automatically ?
69 * - deal with specific type in spec directly, add all dictionary types to known types
70*/
71
72using namespace std;
edisonn@google.com131d4ee2013-06-26 17:48:12 +000073
edisonn@google.com222382b2013-07-10 22:33:10 +000074
75
76// TODO(edisonn): Document PdfTokenLooper and subclasses.
77class PdfTokenLooper {
78protected:
79 PdfTokenLooper* fParent;
80 SkPdfNativeTokenizer* fTokenizer;
81 PdfContext* fPdfContext;
82 SkCanvas* fCanvas;
83
84public:
85 PdfTokenLooper(PdfTokenLooper* parent,
86 SkPdfNativeTokenizer* tokenizer,
87 PdfContext* pdfContext,
88 SkCanvas* canvas)
89 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
90
91 virtual ~PdfTokenLooper() {}
92
93 virtual PdfResult consumeToken(PdfToken& token) = 0;
94 virtual void loop() = 0;
95
96 void setUp(PdfTokenLooper* parent) {
97 fParent = parent;
98 fTokenizer = parent->fTokenizer;
99 fPdfContext = parent->fPdfContext;
100 fCanvas = parent->fCanvas;
101 }
102};
103
104class PdfMainLooper : public PdfTokenLooper {
105public:
106 PdfMainLooper(PdfTokenLooper* parent,
107 SkPdfNativeTokenizer* tokenizer,
108 PdfContext* pdfContext,
109 SkCanvas* canvas)
110 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
111
112 virtual PdfResult consumeToken(PdfToken& token);
113 virtual void loop();
114};
115
116class PdfInlineImageLooper : public PdfTokenLooper {
117public:
118 PdfInlineImageLooper()
119 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
120
121 virtual PdfResult consumeToken(PdfToken& token);
122 virtual void loop();
123 PdfResult done();
124};
125
126class PdfCompatibilitySectionLooper : public PdfTokenLooper {
127public:
128 PdfCompatibilitySectionLooper()
129 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
130
131 virtual PdfResult consumeToken(PdfToken& token);
132 virtual void loop();
133};
134
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000135// Utilities
136static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
137 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
138
139 bitmap->allocPixels();
140 bitmap->eraseColor(color);
141}
142
143// TODO(edisonn): synonyms? DeviceRGB and RGB ...
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000144static int GetColorSpaceComponents(const std::string& colorSpace) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000145 if (colorSpace == "DeviceCMYK") {
146 return 4;
147 } else if (colorSpace == "DeviceGray" ||
148 colorSpace == "CalGray" ||
149 colorSpace == "Indexed") {
150 return 1;
151 } else if (colorSpace == "DeviceRGB" ||
152 colorSpace == "CalRGB" ||
153 colorSpace == "Lab") {
154 return 3;
155 } else {
156 return 0;
157 }
158}
159
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000160SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000161 SkMatrix matrix;
162 matrix.setAll(SkDoubleToScalar(array[0]),
163 SkDoubleToScalar(array[2]),
164 SkDoubleToScalar(array[4]),
165 SkDoubleToScalar(array[1]),
166 SkDoubleToScalar(array[3]),
167 SkDoubleToScalar(array[5]),
168 SkDoubleToScalar(0),
169 SkDoubleToScalar(0),
170 SkDoubleToScalar(1));
171
172 return matrix;
173}
174
175SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
176 double array[6];
177
178 // TODO(edisonn): security issue, ret if size() != 6
179 for (int i = 0; i < 6; i++) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000180 const SkPdfObject* elem = pdfArray->operator [](i);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000181 if (elem == NULL || !elem->isNumber()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000182 return SkMatrix::I(); // TODO(edisonn): report issue
183 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000184 array[i] = elem->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000185 }
186
187 return SkMatrixFromPdfMatrix(array);
188}
189
edisonn@google.com222382b2013-07-10 22:33:10 +0000190
191extern "C" SkNativeParsedPDF* gDoc;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000192SkBitmap* gDumpBitmap = NULL;
193SkCanvas* gDumpCanvas = NULL;
194char gLastKeyword[100] = "";
195int gLastOpKeyword = -1;
196char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
197int gReadOp = 0;
198
199
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000200#ifdef PDF_TRACE_DIFF_IN_PNG
201static bool hasVisualEffect(const char* pdfOp) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000202 return true;
203 if (*pdfOp == '\0') return false;
204
205 char markedPdfOp[100] = ",";
206 strcat(markedPdfOp, pdfOp);
207 strcat(markedPdfOp, ",");
208
209 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
210}
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000211#endif // PDF_TRACE_DIFF_IN_PNG
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000212
edisonn@google.com222382b2013-07-10 22:33:10 +0000213
214
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000215// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000216static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000217 bool ret = fTokenizer->readToken(token);
218
219 gReadOp++;
220
221#ifdef PDF_TRACE_DIFF_IN_PNG
222 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
223 // the numbar and name of last operation, so the file name will reflect op that changed.
224 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
225 gDumpCanvas->flush();
226
227 SkBitmap bitmap;
228 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
229
230 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
231
232 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
233 SkCanvas canvas(device);
234
235 // draw context stuff here
236 SkPaint blueBorder;
237 blueBorder.setColor(SK_ColorBLUE);
238 blueBorder.setStyle(SkPaint::kStroke_Style);
239 blueBorder.setTextSize(SkDoubleToScalar(20));
240
241 SkString str;
242
243 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
244 if (clipStack) {
245 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
246 const SkClipStack::Element* elem;
247 double y = 0;
248 int total = 0;
249 while (elem = iter.next()) {
250 total++;
251 y += 30;
252
253 switch (elem->getType()) {
254 case SkClipStack::Element::kRect_Type:
255 canvas.drawRect(elem->getRect(), blueBorder);
256 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
257 break;
258 case SkClipStack::Element::kPath_Type:
259 canvas.drawPath(elem->getPath(), blueBorder);
260 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
261 break;
262 case SkClipStack::Element::kEmpty_Type:
263 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
264 break;
265 default:
266 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
267 break;
268 }
269 }
270
271 y += 30;
272 str.printf("Number of clips in stack: %i", total);
273 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
274 }
275
276 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
277 SkPath clipPath;
278 if (clipRegion.getBoundaryPath(&clipPath)) {
279 SkPaint redBorder;
280 redBorder.setColor(SK_ColorRED);
281 redBorder.setStyle(SkPaint::kStroke_Style);
282 canvas.drawPath(clipPath, redBorder);
283 }
284
285 canvas.flush();
286
287 SkString out;
288
289 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
290 // ... and other properties, to be able to debug th code easily
291
292 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
293 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
294 }
295
296 if (token->fType == kKeyword_TokenType) {
297 strcpy(gLastKeyword, token->fKeyword);
298 gLastOpKeyword = gReadOp;
299 } else {
300 strcpy(gLastKeyword, "");
301 }
302#endif
303
304 return ret;
305}
306
307
308
309typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
310
311map<std::string, PdfOperatorRenderer> gPdfOps;
312
313map<std::string, int> gRenderStats[kCount_PdfResult];
314
edisonn@google.com571c70b2013-07-10 17:09:50 +0000315const char* gRenderStatsNames[kCount_PdfResult] = {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000316 "Success",
317 "Partially implemented",
318 "Not yet implemented",
319 "Ignore Error",
320 "Error",
321 "Unsupported/Unknown"
322};
323
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000324static PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000325 const SkPdfObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000326 SkCanvas* canvas)
327{
328
329 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
330 if (skfont == NULL) {
331 skfont = SkPdfFont::Default();
332 }
333
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000334
edisonn@google.com571c70b2013-07-10 17:09:50 +0000335 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000336 // TODO(edisonn): report warning
337 return kIgnoreError_PdfResult;
338 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000339 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000340
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000341 SkUnencodedText binary(str);
342
343 SkDecodedText decoded;
344
345 if (skfont->encoding() == NULL) {
346 // TODO(edisonn): report warning
347 return kNYI_PdfResult;
348 }
349
350 skfont->encoding()->decodeText(binary, &decoded);
351
352 SkPaint paint;
353 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
354 // Or maybe just not call setTextSize at all?
355 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
356 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
357 }
358
359// if (fCurFont && fCurFont->GetFontScale() != 0) {
360// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
361// }
362
363 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
364
365 canvas->save();
366
367#if 1
368 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
369
370 SkPoint point1;
371 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
372
373 SkMatrix mirror;
374 mirror.setTranslate(0, -point1.y());
375 // TODO(edisonn): fix rotated text, and skewed too
376 mirror.postScale(SK_Scalar1, -SK_Scalar1);
377 // TODO(edisonn): post rotate, skew
378 mirror.postTranslate(0, point1.y());
379
380 matrix.postConcat(mirror);
381
382 canvas->setMatrix(matrix);
383
384 SkTraceMatrix(matrix, "mirrored");
385#endif
386
edisonn@google.com6e49c342013-06-27 20:03:43 +0000387 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000388 canvas->restore();
389
390 return kPartial_PdfResult;
391}
392
393// TODO(edisonn): create header files with declarations!
394PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
395PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
396PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
397PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
398
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000399// TODO(edisonn): perf!!!
400
401static SkColorTable* getGrayColortable() {
402 static SkColorTable* grayColortable = NULL;
403 if (grayColortable == NULL) {
404 SkPMColor* colors = new SkPMColor[256];
405 for (int i = 0 ; i < 256; i++) {
406 colors[i] = SkPreMultiplyARGB(255, i, i, i);
407 }
408 grayColortable = new SkColorTable(colors, 256);
409 }
410 return grayColortable;
411}
412
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000413static SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, size_t uncompressedStreamLength,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000414 int width, int height, int bytesPerLine,
415 int bpc, const std::string& colorSpace,
416 bool transparencyMask) {
417 SkBitmap bitmap;
418
edisonn@google.com571c70b2013-07-10 17:09:50 +0000419 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000420//#define MAX_COMPONENTS 10
421
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000422 // TODO(edisonn): assume start of lines are aligned at 32 bits?
423 // Is there a faster way to load the uncompressed stream into a bitmap?
424
425 // minimal support for now
426 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
427 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
428
429 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000430 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000431 for (int w = 0 ; w < width; w++) {
432 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
433 uncompressedStream[3 * w + 1],
434 uncompressedStream[3 * w + 2]);
435 i++;
436 }
437 uncompressedStream += bytesPerLine;
438 }
439
440 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
441 bitmap.setPixels(uncompressedStreamArgb);
442 }
443 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
444 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
445
446 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000447 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000448 for (int w = 0 ; w < width; w++) {
449 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
450 uncompressedStream[w];
451 i++;
452 }
453 uncompressedStream += bytesPerLine;
454 }
455
456 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
457 width, height);
458 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
459 }
460
461 // TODO(edisonn): Report Warning, NYI, or error
462 return bitmap;
463}
464
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000465// utils
466
467// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
468// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
469// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
470// skia format, through a table
471
472// this functions returns the image, it does not look at the smask.
473
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000474static SkBitmap getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000475 if (image == NULL || !image->hasStream()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000476 // TODO(edisonn): report warning to be used in testing.
477 return SkBitmap();
478 }
479
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000480 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc);
481 int64_t width = image->Width(pdfContext->fPdfDoc);
482 int64_t height = image->Height(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000483 std::string colorSpace = "DeviceRGB";
484
485 // TODO(edisonn): color space can be an array too!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000486 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
487 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000488 }
489
490/*
491 bool imageMask = image->imageMask();
492
493 if (imageMask) {
494 if (bpc != 0 && bpc != 1) {
495 // TODO(edisonn): report warning to be used in testing.
496 return SkBitmap();
497 }
498 bpc = 1;
499 }
500*/
501
edisonn@google.com571c70b2013-07-10 17:09:50 +0000502 unsigned char* uncompressedStream = NULL;
503 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000504
edisonn@google.com571c70b2013-07-10 17:09:50 +0000505 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000506
edisonn@google.com571c70b2013-07-10 17:09:50 +0000507 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength, pdfContext->fPdfDoc->allocator()) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000508 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000509 // TODO(edisonn): report warning to be used in testing.
510 return SkBitmap();
511 }
512
edisonn@google.com571c70b2013-07-10 17:09:50 +0000513 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
514
515 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
516 streamDict->getFilterAsName(NULL) == "DCTDecode") ||
517 (streamDict->isFilterAArray(NULL) &&
518 streamDict->getFilterAsArray(NULL)->size() > 0 &&
519 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
520 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() == "DCTDecode"))) {
521 SkBitmap bitmap;
522 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, &bitmap);
523 return bitmap;
524 }
525
526
527
528 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
529// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
530// obj.GetDictionary().GetKey(PdfName("Filter")));
531// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
532// value = resolveReferenceObject(pdfContext->fPdfDoc,
533// &value->GetArray()[0]);
534// }
535// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
536// SkStream stream = SkStream::
537// SkImageDecoder::Factory()
538// }
539
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000540 int bytesPerLine = uncompressedStreamLength / height;
541#ifdef PDF_TRACE
542 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000543 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000544 }
545#endif
546
547 SkBitmap bitmap = transferImageStreamToBitmap(
548 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000549 (int)width, (int)height, bytesPerLine,
550 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000551 transparencyMask);
552
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000553 return bitmap;
554}
555
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000556static SkBitmap getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000557 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000558
559 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000560 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000561 }
562
563 // TODO(edisonn): implement GS SMask. Default to empty right now.
564 return pdfContext->fGraphicsState.fSMask;
565}
566
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000567static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000568 if (skpdfimage == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000569 return kIgnoreError_PdfResult;
570 }
571
572 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
573 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
574
575 canvas->save();
576 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000577
578#if 1
579 SkScalar z = SkIntToScalar(0);
580 SkScalar one = SkIntToScalar(1);
581
582 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
583 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
584 SkMatrix flip;
585 SkAssertResult(flip.setPolyToPoly(from, to, 4));
586 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fMatrix;
587 solveImageFlip.preConcat(flip);
588 canvas->setMatrix(solveImageFlip);
589#endif
590
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000591 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
592
593 if (sMask.empty()) {
594 canvas->drawBitmapRect(image, dst, NULL);
595 } else {
596 canvas->saveLayer(&dst, NULL);
597 canvas->drawBitmapRect(image, dst, NULL);
598 SkPaint xfer;
599 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
600 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
601 canvas->drawBitmapRect(sMask, dst, &xfer);
602 canvas->restore();
603 }
604
605 canvas->restore();
606
607 return kPartial_PdfResult;
608}
609
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000610
611
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000612
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000613static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000614 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000615 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000616 }
617
618 PdfOp_q(pdfContext, canvas, NULL);
619 canvas->save();
620
621
edisonn@google.com571c70b2013-07-10 17:09:50 +0000622 if (skobj->Resources(pdfContext->fPdfDoc)) {
623 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000624 }
625
626 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
627
edisonn@google.com571c70b2013-07-10 17:09:50 +0000628 if (skobj->has_Matrix()) {
629 pdfContext->fGraphicsState.fMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000630 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
631 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
632 // TODO(edisonn) reset matrixTm and matricTlm also?
633 }
634
635 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
636
637 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
638
edisonn@google.com571c70b2013-07-10 17:09:50 +0000639 if (skobj->has_BBox()) {
640 canvas->clipRect(skobj->BBox(pdfContext->fPdfDoc), SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000641 }
642
643 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
644 // For this PdfContentsTokenizer needs to be extended.
645
edisonn@google.com571c70b2013-07-10 17:09:50 +0000646 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000647
edisonn@google.com571c70b2013-07-10 17:09:50 +0000648 SkPdfNativeTokenizer* tokenizer = pdfContext->fPdfDoc->tokenizerOfStream(stream);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000649 if (tokenizer != NULL) {
650 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
651 looper.loop();
652 delete tokenizer;
653 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000654
655 // TODO(edisonn): should we restore the variable stack at the same state?
656 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
657 canvas->restore();
658 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000659 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000660}
661
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000662//static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
663// return kNYI_PdfResult;
664//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000665
edisonn@google.com571c70b2013-07-10 17:09:50 +0000666PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
667 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000668 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000669 }
670
671 PdfOp_q(pdfContext, canvas, NULL);
672 canvas->save();
673
674 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
675 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
676
677 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm;
678 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
679
680 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
681
682 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
683
684 SkRect rm = bBox;
685 pdfContext->fGraphicsState.fMatrix.mapRect(&rm);
686
687 SkTraceRect(rm, "bbox mapped");
688
689 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
690
691 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
692 // For this PdfContentsTokenizer needs to be extended.
693
edisonn@google.com571c70b2013-07-10 17:09:50 +0000694 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000695
edisonn@google.com571c70b2013-07-10 17:09:50 +0000696 SkPdfNativeTokenizer* tokenizer = pdfContext->fPdfDoc->tokenizerOfStream(stream);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000697 if (tokenizer != NULL) {
698 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
699 looper.loop();
700 delete tokenizer;
701 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000702
703 // TODO(edisonn): should we restore the variable stack at the same state?
704 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
705 canvas->restore();
706 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000707
708 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000709}
710
711
edisonn@google.com571c70b2013-07-10 17:09:50 +0000712// TODO(edisonn): make sure the pointer is unique
713std::set<const SkPdfObject*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000714
715class CheckRecursiveRendering {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000716 const SkPdfObject* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000717public:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000718 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000719 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000720 }
721
722 ~CheckRecursiveRendering() {
723 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000724 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000725 }
726
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000727 static bool IsInRendering(const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000728 return gInRendering.find(obj) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000729 }
730};
731
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000732static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000733 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000734 // Oops, corrupt PDF!
735 return kIgnoreError_PdfResult;
736 }
737
edisonn@google.com571c70b2013-07-10 17:09:50 +0000738 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000739
edisonn@google.com571c70b2013-07-10 17:09:50 +0000740 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000741 {
742 case kImageDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000743 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000744 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000745 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000746 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
747 //return doXObject_PS(skxobj.asPS());
edisonn@google.com571c70b2013-07-10 17:09:50 +0000748 default:
749 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000750 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000751}
752
753PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
754 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
755 canvas->save();
756 return kOK_PdfResult;
757}
758
759PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
760 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
761 pdfContext->fStateStack.pop();
762 canvas->restore();
763 return kOK_PdfResult;
764}
765
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000766static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000767 double array[6];
768 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000769 array[5 - i] = pdfContext->fObjectStack.top()->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000770 pdfContext->fObjectStack.pop();
771 }
772
773 // a b
774 // c d
775 // e f
776
777 // 0 1
778 // 2 3
779 // 4 5
780
781 // sx ky
782 // kx sy
783 // tx ty
784 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
785
786 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
787
788#ifdef PDF_TRACE
789 printf("cm ");
790 for (int i = 0 ; i < 6 ; i++) {
791 printf("%f ", array[i]);
792 }
793 printf("\n");
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000794 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000795#endif
796
797 return kOK_PdfResult;
798}
799
800//leading TL Set the text leading, Tl
801//, to leading, which is a number expressed in unscaled text
802//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000803static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000804 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000805
806 pdfContext->fGraphicsState.fTextLeading = ty;
807
808 return kOK_PdfResult;
809}
810
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000811static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000812 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
813 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000814
815 double array[6] = {1, 0, 0, 1, tx, ty};
816 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
817
818 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
819 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
820
821 return kPartial_PdfResult;
822}
823
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000824static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000825 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
826 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000827
edisonn@google.com571c70b2013-07-10 17:09:50 +0000828 // TODO(edisonn): Create factory methods or constructors so native is hidden
829 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000830 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000831
832 PdfOp_TL(pdfContext, canvas, looper);
833
edisonn@google.com571c70b2013-07-10 17:09:50 +0000834 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000835 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000836
edisonn@google.com571c70b2013-07-10 17:09:50 +0000837 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000838 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000839
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000840 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
841
842 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
843 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000844
845 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000846}
847
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000848static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000849 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
850 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
851 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
852 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
853 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
854 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000855
856 double array[6];
857 array[0] = a;
858 array[1] = b;
859 array[2] = c;
860 array[3] = d;
861 array[4] = e;
862 array[5] = f;
863
864 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
865 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
866
867 // TODO(edisonn): Text positioning.
868 pdfContext->fGraphicsState.fMatrixTm = matrix;
869 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
870
871 return kPartial_PdfResult;
872}
873
874//— T* Move to the start of the next line. This operator has the same effect as the code
875//0 Tl Td
876//where Tl is the current leading parameter in the text state
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000877static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000878 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
879 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000880
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000881 pdfContext->fObjectStack.push(zero);
882 pdfContext->fObjectStack.push(tl);
883
884 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
885
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000886 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000887}
888
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000889static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000890 if (pdfContext->fGraphicsState.fPathClosed) {
891 pdfContext->fGraphicsState.fPath.reset();
892 pdfContext->fGraphicsState.fPathClosed = false;
893 }
894
edisonn@google.com571c70b2013-07-10 17:09:50 +0000895 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
896 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000897
898 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
899 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
900
901 return kOK_PdfResult;
902}
903
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000904static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000905 if (pdfContext->fGraphicsState.fPathClosed) {
906 pdfContext->fGraphicsState.fPath.reset();
907 pdfContext->fGraphicsState.fPathClosed = false;
908 }
909
edisonn@google.com571c70b2013-07-10 17:09:50 +0000910 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
911 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000912
913 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
914 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
915
916 return kOK_PdfResult;
917}
918
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000919static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000920 if (pdfContext->fGraphicsState.fPathClosed) {
921 pdfContext->fGraphicsState.fPath.reset();
922 pdfContext->fGraphicsState.fPathClosed = false;
923 }
924
edisonn@google.com571c70b2013-07-10 17:09:50 +0000925 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
926 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
927 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
928 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
929 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
930 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000931
932 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
933 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
934 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
935
936 pdfContext->fGraphicsState.fCurPosX = x3;
937 pdfContext->fGraphicsState.fCurPosY = y3;
938
939 return kOK_PdfResult;
940}
941
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000942static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000943 if (pdfContext->fGraphicsState.fPathClosed) {
944 pdfContext->fGraphicsState.fPath.reset();
945 pdfContext->fGraphicsState.fPathClosed = false;
946 }
947
edisonn@google.com571c70b2013-07-10 17:09:50 +0000948 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
949 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
950 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
951 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000952 double y1 = pdfContext->fGraphicsState.fCurPosY;
953 double x1 = pdfContext->fGraphicsState.fCurPosX;
954
955 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
956 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
957 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
958
959 pdfContext->fGraphicsState.fCurPosX = x3;
960 pdfContext->fGraphicsState.fCurPosY = y3;
961
962 return kOK_PdfResult;
963}
964
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000965static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000966 if (pdfContext->fGraphicsState.fPathClosed) {
967 pdfContext->fGraphicsState.fPath.reset();
968 pdfContext->fGraphicsState.fPathClosed = false;
969 }
970
edisonn@google.com571c70b2013-07-10 17:09:50 +0000971 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
972 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000973 double y2 = pdfContext->fGraphicsState.fCurPosY;
974 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000975 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
976 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000977
978 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
979 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
980 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
981
982 pdfContext->fGraphicsState.fCurPosX = x3;
983 pdfContext->fGraphicsState.fCurPosY = y3;
984
985 return kOK_PdfResult;
986}
987
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000988static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000989 if (pdfContext->fGraphicsState.fPathClosed) {
990 pdfContext->fGraphicsState.fPath.reset();
991 pdfContext->fGraphicsState.fPathClosed = false;
992 }
993
edisonn@google.com571c70b2013-07-10 17:09:50 +0000994 double height = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
995 double width = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
996 double y = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
997 double x = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000998
999 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1000 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1001
1002 pdfContext->fGraphicsState.fCurPosX = x;
1003 pdfContext->fGraphicsState.fCurPosY = y + height;
1004
1005 return kOK_PdfResult;
1006}
1007
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001008static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001009 pdfContext->fGraphicsState.fPath.close();
1010 return kOK_PdfResult;
1011}
1012
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001013static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001014 SkPath path = pdfContext->fGraphicsState.fPath;
1015
1016 if (close) {
1017 path.close();
1018 }
1019
1020 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1021
1022 SkPaint paint;
1023
1024 SkPoint line[2];
1025 if (fill && !stroke && path.isLine(line)) {
1026 paint.setStyle(SkPaint::kStroke_Style);
1027
1028 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1029 paint.setStrokeWidth(SkDoubleToScalar(0));
1030
1031 canvas->drawPath(path, paint);
1032 } else {
1033 if (fill) {
1034 paint.setStyle(SkPaint::kFill_Style);
1035 if (evenOdd) {
1036 path.setFillType(SkPath::kEvenOdd_FillType);
1037 }
1038
1039 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1040
1041 canvas->drawPath(path, paint);
1042 }
1043
1044 if (stroke) {
1045 paint.setStyle(SkPaint::kStroke_Style);
1046
1047 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1048
1049 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1050 canvas->drawPath(path, paint);
1051 }
1052 }
1053
1054 pdfContext->fGraphicsState.fPath.reset();
1055 // todo zoom ... other stuff ?
1056
1057 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1058#ifndef PDF_DEBUG_NO_CLIPING
1059 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1060#endif
1061 }
1062
1063 //pdfContext->fGraphicsState.fClipPath.reset();
1064 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1065
1066 return kPartial_PdfResult;
1067
1068}
1069
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001070static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001071 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1072}
1073
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001074static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001075 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1076}
1077
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001078static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001079 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1080}
1081
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001082static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001083 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1084}
1085
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001086static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001087 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1088}
1089
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001090static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001091 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1092}
1093
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001094static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001095 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1096}
1097
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001098static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001099 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1100}
1101
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001102static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001103 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1104}
1105
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001106static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001107 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1108 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1109#ifndef PDF_DEBUG_NO_CLIPING
1110 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1111#endif
1112 }
1113
1114 //pdfContext->fGraphicsState.fClipPath.reset();
1115 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1116
1117 pdfContext->fGraphicsState.fPathClosed = true;
1118
1119 return kOK_PdfResult;
1120}
1121
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001122static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001123 pdfContext->fGraphicsState.fTextBlock = true;
1124 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1125 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1126
1127 return kPartial_PdfResult;
1128}
1129
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001130static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001131 if (!pdfContext->fGraphicsState.fTextBlock) {
1132 return kIgnoreError_PdfResult;
1133 }
1134 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1135 return kPartial_PdfResult;
1136}
1137
1138//font size Tf Set the text font, Tf
1139//, to font and the text font size, Tfs, to size. font is the name of a
1140//font resource in the Fontsubdictionary of the current resource dictionary; size is
1141//a number representing a scale factor. There is no initial value for either font or
1142//size; they must be specified explicitly using Tf before any text is shown.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001143static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001144 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1145 const char* fontName = pdfContext->fObjectStack.top()->nameValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001146
1147#ifdef PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +00001148 printf("font name: %s\n", fontName);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001149#endif
1150
edisonn@google.com571c70b2013-07-10 17:09:50 +00001151 if (pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1152 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1153 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1154 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1155 // TODO(edisonn): try to recover and draw it any way?
1156 return kIgnoreError_PdfResult;
1157 }
1158 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1159
1160 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1161
1162 if (skfont) {
1163 pdfContext->fGraphicsState.fSkFont = skfont;
1164 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001165 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001166 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001167}
1168
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001169static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001170 if (!pdfContext->fGraphicsState.fTextBlock) {
1171 // TODO(edisonn): try to recover and draw it any way?
1172 return kIgnoreError_PdfResult;
1173 }
1174
1175 PdfResult ret = DrawText(pdfContext,
1176 pdfContext->fObjectStack.top(),
1177 canvas);
1178 pdfContext->fObjectStack.pop();
1179
1180 return ret;
1181}
1182
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001183static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001184 if (!pdfContext->fGraphicsState.fTextBlock) {
1185 // TODO(edisonn): try to recover and draw it any way?
1186 return kIgnoreError_PdfResult;
1187 }
1188
1189 PdfOp_T_star(pdfContext, canvas, looper);
1190 // Do not pop, and push, just transfer the param to Tj
1191 return PdfOp_Tj(pdfContext, canvas, looper);
1192}
1193
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001194static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001195 if (!pdfContext->fGraphicsState.fTextBlock) {
1196 // TODO(edisonn): try to recover and draw it any way?
1197 return kIgnoreError_PdfResult;
1198 }
1199
1200 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1201 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1202 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1203
1204 pdfContext->fObjectStack.push(aw);
1205 PdfOp_Tw(pdfContext, canvas, looper);
1206
1207 pdfContext->fObjectStack.push(ac);
1208 PdfOp_Tc(pdfContext, canvas, looper);
1209
1210 pdfContext->fObjectStack.push(str);
1211 PdfOp_quote(pdfContext, canvas, looper);
1212
1213 return kPartial_PdfResult;
1214}
1215
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001216static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001217 if (!pdfContext->fGraphicsState.fTextBlock) {
1218 // TODO(edisonn): try to recover and draw it any way?
1219 return kIgnoreError_PdfResult;
1220 }
1221
edisonn@google.com571c70b2013-07-10 17:09:50 +00001222 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001223 pdfContext->fObjectStack.pop();
1224
edisonn@google.com571c70b2013-07-10 17:09:50 +00001225 if (!array->isArray()) {
1226 return kIgnoreError_PdfResult;
1227 }
1228
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001229 for( int i=0; i<static_cast<int>(array->size()); i++ )
1230 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001231 if( (*array)[i]->isAnyString()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001232 SkPdfObject* obj = (*array)[i];
1233 DrawText(pdfContext,
1234 obj,
1235 canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001236 } else if ((*array)[i]->isNumber()) {
1237 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001238 SkMatrix matrix;
1239 matrix.setAll(SkDoubleToScalar(1),
1240 SkDoubleToScalar(0),
1241 // TODO(edisonn): use writing mode, vertical/horizontal.
1242 SkDoubleToScalar(-dx), // amount is substracted!!!
1243 SkDoubleToScalar(0),
1244 SkDoubleToScalar(1),
1245 SkDoubleToScalar(0),
1246 SkDoubleToScalar(0),
1247 SkDoubleToScalar(0),
1248 SkDoubleToScalar(1));
1249
1250 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1251 }
1252 }
1253 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1254}
1255
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001256static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001257 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->nameValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001258 return kOK_PdfResult;
1259}
1260
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001261static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001262 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1263}
1264
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001265static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001266 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1267}
1268
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001269static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001270 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001271// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001272
1273 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1274
1275 bool doubles = true;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001276 if (strcmp(colorOperator->fColorSpace, "Indexed") == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001277 doubles = false;
1278 }
1279
1280#ifdef PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +00001281 printf("color space = %s, N = %i\n", colorOperator->fColorSpace, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001282#endif
1283
1284 for (int i = n - 1; i >= 0 ; i--) {
1285 if (doubles) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001286 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1287// } else {
1288// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001289 }
1290 }
1291
1292 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001293 // TODO(edisonn): do possible field values to enum at parsing time!
1294 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
1295 if (strcmp(colorOperator->fColorSpace, "DeviceRGB") == 0 || strcmp(colorOperator->fColorSpace, "RGB") == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001296 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
1297 }
1298 return kPartial_PdfResult;
1299}
1300
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001301static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001302 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1303}
1304
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001305static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001306 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1307}
1308
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001309static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001310 //SkPdfString* name;
1311 if (pdfContext->fObjectStack.top()->isName()) {
1312 // TODO(edisonn): get name, pass it
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001313 pdfContext->fObjectStack.pop();
1314 }
1315
1316 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1317 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1318
1319 return kPartial_PdfResult;
1320}
1321
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001322static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001323 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1324}
1325
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001326static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001327 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1328}
1329
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001330static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001331 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001332 return kNYI_PdfResult;
1333}
1334
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001335static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001336 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1337}
1338
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001339static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001340 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1341}
1342
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001343static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001344 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1345 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1346 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001347
1348 colorOperator->fColorSpace = "DeviceRGB";
1349 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
1350 return kOK_PdfResult;
1351}
1352
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001353static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001354 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1355}
1356
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001357static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001358 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1359}
1360
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001361static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001362 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001363 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1364 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1365 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1366 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001367
1368 colorOperator->fColorSpace = "DeviceCMYK";
1369 // TODO(edisonn): Set color.
1370 return kNYI_PdfResult;
1371}
1372
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001373static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001374 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1375}
1376
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001377static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001378 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1379}
1380
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001381static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001382 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1383 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1384
1385 return kOK_PdfResult;
1386}
1387
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001388static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001389 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1390
1391#ifdef PDF_TRACE
1392 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
1393 printf("CLIP IS RECT\n");
1394 }
1395#endif
1396
1397 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
1398 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1399 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1400
1401 return kPartial_PdfResult;
1402}
1403
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001404static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001405 *looper = new PdfCompatibilitySectionLooper();
1406 return kOK_PdfResult;
1407}
1408
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001409static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001410#ifdef ASSERT_BAD_PDF_OPS
1411 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1412 // have the assert when testing good pdfs.
1413#endif
1414 return kIgnoreError_PdfResult;
1415}
1416
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001417static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001418 *looper = new PdfInlineImageLooper();
1419 return kOK_PdfResult;
1420}
1421
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001422static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001423#ifdef ASSERT_BAD_PDF_OPS
1424 SkASSERT(false); // must be processed in inline image looper, but let's
1425 // have the assert when testing good pdfs.
1426#endif
1427 return kIgnoreError_PdfResult;
1428}
1429
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001430static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001431#ifdef ASSERT_BAD_PDF_OPS
1432 SkASSERT(false); // must be processed in inline image looper, but let's
1433 // have the assert when testing good pdfs.
1434#endif
1435 return kIgnoreError_PdfResult;
1436}
1437
1438//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001439static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001440 double lineWidth = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001441 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1442
1443 return kOK_PdfResult;
1444}
1445
1446//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001447static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001448 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001449 //double lineCap = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001450
1451 return kNYI_PdfResult;
1452}
1453
1454//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001455static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001456 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001457 //double lineJoin = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001458
1459 return kNYI_PdfResult;
1460}
1461
1462//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001463static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001464 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001465 //double miterLimit = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001466
1467 return kNYI_PdfResult;
1468}
1469
1470//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1471//page 155).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001472static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001473 pdfContext->fObjectStack.pop();
1474 pdfContext->fObjectStack.pop();
1475
1476 return kNYI_PdfResult;
1477}
1478
1479//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001480static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001481 pdfContext->fObjectStack.pop();
1482
1483 return kNYI_PdfResult;
1484}
1485
1486//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1487//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1488//fies the output device’s default flatness tolerance.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001489static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001490 pdfContext->fObjectStack.pop();
1491
1492 return kNYI_PdfResult;
1493}
1494
1495//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
1496//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001497static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001498 const char* name = pdfContext->fObjectStack.top()->nameValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001499
1500#ifdef PDF_TRACE
1501 std::string str;
1502#endif
1503
1504 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001505 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001506
1507 if (extGStateDictionary == NULL) {
1508#ifdef PDF_TRACE
1509 printf("ExtGState is NULL!\n");
1510#endif
1511 return kIgnoreError_PdfResult;
1512 }
1513
edisonn@google.com571c70b2013-07-10 17:09:50 +00001514 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001515
edisonn@google.com571c70b2013-07-10 17:09:50 +00001516 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
1517 return kIgnoreError_PdfResult;
1518 }
1519 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001520
1521 // TODO(edisonn): now load all those properties in graphic state.
1522 if (gs == NULL) {
1523 return kIgnoreError_PdfResult;
1524 }
1525
1526 if (gs->has_CA()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001527 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001528 }
1529
1530 if (gs->has_ca()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001531 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001532 }
1533
1534 if (gs->has_LW()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001535 pdfContext->fGraphicsState.fLineWidth = gs->LW(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001536 }
1537
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001538 return kNYI_PdfResult;
1539}
1540
1541//charSpace Tc Set the character spacing, Tc
1542//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
1543//Initial value: 0.
1544PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001545 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001546 pdfContext->fGraphicsState.fCharSpace = charSpace;
1547
1548 return kOK_PdfResult;
1549}
1550
1551//wordSpace Tw Set the word spacing, T
1552//w
1553//, to wordSpace, which is a number expressed in unscaled
1554//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
1555//value: 0.
1556PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001557 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001558 pdfContext->fGraphicsState.fWordSpace = wordSpace;
1559
1560 return kOK_PdfResult;
1561}
1562
1563//scale Tz Set the horizontal scaling, Th
1564//, to (scale ˜ 100). scale is a number specifying the
1565//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001566static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001567 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001568
1569 return kNYI_PdfResult;
1570}
1571
1572//render Tr Set the text rendering mode, T
1573//mode, to render, which is an integer. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001574static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001575 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001576
1577 return kNYI_PdfResult;
1578}
1579//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
1580//units. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001581static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001582 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001583
1584 return kNYI_PdfResult;
1585}
1586
1587//wx wy d0
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001588static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001589 pdfContext->fObjectStack.pop();
1590 pdfContext->fObjectStack.pop();
1591
1592 return kNYI_PdfResult;
1593}
1594
1595//wx wy llx lly urx ury d1
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001596static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001597 pdfContext->fObjectStack.pop();
1598 pdfContext->fObjectStack.pop();
1599 pdfContext->fObjectStack.pop();
1600 pdfContext->fObjectStack.pop();
1601 pdfContext->fObjectStack.pop();
1602 pdfContext->fObjectStack.pop();
1603
1604 return kNYI_PdfResult;
1605}
1606
1607//name sh
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001608static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001609 pdfContext->fObjectStack.pop();
1610
1611 return kNYI_PdfResult;
1612}
1613
1614//name Do
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001615static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001616 const char* name = pdfContext->fObjectStack.top()->nameValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001617
edisonn@google.com571c70b2013-07-10 17:09:50 +00001618 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001619
1620 if (xObject == NULL) {
1621#ifdef PDF_TRACE
1622 printf("XObject is NULL!\n");
1623#endif
1624 return kIgnoreError_PdfResult;
1625 }
1626
edisonn@google.com571c70b2013-07-10 17:09:50 +00001627 SkPdfObject* value = xObject->get(name);
1628 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001629
1630#ifdef PDF_TRACE
1631// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001632// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001633#endif
1634
edisonn@google.com571c70b2013-07-10 17:09:50 +00001635 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001636}
1637
1638//tag MP Designate a marked-content point. tag is a name object indicating the role or
1639//significance of the point.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001640static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001641 pdfContext->fObjectStack.pop();
1642
1643 return kNYI_PdfResult;
1644}
1645
1646//tag properties DP Designate a marked-content point with an associated property list. tag is a
1647//name object indicating the role or significance of the point; properties is
1648//either an inline dictionary containing the property list or a name object
1649//associated with it in the Properties subdictionary of the current resource
1650//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001651static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001652 pdfContext->fObjectStack.pop();
1653 pdfContext->fObjectStack.pop();
1654
1655 return kNYI_PdfResult;
1656}
1657
1658//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
1659//tag is a name object indicating the role or significance of the sequence.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001660static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001661 pdfContext->fObjectStack.pop();
1662
1663 return kNYI_PdfResult;
1664}
1665
1666//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
1667//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
1668//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”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001669static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001670 pdfContext->fObjectStack.pop();
1671 pdfContext->fObjectStack.pop();
1672
1673 return kNYI_PdfResult;
1674}
1675
1676//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001677static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001678 return kNYI_PdfResult;
1679}
1680
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001681static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001682 static bool gInitialized = false;
1683 if (gInitialized) {
1684 return;
1685 }
1686
1687 gPdfOps["q"] = PdfOp_q;
1688 gPdfOps["Q"] = PdfOp_Q;
1689 gPdfOps["cm"] = PdfOp_cm;
1690
1691 gPdfOps["TD"] = PdfOp_TD;
1692 gPdfOps["Td"] = PdfOp_Td;
1693 gPdfOps["Tm"] = PdfOp_Tm;
1694 gPdfOps["T*"] = PdfOp_T_star;
1695
1696 gPdfOps["m"] = PdfOp_m;
1697 gPdfOps["l"] = PdfOp_l;
1698 gPdfOps["c"] = PdfOp_c;
1699 gPdfOps["v"] = PdfOp_v;
1700 gPdfOps["y"] = PdfOp_y;
1701 gPdfOps["h"] = PdfOp_h;
1702 gPdfOps["re"] = PdfOp_re;
1703
1704 gPdfOps["S"] = PdfOp_S;
1705 gPdfOps["s"] = PdfOp_s;
1706 gPdfOps["f"] = PdfOp_f;
1707 gPdfOps["F"] = PdfOp_F;
1708 gPdfOps["f*"] = PdfOp_f_star;
1709 gPdfOps["B"] = PdfOp_B;
1710 gPdfOps["B*"] = PdfOp_B_star;
1711 gPdfOps["b"] = PdfOp_b;
1712 gPdfOps["b*"] = PdfOp_b_star;
1713 gPdfOps["n"] = PdfOp_n;
1714
1715 gPdfOps["BT"] = PdfOp_BT;
1716 gPdfOps["ET"] = PdfOp_ET;
1717
1718 gPdfOps["Tj"] = PdfOp_Tj;
1719 gPdfOps["'"] = PdfOp_quote;
1720 gPdfOps["\""] = PdfOp_doublequote;
1721 gPdfOps["TJ"] = PdfOp_TJ;
1722
1723 gPdfOps["CS"] = PdfOp_CS;
1724 gPdfOps["cs"] = PdfOp_cs;
1725 gPdfOps["SC"] = PdfOp_SC;
1726 gPdfOps["SCN"] = PdfOp_SCN;
1727 gPdfOps["sc"] = PdfOp_sc;
1728 gPdfOps["scn"] = PdfOp_scn;
1729 gPdfOps["G"] = PdfOp_G;
1730 gPdfOps["g"] = PdfOp_g;
1731 gPdfOps["RG"] = PdfOp_RG;
1732 gPdfOps["rg"] = PdfOp_rg;
1733 gPdfOps["K"] = PdfOp_K;
1734 gPdfOps["k"] = PdfOp_k;
1735
1736 gPdfOps["W"] = PdfOp_W;
1737 gPdfOps["W*"] = PdfOp_W_star;
1738
1739 gPdfOps["BX"] = PdfOp_BX;
1740 gPdfOps["EX"] = PdfOp_EX;
1741
1742 gPdfOps["BI"] = PdfOp_BI;
1743 gPdfOps["ID"] = PdfOp_ID;
1744 gPdfOps["EI"] = PdfOp_EI;
1745
1746 gPdfOps["w"] = PdfOp_w;
1747 gPdfOps["J"] = PdfOp_J;
1748 gPdfOps["j"] = PdfOp_j;
1749 gPdfOps["M"] = PdfOp_M;
1750 gPdfOps["d"] = PdfOp_d;
1751 gPdfOps["ri"] = PdfOp_ri;
1752 gPdfOps["i"] = PdfOp_i;
1753 gPdfOps["gs"] = PdfOp_gs;
1754
1755 gPdfOps["Tc"] = PdfOp_Tc;
1756 gPdfOps["Tw"] = PdfOp_Tw;
1757 gPdfOps["Tz"] = PdfOp_Tz;
1758 gPdfOps["TL"] = PdfOp_TL;
1759 gPdfOps["Tf"] = PdfOp_Tf;
1760 gPdfOps["Tr"] = PdfOp_Tr;
1761 gPdfOps["Ts"] = PdfOp_Ts;
1762
1763 gPdfOps["d0"] = PdfOp_d0;
1764 gPdfOps["d1"] = PdfOp_d1;
1765
1766 gPdfOps["sh"] = PdfOp_sh;
1767
1768 gPdfOps["Do"] = PdfOp_Do;
1769
1770 gPdfOps["MP"] = PdfOp_MP;
1771 gPdfOps["DP"] = PdfOp_DP;
1772 gPdfOps["BMC"] = PdfOp_BMC;
1773 gPdfOps["BDC"] = PdfOp_BDC;
1774 gPdfOps["EMC"] = PdfOp_EMC;
1775
1776 gInitialized = true;
1777}
1778
1779class InitPdfOps {
1780public:
1781 InitPdfOps() {
1782 initPdfOperatorRenderes();
1783 }
1784};
1785
1786InitPdfOps gInitPdfOps;
1787
1788void reportPdfRenderStats() {
1789 std::map<std::string, int>::iterator iter;
1790
1791 for (int i = 0 ; i < kCount_PdfResult; i++) {
1792 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++iter) {
1793 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_str(), iter->second);
1794 }
1795 }
1796}
1797
1798PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001799 char keyword[256];
1800
1801 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001802 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001803 strncpy(keyword, token.fKeyword, token.fKeywordLength);
1804 keyword[token.fKeywordLength] = '\0';
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001805 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com571c70b2013-07-10 17:09:50 +00001806 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[keyword];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001807 if (pdfOperatorRenderer) {
1808 // caller, main work is done by pdfOperatorRenderer(...)
1809 PdfTokenLooper* childLooper = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001810 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)][keyword]++;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001811
1812 if (childLooper) {
1813 childLooper->setUp(this);
1814 childLooper->loop();
1815 delete childLooper;
1816 }
1817 } else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001818 gRenderStats[kUnsupported_PdfResult][keyword]++;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001819 }
1820 }
1821 else if (token.fType == kObject_TokenType)
1822 {
1823 fPdfContext->fObjectStack.push( token.fObject );
1824 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001825 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001826 // TODO(edisonn): deine or use assert not reached
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001827 return kIgnoreError_PdfResult;
1828 }
1829 return kOK_PdfResult;
1830}
1831
1832void PdfMainLooper::loop() {
1833 PdfToken token;
1834 while (readToken(fTokenizer, &token)) {
1835 consumeToken(token);
1836 }
1837}
1838
1839PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
1840 //pdfContext.fInlineImage.fKeyValuePairs[key] = value;
1841 return kNYI_PdfResult;
1842}
1843
1844void PdfInlineImageLooper::loop() {
1845 PdfToken token;
1846 while (readToken(fTokenizer, &token)) {
1847 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
1848 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
1849 looper->setUp(this);
1850 looper->loop();
1851 } else {
1852 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI") == 0) {
1853 done();
1854 return;
1855 }
1856
1857 consumeToken(token);
1858 }
1859 }
1860 // TODO(edisonn): report error/warning, EOF without EI.
1861}
1862
1863PdfResult PdfInlineImageLooper::done() {
1864
1865 // TODO(edisonn): long to short names
1866 // TODO(edisonn): set properties in a map
1867 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
1868 // the stream.
1869
1870 SkBitmap bitmap;
1871 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
1872
1873 // TODO(edisonn): matrix use.
1874 // Draw dummy red square, to show the prezence of the inline image.
1875 fCanvas->drawBitmap(bitmap,
1876 SkDoubleToScalar(0),
1877 SkDoubleToScalar(0),
1878 NULL);
1879 return kNYI_PdfResult;
1880}
1881
1882PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
1883 return fParent->consumeToken(token);
1884}
1885
1886void PdfCompatibilitySectionLooper::loop() {
1887 // TODO(edisonn): save stacks position, or create a new stack?
1888 // TODO(edisonn): what happens if we pop out more variables then when we started?
1889 // restore them? fail? We could create a new operands stack for every new BX/EX section,
1890 // pop-ing too much will not affect outside the section.
1891 PdfToken token;
1892 while (readToken(fTokenizer, &token)) {
1893 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
1894 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
1895 looper->setUp(this);
1896 looper->loop();
1897 delete looper;
1898 } else {
1899 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
1900 fParent->consumeToken(token);
1901 }
1902 }
1903 // TODO(edisonn): restore stack.
1904}
1905
1906// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
1907// TODO(edisonn): Add API for Forms viewing and editing
1908// e.g. SkBitmap getPage(int page);
1909// int formsCount();
1910// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
1911// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
1912// if we load the first page, and we zoom to fit to screen horizontally, then load only those
1913// resources needed, so the preview is fast.
1914// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
1915// references automatically.
1916
edisonn@google.com222382b2013-07-10 22:33:10 +00001917PdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001918
edisonn@google.com222382b2013-07-10 22:33:10 +00001919bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas) const {
1920 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001921 return false;
1922 }
1923
edisonn@google.com222382b2013-07-10 22:33:10 +00001924 if (page < 0 || page >= pages()) {
1925 return false;
1926 }
1927
1928 SkPdfNativeTokenizer* tokenizer = fPdfDoc->tokenizerOfPage(page);
1929
1930 PdfContext pdfContext(fPdfDoc);
1931 pdfContext.fOriginalMatrix = SkMatrix::I();
1932 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
1933
1934 gPdfContext = &pdfContext;
1935
1936 // TODO(edisonn): get matrix stuff right.
1937 // TODO(edisonn): add DPI/scale/zoom.
1938 SkScalar z = SkIntToScalar(0);
1939 SkRect rect = fPdfDoc->MediaBox(page);
1940 SkScalar w = rect.width();
1941 SkScalar h = rect.height();
1942
1943 SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(w, z), SkPoint::Make(w, h), SkPoint::Make(z, h)};
1944// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
1945
1946 // TODO(edisonn): add flag for this app to create sourunding buffer zone
1947 // TODO(edisonn): add flagg for no clipping.
1948 // Use larger image to make sure we do not draw anything outside of page
1949 // could be used in tests.
1950
1951#ifdef PDF_DEBUG_3X
1952 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)};
1953#else
1954 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
1955#endif
1956 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
1957 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
1958
1959 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
1960 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
1961
1962 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
1963 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
1964
1965 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
1966 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
1967
1968
1969 pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
1970 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
1971 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
1972
1973 canvas->setMatrix(pdfContext.fOriginalMatrix);
1974
1975#ifndef PDF_DEBUG_NO_PAGE_CLIPING
1976 canvas->clipRect(SkRect::MakeXYWH(z, z, w, h), SkRegion::kIntersect_Op, true);
1977#endif
1978
1979// erase with red before?
1980// SkPaint paint;
1981// paint.setColor(SK_ColorRED);
1982// canvas->drawRect(rect, paint);
1983
1984 PdfMainLooper looper(NULL, tokenizer, &pdfContext, canvas);
1985 looper.loop();
1986
1987 delete tokenizer;
1988
1989 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001990 return true;
1991}
edisonn@google.com222382b2013-07-10 22:33:10 +00001992
1993bool SkPdfRenderer::load(const SkString inputFileName) {
1994 unload();
1995
1996 // TODO(edisonn): create static function that could return NULL if there are errors
1997 fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
1998
1999 return fPdfDoc != NULL;
2000}
2001
2002int SkPdfRenderer::pages() const {
2003 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2004}
2005
2006void SkPdfRenderer::unload() {
2007 delete fPdfDoc;
2008 fPdfDoc = NULL;
2009}
2010
2011SkRect SkPdfRenderer::MediaBox(int page) const {
2012 SkASSERT(fPdfDoc);
2013 return fPdfDoc->MediaBox(page);
2014}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002015
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002016size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002017 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2018}
2019