blob: e18c3399255d8f697ca9687ec0164fe27e740d96 [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
edisonn@google.come91260c2013-09-04 17:29:06 +00008#include "SkBitmapDevice.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +00009#include "SkCanvas.h"
10#include "SkDevice.h"
11#include "SkForceLinking.h"
12#include "SkGraphics.h"
13#include "SkImageDecoder.h"
14#include "SkImageEncoder.h"
15#include "SkOSFile.h"
16#include "SkPicture.h"
17#include "SkStream.h"
18#include "SkTypeface.h"
19#include "SkTArray.h"
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000020#include "SkTDict.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000021
edisonn@google.com3aa35552013-08-14 18:26:20 +000022#include "SkPdfGraphicsState.h"
edisonn@google.com15b11182013-07-11 14:43:15 +000023#include "SkPdfNativeTokenizer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000024
edisonn@google.com3aa35552013-08-14 18:26:20 +000025extern "C" SkPdfContext* gPdfContext;
edisonn@google.com15b11182013-07-11 14:43:15 +000026extern "C" SkBitmap* gDumpBitmap;
27extern "C" SkCanvas* gDumpCanvas;
28
edisonn@google.com131d4ee2013-06-26 17:48:12 +000029__SK_FORCE_IMAGE_DECODER_LINKING;
30
31// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
32// keep for each object pos in file
33// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
34
35// TODO(edisonn): security - validate all the user input, all pdf!
36
edisonn@google.com6e49c342013-06-27 20:03:43 +000037// TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor changes in text positioning and font
38// this way, we look more at other features and layout in diffs
edisonn@google.com131d4ee2013-06-26 17:48:12 +000039
edisonn@google.com3aac1f92013-07-02 22:42:53 +000040// TODO(edisonn): move trace dump in the get functions, and mapper ones too so it ghappens automatically
41/*
42#ifdef PDF_TRACE
edisonn@google.com063d7072013-08-16 15:05:08 +000043 SkString str;
edisonn@google.com571c70b2013-07-10 17:09:50 +000044 pdfContext->fGraphicsState.fResources->native()->ToString(str);
edisonn@google.com3aac1f92013-07-02 22:42:53 +000045 printf("Print Tf Resources: %s\n", str.c_str());
46#endif
47 */
48
edisonn@google.com131d4ee2013-06-26 17:48:12 +000049#include "SkPdfHeaders_autogen.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +000050#include "SkPdfMapper_autogen.h"
edisonn@google.com222382b2013-07-10 22:33:10 +000051#include "SkPdfRenderer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000052
edisonn@google.com131d4ee2013-06-26 17:48:12 +000053#include "SkPdfUtils.h"
54
55#include "SkPdfFont.h"
56
edisonn@google.com131d4ee2013-06-26 17:48:12 +000057/*
58 * TODO(edisonn):
59 * - all font types and all ppdf font features
60 * - word spacing
61 * - load font for baidu.pdf
62 * - load font for youtube.pdf
63 * - parser for pdf from the definition already available in pdfspec_autogen.py
64 * - all docs from ~/work
edisonn@google.com571c70b2013-07-10 17:09:50 +000065 * - 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 +000066 * - load gs/ especially smask and already known prop (skp) ... in progress
67 * - wrapper on classes for customizations? e.g.
68 * SkPdfPageObjectVanila - has only the basic loaders/getters
69 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
70 * need to find a nice object model for all this with constructors and factories
71 * - deal with inheritable automatically ?
72 * - deal with specific type in spec directly, add all dictionary types to known types
73*/
74
edisonn@google.combd2f3012013-08-22 14:18:04 +000075#define EXPECT_OPERANDS(pdfContext,n) \
76 bool __failed = pdfContext->fObjectStack.count() < n; \
77 SkDEBUGCODE(int __cnt = n);
78
79#define POP_OBJ(pdfContext,name) \
80 SkDEBUGCODE(__cnt--); \
81 SkASSERT(__cnt >= 0); \
82 SkPdfNativeObject* name = NULL; \
83 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
84 if (pdfContext->fObjectStack.count() > 0) { \
85 name = pdfContext->fObjectStack.top(); \
86 pdfContext->fObjectStack.pop(); \
87 }
88
89#define POP_NUMBER(pdfContext,name) \
90 SkDEBUGCODE(__cnt--); \
91 SkASSERT(__cnt >= 0); \
92 double name = 0; \
93 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
94 if (pdfContext->fObjectStack.count() > 0) { \
95 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
96 pdfContext->fObjectStack.pop(); \
97 if (!tmp || !tmp->isNumber()) { \
98 __failed = true;\
99 } else { \
100 name = tmp->numberValue(); \
101 } \
102 }
103
104#define POP_INTEGER(pdfContext,name) \
105 SkDEBUGCODE(__cnt--); \
106 SkASSERT(__cnt >= 0); \
107 int64_t name = 0; \
108 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
109 if (pdfContext->fObjectStack.count() > 0) { \
110 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
111 pdfContext->fObjectStack.pop(); \
112 if (!tmp || !tmp->isInteger()) { \
113 __failed = true;\
114 } else { \
115 name = tmp->intValue(); \
116 } \
117 }
118
119#define POP_NUMBER_INTO(pdfContext,var) \
120 SkDEBUGCODE(__cnt--); \
121 SkASSERT(__cnt >= 0); \
122 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
123 if (pdfContext->fObjectStack.count() > 0) { \
124 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
125 pdfContext->fObjectStack.pop(); \
126 if (!tmp || !tmp->isNumber()) { \
127 __failed = true;\
128 } else { \
129 var = tmp->numberValue(); \
130 } \
131 }
132
133
134#define POP_NAME(pdfContext,name) \
135 SkDEBUGCODE(__cnt--); \
136 SkASSERT(__cnt >= 0); \
137 SkPdfNativeObject* name = NULL; \
138 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
139 if (pdfContext->fObjectStack.count() > 0) { \
140 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
141 pdfContext->fObjectStack.pop(); \
142 if (!tmp || !tmp->isName()) { \
143 __failed = true;\
144 } else { \
145 name = tmp; \
146 } \
147 }
148
149#define POP_STRING(pdfContext,name) \
150 SkDEBUGCODE(__cnt--); \
151 SkASSERT(__cnt >= 0); \
152 SkPdfNativeObject* name = NULL; \
153 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
154 if (pdfContext->fObjectStack.count() > 0) { \
155 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
156 pdfContext->fObjectStack.pop(); \
157 if (!tmp || !tmp->isAnyString()) { \
158 __failed = true;\
159 } else { \
160 name = tmp; \
161 } \
162 }
163
164#define POP_ARRAY(pdfContext,name) \
165 SkDEBUGCODE(__cnt--); \
166 SkASSERT(__cnt >= 0); \
167 SkPdfArray* name = NULL; \
168 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
169 if (pdfContext->fObjectStack.count() > 0) { \
170 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
171 pdfContext->fObjectStack.pop(); \
172 if (!tmp || !tmp->isArray()) { \
173 __failed = true;\
174 } else { \
175 name = (SkPdfArray*)tmp; \
176 } \
177 }
178
179// TODO(edisonn): ability to turn on asserts for known good files
180// log error - add name of function? location in file?
181#define CHECK_PARAMETERS() \
182 SkASSERT(__cnt == 0); \
183 if (__failed) return kIgnoreError_SkPdfResult;
184
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000185NotOwnedString strings_DeviceRGB;
186NotOwnedString strings_DeviceCMYK;
edisonn@google.com222382b2013-07-10 22:33:10 +0000187
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000188class StringsInit {
189public:
190 StringsInit() {
191 NotOwnedString::init(&strings_DeviceRGB, "DeviceRGB");
192 NotOwnedString::init(&strings_DeviceCMYK, "DeviceCMYK");
193 }
194};
195
196StringsInit gStringsInit;
edisonn@google.com222382b2013-07-10 22:33:10 +0000197
198// TODO(edisonn): Document PdfTokenLooper and subclasses.
199class PdfTokenLooper {
200protected:
201 PdfTokenLooper* fParent;
202 SkPdfNativeTokenizer* fTokenizer;
edisonn@google.com3aa35552013-08-14 18:26:20 +0000203 SkPdfContext* fPdfContext;
edisonn@google.com222382b2013-07-10 22:33:10 +0000204 SkCanvas* fCanvas;
205
206public:
207 PdfTokenLooper(PdfTokenLooper* parent,
208 SkPdfNativeTokenizer* tokenizer,
edisonn@google.com3aa35552013-08-14 18:26:20 +0000209 SkPdfContext* pdfContext,
edisonn@google.com222382b2013-07-10 22:33:10 +0000210 SkCanvas* canvas)
211 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
212
213 virtual ~PdfTokenLooper() {}
214
edisonn@google.com3aa35552013-08-14 18:26:20 +0000215 virtual SkPdfResult consumeToken(PdfToken& token) = 0;
edisonn@google.com222382b2013-07-10 22:33:10 +0000216 virtual void loop() = 0;
217
218 void setUp(PdfTokenLooper* parent) {
219 fParent = parent;
220 fTokenizer = parent->fTokenizer;
221 fPdfContext = parent->fPdfContext;
222 fCanvas = parent->fCanvas;
223 }
edisonn@google.com78b38b12013-07-15 18:20:58 +0000224
225 SkPdfNativeTokenizer* tokenizer() { return fTokenizer; }
edisonn@google.com222382b2013-07-10 22:33:10 +0000226};
227
228class PdfMainLooper : public PdfTokenLooper {
229public:
230 PdfMainLooper(PdfTokenLooper* parent,
231 SkPdfNativeTokenizer* tokenizer,
edisonn@google.com3aa35552013-08-14 18:26:20 +0000232 SkPdfContext* pdfContext,
edisonn@google.com222382b2013-07-10 22:33:10 +0000233 SkCanvas* canvas)
234 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
235
edisonn@google.com3aa35552013-08-14 18:26:20 +0000236 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000237 virtual void loop();
238};
239
240class PdfInlineImageLooper : public PdfTokenLooper {
241public:
242 PdfInlineImageLooper()
243 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
244
edisonn@google.com3aa35552013-08-14 18:26:20 +0000245 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000246 virtual void loop();
edisonn@google.com3aa35552013-08-14 18:26:20 +0000247 SkPdfResult done();
edisonn@google.com222382b2013-07-10 22:33:10 +0000248};
249
250class PdfCompatibilitySectionLooper : public PdfTokenLooper {
251public:
252 PdfCompatibilitySectionLooper()
253 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
254
edisonn@google.com3aa35552013-08-14 18:26:20 +0000255 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000256 virtual void loop();
257};
258
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000259// Utilities
260static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
261 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
262
263 bitmap->allocPixels();
264 bitmap->eraseColor(color);
265}
266
267// TODO(edisonn): synonyms? DeviceRGB and RGB ...
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000268static int GetColorSpaceComponents(NotOwnedString& colorSpace) {
269 if (colorSpace.equals("DeviceCMYK")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000270 return 4;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000271 } else if (colorSpace.equals("DeviceGray") ||
272 colorSpace.equals("CalGray") ||
273 colorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000274 return 1;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000275 } else if (colorSpace.equals("DeviceRGB") ||
276 colorSpace.equals("CalRGB") ||
277 colorSpace.equals("Lab")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000278 return 3;
279 } else {
280 return 0;
281 }
282}
283
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000284SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000285 SkMatrix matrix;
286 matrix.setAll(SkDoubleToScalar(array[0]),
287 SkDoubleToScalar(array[2]),
288 SkDoubleToScalar(array[4]),
289 SkDoubleToScalar(array[1]),
290 SkDoubleToScalar(array[3]),
291 SkDoubleToScalar(array[5]),
292 SkDoubleToScalar(0),
293 SkDoubleToScalar(0),
294 SkDoubleToScalar(1));
295
296 return matrix;
297}
298
299SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
300 double array[6];
301
302 // TODO(edisonn): security issue, ret if size() != 6
303 for (int i = 0; i < 6; i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000304 const SkPdfNativeObject* elem = pdfArray->operator [](i);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000305 if (elem == NULL || !elem->isNumber()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000306 return SkMatrix::I(); // TODO(edisonn): report issue
307 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000308 array[i] = elem->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000309 }
310
311 return SkMatrixFromPdfMatrix(array);
312}
313
edisonn@google.com222382b2013-07-10 22:33:10 +0000314
edisonn@google.com3aa35552013-08-14 18:26:20 +0000315extern "C" SkPdfNativeDoc* gDoc;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000316SkBitmap* gDumpBitmap = NULL;
317SkCanvas* gDumpCanvas = NULL;
318char gLastKeyword[100] = "";
319int gLastOpKeyword = -1;
320char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
321int gReadOp = 0;
322
323
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000324#ifdef PDF_TRACE_DIFF_IN_PNG
325static bool hasVisualEffect(const char* pdfOp) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000326 return true;
327 if (*pdfOp == '\0') return false;
328
329 char markedPdfOp[100] = ",";
330 strcat(markedPdfOp, pdfOp);
331 strcat(markedPdfOp, ",");
332
333 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
334}
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000335#endif // PDF_TRACE_DIFF_IN_PNG
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000336
edisonn@google.com222382b2013-07-10 22:33:10 +0000337
338
edisonn@google.com3aa35552013-08-14 18:26:20 +0000339// TODO(edisonn): Pass SkPdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000340static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000341 bool ret = fTokenizer->readToken(token);
342
343 gReadOp++;
edisonn@google.com0f901902013-08-07 11:56:16 +0000344 gLastOpKeyword++;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000345#ifdef PDF_TRACE_DIFF_IN_PNG
346 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
347 // the numbar and name of last operation, so the file name will reflect op that changed.
edisonn@google.com0f901902013-08-07 11:56:16 +0000348 if (gLastKeyword[0] && hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000349 gDumpCanvas->flush();
350
351 SkBitmap bitmap;
352 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
353
354 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
355
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000356 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000357 SkCanvas canvas(device);
358
359 // draw context stuff here
360 SkPaint blueBorder;
361 blueBorder.setColor(SK_ColorBLUE);
362 blueBorder.setStyle(SkPaint::kStroke_Style);
363 blueBorder.setTextSize(SkDoubleToScalar(20));
364
365 SkString str;
366
367 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
368 if (clipStack) {
369 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
370 const SkClipStack::Element* elem;
371 double y = 0;
372 int total = 0;
edisonn@google.com91ce6982013-08-05 20:45:40 +0000373 while ((elem = iter.next()) != NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000374 total++;
375 y += 30;
376
377 switch (elem->getType()) {
378 case SkClipStack::Element::kRect_Type:
379 canvas.drawRect(elem->getRect(), blueBorder);
380 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
381 break;
382 case SkClipStack::Element::kPath_Type:
383 canvas.drawPath(elem->getPath(), blueBorder);
384 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
385 break;
386 case SkClipStack::Element::kEmpty_Type:
387 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
388 break;
389 default:
390 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
391 break;
392 }
393 }
394
395 y += 30;
396 str.printf("Number of clips in stack: %i", total);
397 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
398 }
399
400 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
401 SkPath clipPath;
402 if (clipRegion.getBoundaryPath(&clipPath)) {
403 SkPaint redBorder;
404 redBorder.setColor(SK_ColorRED);
405 redBorder.setStyle(SkPaint::kStroke_Style);
406 canvas.drawPath(clipPath, redBorder);
407 }
408
409 canvas.flush();
410
411 SkString out;
412
413 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
414 // ... and other properties, to be able to debug th code easily
415
416 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
417 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
418 }
edisonn@google.com0f901902013-08-07 11:56:16 +0000419
420 if (ret && token->fType == kKeyword_TokenType && token->fKeyword && token->fKeywordLength > 0 && token->fKeywordLength < 100) {
421 strncpy(gLastKeyword, token->fKeyword, token->fKeywordLength);
422 gLastKeyword[token->fKeywordLength] = '\0';
423 } else {
424 gLastKeyword[0] = '\0';
425 }
426
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000427#endif
428
429 return ret;
430}
431
432
433
edisonn@google.com3aa35552013-08-14 18:26:20 +0000434typedef SkPdfResult (*PdfOperatorRenderer)(SkPdfContext*, SkCanvas*, PdfTokenLooper**);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000435
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000436SkTDict<PdfOperatorRenderer> gPdfOps(100);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000437
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000438
439template <typename T> class SkTDictWithDefaultConstructor : public SkTDict<T> {
440public:
441 SkTDictWithDefaultConstructor() : SkTDict<T>(10) {}
442};
443
edisonn@google.com3aa35552013-08-14 18:26:20 +0000444SkTDictWithDefaultConstructor<int> gRenderStats[kCount_SkPdfResult];
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000445
edisonn@google.com3aa35552013-08-14 18:26:20 +0000446const char* gRenderStatsNames[kCount_SkPdfResult] = {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000447 "Success",
448 "Partially implemented",
449 "Not yet implemented",
450 "Ignore Error",
451 "Error",
452 "Unsupported/Unknown"
453};
454
edisonn@google.com3aa35552013-08-14 18:26:20 +0000455static SkPdfResult DrawText(SkPdfContext* pdfContext,
456 const SkPdfNativeObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000457 SkCanvas* canvas)
458{
459
460 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
461 if (skfont == NULL) {
462 skfont = SkPdfFont::Default();
463 }
464
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000465
edisonn@google.com571c70b2013-07-10 17:09:50 +0000466 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000467 // TODO(edisonn): report warning
edisonn@google.com3aa35552013-08-14 18:26:20 +0000468 return kIgnoreError_SkPdfResult;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000469 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000470 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000471
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000472 SkUnencodedText binary(str);
473
474 SkDecodedText decoded;
475
476 if (skfont->encoding() == NULL) {
477 // TODO(edisonn): report warning
edisonn@google.com3aa35552013-08-14 18:26:20 +0000478 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000479 }
480
481 skfont->encoding()->decodeText(binary, &decoded);
482
483 SkPaint paint;
484 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
485 // Or maybe just not call setTextSize at all?
486 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
487 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
488 }
489
490// if (fCurFont && fCurFont->GetFontScale() != 0) {
491// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
492// }
493
494 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
495
edisonn@google.com6e49c342013-06-27 20:03:43 +0000496 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000497
edisonn@google.com3aa35552013-08-14 18:26:20 +0000498 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000499}
500
501// TODO(edisonn): create header files with declarations!
edisonn@google.com3aa35552013-08-14 18:26:20 +0000502SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
503SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
504SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
505SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000506
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000507// TODO(edisonn): perf!!!
508
509static SkColorTable* getGrayColortable() {
510 static SkColorTable* grayColortable = NULL;
511 if (grayColortable == NULL) {
512 SkPMColor* colors = new SkPMColor[256];
513 for (int i = 0 ; i < 256; i++) {
514 colors[i] = SkPreMultiplyARGB(255, i, i, i);
515 }
516 grayColortable = new SkColorTable(colors, 256);
517 }
518 return grayColortable;
519}
520
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000521static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream, size_t uncompressedStreamLength,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000522 int width, int height, int bytesPerLine,
edisonn@google.com063d7072013-08-16 15:05:08 +0000523 int bpc, const SkString& colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000524 bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000525 SkBitmap* bitmap = new SkBitmap();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000526
edisonn@google.com571c70b2013-07-10 17:09:50 +0000527 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000528//#define MAX_COMPONENTS 10
529
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000530 // TODO(edisonn): assume start of lines are aligned at 32 bits?
531 // Is there a faster way to load the uncompressed stream into a bitmap?
532
533 // minimal support for now
edisonn@google.com063d7072013-08-16 15:05:08 +0000534 if ((colorSpace.equals("DeviceRGB") || colorSpace.equals("RGB")) && bpc == 8) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000535 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
536
537 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000538 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000539 for (int w = 0 ; w < width; w++) {
540 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
541 uncompressedStream[3 * w + 1],
542 uncompressedStream[3 * w + 2]);
543 i++;
544 }
545 uncompressedStream += bytesPerLine;
546 }
547
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000548 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
549 bitmap->setPixels(uncompressedStreamArgb);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000550 }
edisonn@google.com063d7072013-08-16 15:05:08 +0000551 else if ((colorSpace.equals("DeviceGray") || colorSpace.equals("Gray")) && bpc == 8) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000552 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
553
554 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000555 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000556 for (int w = 0 ; w < width; w++) {
557 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
558 uncompressedStream[w];
559 i++;
560 }
561 uncompressedStream += bytesPerLine;
562 }
563
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000564 bitmap->setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000565 width, height);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000566 bitmap->setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000567 }
568
569 // TODO(edisonn): Report Warning, NYI, or error
570 return bitmap;
571}
572
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000573// utils
574
575// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
576// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
577// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
578// skia format, through a table
579
580// this functions returns the image, it does not look at the smask.
581
edisonn@google.com3aa35552013-08-14 18:26:20 +0000582static SkBitmap* getImageFromObjectCore(SkPdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000583 if (image == NULL || !image->hasStream()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000584 // TODO(edisonn): report warning to be used in testing.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000585 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000586 }
587
edisonn@google.com33f11b62013-08-14 21:35:27 +0000588 int bpc = (int)image->BitsPerComponent(pdfContext->fPdfDoc);
589 int width = (int)image->Width(pdfContext->fPdfDoc);
590 int height = (int)image->Height(pdfContext->fPdfDoc);
edisonn@google.com063d7072013-08-16 15:05:08 +0000591 SkString colorSpace("DeviceRGB");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000592
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000593 bool indexed = false;
594 SkPMColor colors[256];
595 int cnt = 0;
596
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000597 // TODO(edisonn): color space can be an array too!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000598 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
599 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000600 } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) {
601 SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc);
602 if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") &&
603 (array->objAtAIndex(1)->isName("DeviceRGB") || array->objAtAIndex(1)->isName("RGB")) &&
604 array->objAtAIndex(2)->isInteger() &&
605 array->objAtAIndex(3)->isHexString()
606 ) {
607 // TODO(edisonn): suport only DeviceRGB for now.
608 indexed = true;
edisonn@google.com33f11b62013-08-14 21:35:27 +0000609 cnt = (int)array->objAtAIndex(2)->intValue() + 1;
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000610 if (cnt > 256) {
611 // TODO(edionn): report NYIs
612 return NULL;
613 }
614 SkColorTable colorTable(cnt);
615 NotOwnedString data = array->objAtAIndex(3)->strRef();
616 if (data.fBytes != (unsigned int)cnt * 3) {
617 // TODO(edionn): report error/warning
618 return NULL;
619 }
620 for (int i = 0 ; i < cnt; i++) {
621 colors[i] = SkPreMultiplyARGB(0xff, data.fBuffer[3 * i], data.fBuffer[3 * i + 1], data.fBuffer[3 * i + 2]);
622 }
623 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000624 }
625
626/*
627 bool imageMask = image->imageMask();
628
629 if (imageMask) {
630 if (bpc != 0 && bpc != 1) {
631 // TODO(edisonn): report warning to be used in testing.
632 return SkBitmap();
633 }
634 bpc = 1;
635 }
636*/
637
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000638 const unsigned char* uncompressedStream = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000639 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000640
edisonn@google.com571c70b2013-07-10 17:09:50 +0000641 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000642
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000643 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000644 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000645 // TODO(edisonn): report warning to be used in testing.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000646 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000647 }
648
edisonn@google.com571c70b2013-07-10 17:09:50 +0000649 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
650
651 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
edisonn@google.com063d7072013-08-16 15:05:08 +0000652 streamDict->getFilterAsName(NULL).equals("DCTDecode")) ||
edisonn@google.com571c70b2013-07-10 17:09:50 +0000653 (streamDict->isFilterAArray(NULL) &&
654 streamDict->getFilterAsArray(NULL)->size() > 0 &&
655 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
edisonn@google.com063d7072013-08-16 15:05:08 +0000656 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2().equals("DCTDecode")))) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000657 SkBitmap* bitmap = new SkBitmap();
658 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000659 return bitmap;
660 }
661
662
663
664 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
665// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
666// obj.GetDictionary().GetKey(PdfName("Filter")));
667// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
668// value = resolveReferenceObject(pdfContext->fPdfDoc,
669// &value->GetArray()[0]);
670// }
671// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
672// SkStream stream = SkStream::
673// SkImageDecoder::Factory()
674// }
675
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000676 // TODO(edisonn): assumes RGB for now, since it is the only onwe implemented
677 if (indexed) {
678 SkBitmap* bitmap = new SkBitmap();
679 bitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
680 SkColorTable* colorTable = new SkColorTable(colors, cnt);
681 bitmap->setPixels((void*)uncompressedStream, colorTable);
682 return bitmap;
683 }
684
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000685 int bytesPerLine = (int)(uncompressedStreamLength / height);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000686#ifdef PDF_TRACE
687 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000688 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000689 }
690#endif
691
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000692 SkBitmap* bitmap = transferImageStreamToBitmap(
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000693 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000694 (int)width, (int)height, bytesPerLine,
695 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000696 transparencyMask);
697
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000698 return bitmap;
699}
700
edisonn@google.com3aa35552013-08-14 18:26:20 +0000701static SkBitmap* getImageFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000702 if (!transparencyMask) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000703 if (!image->hasData(SkPdfNativeObject::kBitmap_Data)) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000704 SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000705 image->setData(bitmap, SkPdfNativeObject::kBitmap_Data);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000706 }
edisonn@google.com3aa35552013-08-14 18:26:20 +0000707 return (SkBitmap*) image->data(SkPdfNativeObject::kBitmap_Data);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000708 } else {
709 return getImageFromObjectCore(pdfContext, image, transparencyMask);
710 }
711}
712
edisonn@google.com3aa35552013-08-14 18:26:20 +0000713static SkBitmap* getSmaskFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000714 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000715
716 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000717 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000718 }
719
720 // TODO(edisonn): implement GS SMask. Default to empty right now.
721 return pdfContext->fGraphicsState.fSMask;
722}
723
edisonn@google.com3aa35552013-08-14 18:26:20 +0000724static SkPdfResult doXObject_Image(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000725 if (skpdfimage == NULL) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000726 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000727 }
728
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000729 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
730 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000731
732 canvas->save();
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000733 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000734
edisonn@google.com571c70b2013-07-10 17:09:50 +0000735 SkScalar z = SkIntToScalar(0);
736 SkScalar one = SkIntToScalar(1);
737
738 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
739 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
740 SkMatrix flip;
741 SkAssertResult(flip.setPolyToPoly(from, to, 4));
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000742 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000743 solveImageFlip.preConcat(flip);
744 canvas->setMatrix(solveImageFlip);
edisonn@google.com0f901902013-08-07 11:56:16 +0000745
746#ifdef PDF_TRACE
747 SkPoint final[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
748 solveImageFlip.mapPoints(final, 4);
749 printf("IMAGE rect = ");
750 for (int i = 0; i < 4; i++) {
751 printf("(%f %f) ", SkScalarToDouble(final[i].x()), SkScalarToDouble(final[i].y()));
752 }
753 printf("\n");
754#endif // PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +0000755
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000756 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
757
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000758 // TODO(edisonn): soft mask type? alpha/luminosity.
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000759 SkPaint paint;
760 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
761
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000762 if (!sMask || sMask->empty()) {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000763 canvas->drawBitmapRect(*image, dst, &paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000764 } else {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000765 canvas->saveLayer(&dst, &paint);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000766 canvas->drawBitmapRect(*image, dst, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000767 SkPaint xfer;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000768 // TODO(edisonn): is the blend mode specified already implicitly/explicitly in pdf?
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000769 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000770 canvas->drawBitmapRect(*sMask, dst, &xfer);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000771 canvas->restore();
772 }
773
774 canvas->restore();
775
edisonn@google.com3aa35552013-08-14 18:26:20 +0000776 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000777}
778
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000779//TODO(edisonn): options for implementing isolation and knockout
780// 1) emulate them (current solution)
781// PRO: simple
782// CON: will need to use readPixels, which means serious perf issues
783// 2) Compile a plan for an array of matrixes, compose the result at the end
784// PRO: might be faster then 1, no need to readPixels
785// CON: multiple drawings (but on smaller areas), pay a price at loading pdf to compute a pdf draw plan
786// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
787// 3) support them natively in SkCanvas
788// PRO: simple
789// CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1)
790// 4) compile a plan using pathops, and render once without any fancy rules with backdrop
791// PRO: simple, fast
792// CON: pathops must be bug free first + time to compute new paths
793// pay a price at loading pdf to compute a pdf draw plan
794// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
edisonn@google.com251176e2013-08-01 13:24:00 +0000795// 5) for knockout, render the objects in reverse order, and add every object to the clip, and any new draw will be cliped
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000796
797
798// TODO(edisonn): draw plan from point! - list of draw ops of a point, like a tree!
799// TODO(edisonn): Minimal PDF to draw some points - remove everything that it is not needed, save pdf uncompressed
800
801
802
edisonn@google.com3aa35552013-08-14 18:26:20 +0000803static void doGroup_before(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup, bool page) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000804 SkRect bboxOrig = bbox;
805 SkBitmap backdrop;
806 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
807// bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000808 SkPaint paint;
809 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
810 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000811}
812
edisonn@google.com251176e2013-08-01 13:24:00 +0000813// TODO(edisonn): non isolation implemented in skia
edisonn@google.com3aa35552013-08-14 18:26:20 +0000814//static void doGroup_after(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup) {
edisonn@google.com251176e2013-08-01 13:24:00 +0000815// if not isolated
816// canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000817//}
818
edisonn@google.com3aa35552013-08-14 18:26:20 +0000819static SkPdfResult doXObject_Form(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000820 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000821 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000822 }
823
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000824 if (!skobj->has_BBox()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000825 return kIgnoreError_SkPdfResult;
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000826 }
827
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000828 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000829
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000830
edisonn@google.com571c70b2013-07-10 17:09:50 +0000831 if (skobj->Resources(pdfContext->fPdfDoc)) {
832 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000833 }
834
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000835 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000836
edisonn@google.com571c70b2013-07-10 17:09:50 +0000837 if (skobj->has_Matrix()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000838 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000839 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
840 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
841 pdfContext->fGraphicsState.fMatrixTm = matrix;
842 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000843 // TODO(edisonn) reset matrixTm and matricTlm also?
844 }
845
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000846 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com0f901902013-08-07 11:56:16 +0000847 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000848
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000849 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000850
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000851 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
852 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000853
854 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
855 // For this PdfContentsTokenizer needs to be extended.
856
edisonn@google.come878e722013-07-29 19:10:58 +0000857 // This is a group?
858 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000859 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
860 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
edisonn@google.come878e722013-07-29 19:10:58 +0000861 }
862
edisonn@google.com571c70b2013-07-10 17:09:50 +0000863 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000864
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000865 SkPdfNativeTokenizer* tokenizer =
866 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000867 if (tokenizer != NULL) {
868 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
869 looper.loop();
870 delete tokenizer;
871 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000872
873 // TODO(edisonn): should we restore the variable stack at the same state?
874 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000875
876 if (skobj->has_Group()) {
877 canvas->restore();
878 }
879
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000880 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000881 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000882}
883
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000884
885// TODO(edisonn): Extract a class like ObjWithStream
edisonn@google.com3aa35552013-08-14 18:26:20 +0000886static SkPdfResult doXObject_Pattern(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfType1PatternDictionary* skobj) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000887 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000888 return kIgnoreError_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000889 }
890
891 if (!skobj->has_BBox()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000892 return kIgnoreError_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000893 }
894
895 PdfOp_q(pdfContext, canvas, NULL);
896
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000897
898 if (skobj->Resources(pdfContext->fPdfDoc)) {
899 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
900 }
901
edisonn@google.com0f901902013-08-07 11:56:16 +0000902 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000903
904 if (skobj->has_Matrix()) {
edisonn@google.com0f901902013-08-07 11:56:16 +0000905 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000906 }
907
edisonn@google.com0f901902013-08-07 11:56:16 +0000908 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000909
edisonn@google.com0f901902013-08-07 11:56:16 +0000910 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix);
911 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000912
913 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
914 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
915
916 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
917 // For this PdfContentsTokenizer needs to be extended.
918
919 SkPdfStream* stream = (SkPdfStream*)skobj;
920
921 SkPdfNativeTokenizer* tokenizer =
922 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
923 if (tokenizer != NULL) {
924 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
925 looper.loop();
926 delete tokenizer;
927 }
928
929 // TODO(edisonn): should we restore the variable stack at the same state?
930 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
931
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000932 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000933 return kPartial_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000934}
935
936
edisonn@google.com3aa35552013-08-14 18:26:20 +0000937//static SkPdfResult doXObject_PS(SkPdfContext* pdfContext, SkCanvas* canvas, const SkPdfNativeObject* obj) {
938// return kNYI_SkPdfResult;
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000939//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000940
edisonn@google.com3aa35552013-08-14 18:26:20 +0000941SkPdfResult doType3Char(SkPdfContext* pdfContext, SkCanvas* canvas, const SkPdfNativeObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000942 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000943 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000944 }
945
946 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000947
948 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
949 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000950 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000951
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000952 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.come57c62d2013-08-07 18:04:15 +0000953 pdfContext->fGraphicsState.fCTM.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000954
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000955 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000956
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000957 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000958
959 SkRect rm = bBox;
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000960 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000961
962 SkTraceRect(rm, "bbox mapped");
963
964 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
965
966 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
967 // For this PdfContentsTokenizer needs to be extended.
968
edisonn@google.com571c70b2013-07-10 17:09:50 +0000969 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000970
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000971 SkPdfNativeTokenizer* tokenizer =
972 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000973 if (tokenizer != NULL) {
974 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
975 looper.loop();
976 delete tokenizer;
977 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000978
979 // TODO(edisonn): should we restore the variable stack at the same state?
980 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000981 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000982
edisonn@google.com3aa35552013-08-14 18:26:20 +0000983 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000984}
985
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000986class CheckRecursiveRendering {
edisonn@google.com063d7072013-08-16 15:05:08 +0000987 SkPdfNativeObject* fObj;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000988public:
edisonn@google.com063d7072013-08-16 15:05:08 +0000989 CheckRecursiveRendering(SkPdfNativeObject* obj) : fObj(obj) {
990 SkASSERT(!obj->inRendering());
991 obj->startRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000992 }
993
994 ~CheckRecursiveRendering() {
edisonn@google.com063d7072013-08-16 15:05:08 +0000995 SkASSERT(fObj->inRendering());
996 fObj->doneRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000997 }
998
edisonn@google.com3aa35552013-08-14 18:26:20 +0000999 static bool IsInRendering(const SkPdfNativeObject* obj) {
edisonn@google.com063d7072013-08-16 15:05:08 +00001000 return obj->inRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001001 }
1002};
1003
edisonn@google.com063d7072013-08-16 15:05:08 +00001004static SkPdfResult doXObject(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfNativeObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001005 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001006 // Oops, corrupt PDF!
edisonn@google.com3aa35552013-08-14 18:26:20 +00001007 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001008 }
1009
edisonn@google.com571c70b2013-07-10 17:09:50 +00001010 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001011
edisonn@google.com571c70b2013-07-10 17:09:50 +00001012 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001013 {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001014 case kImageDictionary_SkPdfNativeObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001015 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001016 case kType1FormDictionary_SkPdfNativeObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001017 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001018 //case kObjectDictionaryXObjectPS_SkPdfNativeObjectType:
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001019 //return doXObject_PS(skxobj.asPS());
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001020 default: {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001021 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != kNone_SkPdfNativeObjectType) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001022 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
1023 return doXObject_Pattern(pdfContext, canvas, pattern);
1024 }
1025 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001026 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001027 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001028}
1029
edisonn@google.com3aa35552013-08-14 18:26:20 +00001030static SkPdfResult doPage(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfPageObjectDictionary* skobj) {
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001031 if (!skobj) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001032 return kIgnoreError_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001033 }
1034
1035 if (!skobj->isContentsAStream(pdfContext->fPdfDoc)) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001036 return kNYI_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001037 }
1038
1039 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
1040
1041 if (!stream) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001042 return kIgnoreError_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001043 }
1044
edisonn@google.com73613c12013-08-22 15:48:35 +00001045 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
1046
1047 if (!pdfContext->fGraphicsState.fResources) {
1048 return kIgnoreError_SkPdfResult; // probably it is null because we have not implemented yet inheritance
1049 }
1050
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001051 if (CheckRecursiveRendering::IsInRendering(skobj)) {
1052 // Oops, corrupt PDF!
edisonn@google.com3aa35552013-08-14 18:26:20 +00001053 return kIgnoreError_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001054 }
1055 CheckRecursiveRendering checkRecursion(skobj);
1056
1057
1058 PdfOp_q(pdfContext, canvas, NULL);
1059
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001060 // TODO(edisonn): MediaBox can be inherited!!!!
1061 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001062 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001063 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
1064 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
1065 } else {
1066 canvas->save();
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001067 }
1068
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001069
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001070 SkPdfNativeTokenizer* tokenizer =
1071 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
1072 if (tokenizer != NULL) {
1073 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
1074 looper.loop();
1075 delete tokenizer;
1076 }
1077
1078 // TODO(edisonn): should we restore the variable stack at the same state?
1079 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1080 canvas->restore();
1081 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001082 return kPartial_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001083}
1084
edisonn@google.com3aa35552013-08-14 18:26:20 +00001085SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001086 // TODO(edisonn): create a new stack of parameters, so once we start a q,
1087 // it is not possible to see under the previous q?
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001088 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1089 canvas->save();
edisonn@google.comf68aed32013-08-22 15:37:21 +00001090 pdfContext->fObjectStack.nest();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001091 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001092}
1093
edisonn@google.com3aa35552013-08-14 18:26:20 +00001094SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comf68aed32013-08-22 15:37:21 +00001095 if (pdfContext->fStateStack.count() > 0) {
1096 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1097 pdfContext->fStateStack.pop();
1098 canvas->restore();
1099
1100 if (pdfContext->fObjectStack.nests() <= 0) {
1101 return kIgnoreError_SkPdfResult;
1102 } else {
1103 pdfContext->fObjectStack.unnest();
1104 }
1105 } else {
1106 return kIgnoreError_SkPdfResult;
1107 }
1108
edisonn@google.com3aa35552013-08-14 18:26:20 +00001109 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001110}
1111
edisonn@google.com3aa35552013-08-14 18:26:20 +00001112static SkPdfResult PdfOp_cm(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001113 EXPECT_OPERANDS(pdfContext, 6);
1114 POP_NUMBER(pdfContext, f);
1115 POP_NUMBER(pdfContext, e);
1116 POP_NUMBER(pdfContext, d);
1117 POP_NUMBER(pdfContext, c);
1118 POP_NUMBER(pdfContext, b);
1119 POP_NUMBER(pdfContext, a);
1120 CHECK_PARAMETERS();
1121 double array[6] = {a, b, c, d, e, f};
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001122
1123 // a b
1124 // c d
1125 // e f
1126
1127 // 0 1
1128 // 2 3
1129 // 4 5
1130
1131 // sx ky
1132 // kx sy
1133 // tx ty
1134 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1135
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001136 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001137
1138#ifdef PDF_TRACE
1139 printf("cm ");
1140 for (int i = 0 ; i < 6 ; i++) {
1141 printf("%f ", array[i]);
1142 }
1143 printf("\n");
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001144 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001145#endif
1146
edisonn@google.com3aa35552013-08-14 18:26:20 +00001147 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001148}
1149
1150//leading TL Set the text leading, Tl
1151//, to leading, which is a number expressed in unscaled text
1152//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00001153static SkPdfResult PdfOp_TL(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001154 EXPECT_OPERANDS(pdfContext, 1)
1155 POP_NUMBER(pdfContext, ty);
1156 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001157
1158 pdfContext->fGraphicsState.fTextLeading = ty;
1159
edisonn@google.com3aa35552013-08-14 18:26:20 +00001160 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001161}
1162
edisonn@google.com3aa35552013-08-14 18:26:20 +00001163static SkPdfResult PdfOp_Td(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001164 EXPECT_OPERANDS(pdfContext, 2);
1165 POP_NUMBER(pdfContext, ty);
1166 POP_NUMBER(pdfContext, tx);
1167 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001168
edisonn@google.come57c62d2013-08-07 18:04:15 +00001169 double array[6] = {1, 0, 0, 1, tx, -ty};
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001170 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1171
1172 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1173 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1174
edisonn@google.com3aa35552013-08-14 18:26:20 +00001175 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001176}
1177
edisonn@google.com3aa35552013-08-14 18:26:20 +00001178static SkPdfResult PdfOp_TD(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001179 EXPECT_OPERANDS(pdfContext, 2)
1180 POP_NUMBER(pdfContext, ty);
1181 POP_NUMBER(pdfContext, tx);
1182 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001183
edisonn@google.com571c70b2013-07-10 17:09:50 +00001184 // TODO(edisonn): Create factory methods or constructors so native is hidden
1185 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001186 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001187
1188 PdfOp_TL(pdfContext, canvas, looper);
1189
edisonn@google.com571c70b2013-07-10 17:09:50 +00001190 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001191 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001192
edisonn@google.com571c70b2013-07-10 17:09:50 +00001193 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001194 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001195
edisonn@google.com3aa35552013-08-14 18:26:20 +00001196 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001197
1198 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
1199 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001200
1201 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001202}
1203
edisonn@google.com3aa35552013-08-14 18:26:20 +00001204static SkPdfResult PdfOp_Tm(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001205 EXPECT_OPERANDS(pdfContext, 6);
1206 POP_NUMBER(pdfContext, f);
1207 POP_NUMBER(pdfContext, e);
1208 POP_NUMBER(pdfContext, d);
1209 POP_NUMBER(pdfContext, c);
1210 POP_NUMBER(pdfContext, b);
1211 POP_NUMBER(pdfContext, a);
1212 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001213
1214 double array[6];
1215 array[0] = a;
1216 array[1] = b;
1217 array[2] = c;
1218 array[3] = d;
1219 array[4] = e;
1220 array[5] = f;
1221
1222 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001223 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
edisonn@google.come57c62d2013-08-07 18:04:15 +00001224 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001225
1226 // TODO(edisonn): Text positioning.
1227 pdfContext->fGraphicsState.fMatrixTm = matrix;
1228 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1229
edisonn@google.com3aa35552013-08-14 18:26:20 +00001230 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001231}
1232
1233//— T* Move to the start of the next line. This operator has the same effect as the code
1234//0 Tl Td
1235//where Tl is the current leading parameter in the text state
edisonn@google.com3aa35552013-08-14 18:26:20 +00001236static SkPdfResult PdfOp_T_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001237 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
1238 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001239
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001240 pdfContext->fObjectStack.push(zero);
1241 pdfContext->fObjectStack.push(tl);
1242
edisonn@google.com3aa35552013-08-14 18:26:20 +00001243 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001244
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001245 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001246}
1247
edisonn@google.com3aa35552013-08-14 18:26:20 +00001248static SkPdfResult PdfOp_m(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001249 if (pdfContext->fGraphicsState.fPathClosed) {
1250 pdfContext->fGraphicsState.fPath.reset();
1251 pdfContext->fGraphicsState.fPathClosed = false;
1252 }
1253
edisonn@google.combd2f3012013-08-22 14:18:04 +00001254 EXPECT_OPERANDS(pdfContext, 2);
1255 POP_NUMBER(pdfContext, y);
1256 POP_NUMBER(pdfContext, x);
1257 CHECK_PARAMETERS();
1258
1259 pdfContext->fGraphicsState.fCurPosY = y;
1260 pdfContext->fGraphicsState.fCurPosX = x;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001261
1262 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001263 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001264
edisonn@google.com3aa35552013-08-14 18:26:20 +00001265 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001266}
1267
edisonn@google.com3aa35552013-08-14 18:26:20 +00001268static SkPdfResult PdfOp_l(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001269 if (pdfContext->fGraphicsState.fPathClosed) {
1270 pdfContext->fGraphicsState.fPath.reset();
1271 pdfContext->fGraphicsState.fPathClosed = false;
1272 }
1273
edisonn@google.combd2f3012013-08-22 14:18:04 +00001274 EXPECT_OPERANDS(pdfContext, 2);
1275 POP_NUMBER(pdfContext, y);
1276 POP_NUMBER(pdfContext, x);
1277 CHECK_PARAMETERS();
1278
1279 pdfContext->fGraphicsState.fCurPosY = y;
1280 pdfContext->fGraphicsState.fCurPosX = x;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001281
1282 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001283 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001284
edisonn@google.com3aa35552013-08-14 18:26:20 +00001285 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001286}
1287
edisonn@google.com3aa35552013-08-14 18:26:20 +00001288static SkPdfResult PdfOp_c(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001289 if (pdfContext->fGraphicsState.fPathClosed) {
1290 pdfContext->fGraphicsState.fPath.reset();
1291 pdfContext->fGraphicsState.fPathClosed = false;
1292 }
1293
edisonn@google.combd2f3012013-08-22 14:18:04 +00001294 EXPECT_OPERANDS(pdfContext, 6);
1295 POP_NUMBER(pdfContext, y3);
1296 POP_NUMBER(pdfContext, x3);
1297 POP_NUMBER(pdfContext, y2);
1298 POP_NUMBER(pdfContext, x2);
1299 POP_NUMBER(pdfContext, y1);
1300 POP_NUMBER(pdfContext, x1);
1301 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001302
1303 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001304 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1305 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001306
1307 pdfContext->fGraphicsState.fCurPosX = x3;
1308 pdfContext->fGraphicsState.fCurPosY = y3;
1309
edisonn@google.com3aa35552013-08-14 18:26:20 +00001310 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001311}
1312
edisonn@google.com3aa35552013-08-14 18:26:20 +00001313static SkPdfResult PdfOp_v(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001314 if (pdfContext->fGraphicsState.fPathClosed) {
1315 pdfContext->fGraphicsState.fPath.reset();
1316 pdfContext->fGraphicsState.fPathClosed = false;
1317 }
1318
edisonn@google.combd2f3012013-08-22 14:18:04 +00001319 EXPECT_OPERANDS(pdfContext, 4);
1320 POP_NUMBER(pdfContext, y3);
1321 POP_NUMBER(pdfContext, x3);
1322 POP_NUMBER(pdfContext, y2);
1323 POP_NUMBER(pdfContext, x2);
1324 CHECK_PARAMETERS();
1325
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001326 double y1 = pdfContext->fGraphicsState.fCurPosY;
1327 double x1 = pdfContext->fGraphicsState.fCurPosX;
1328
1329 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001330 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1331 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001332
1333 pdfContext->fGraphicsState.fCurPosX = x3;
1334 pdfContext->fGraphicsState.fCurPosY = y3;
1335
edisonn@google.com3aa35552013-08-14 18:26:20 +00001336 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001337}
1338
edisonn@google.com3aa35552013-08-14 18:26:20 +00001339static SkPdfResult PdfOp_y(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001340 if (pdfContext->fGraphicsState.fPathClosed) {
1341 pdfContext->fGraphicsState.fPath.reset();
1342 pdfContext->fGraphicsState.fPathClosed = false;
1343 }
1344
edisonn@google.combd2f3012013-08-22 14:18:04 +00001345 EXPECT_OPERANDS(pdfContext, 4);
1346 POP_NUMBER(pdfContext, y3);
1347 POP_NUMBER(pdfContext, x3);
1348 POP_NUMBER(pdfContext, y1);
1349 POP_NUMBER(pdfContext, x1);
1350 CHECK_PARAMETERS();
1351
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001352 double y2 = pdfContext->fGraphicsState.fCurPosY;
1353 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001354
1355 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001356 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1357 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001358
1359 pdfContext->fGraphicsState.fCurPosX = x3;
1360 pdfContext->fGraphicsState.fCurPosY = y3;
1361
edisonn@google.com3aa35552013-08-14 18:26:20 +00001362 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001363}
1364
edisonn@google.com3aa35552013-08-14 18:26:20 +00001365static SkPdfResult PdfOp_re(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001366 if (pdfContext->fGraphicsState.fPathClosed) {
1367 pdfContext->fGraphicsState.fPath.reset();
1368 pdfContext->fGraphicsState.fPathClosed = false;
1369 }
1370
edisonn@google.combd2f3012013-08-22 14:18:04 +00001371 EXPECT_OPERANDS(pdfContext, 4);
1372 POP_NUMBER(pdfContext, height);
1373 POP_NUMBER(pdfContext, width);
1374 POP_NUMBER(pdfContext, y);
1375 POP_NUMBER(pdfContext, x);
1376 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001377
1378 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001379 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001380
1381 pdfContext->fGraphicsState.fCurPosX = x;
1382 pdfContext->fGraphicsState.fCurPosY = y + height;
1383
edisonn@google.com3aa35552013-08-14 18:26:20 +00001384 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001385}
1386
edisonn@google.com3aa35552013-08-14 18:26:20 +00001387static SkPdfResult PdfOp_h(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001388 pdfContext->fGraphicsState.fPath.close();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001389 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001390}
1391
edisonn@google.com3aa35552013-08-14 18:26:20 +00001392static SkPdfResult PdfOp_fillAndStroke(SkPdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001393 SkPath path = pdfContext->fGraphicsState.fPath;
1394
1395 if (close) {
1396 path.close();
1397 }
1398
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001399 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001400
1401 SkPaint paint;
1402
1403 SkPoint line[2];
1404 if (fill && !stroke && path.isLine(line)) {
1405 paint.setStyle(SkPaint::kStroke_Style);
1406
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001407 // TODO(edisonn): implement this with patterns
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001408 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1409 paint.setStrokeWidth(SkDoubleToScalar(0));
1410
1411 canvas->drawPath(path, paint);
1412 } else {
1413 if (fill) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001414 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0 &&
1415 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1416
1417 // TODO(edisonn): we can use a shader here, like imageshader to draw fast. ultimately,
1418 // if this is not possible, and we are in rasper mode, and the cells don't intersect, we could even have multiple cpus.
1419
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001420 PdfOp_q(pdfContext, canvas, NULL);
1421
1422 if (evenOdd) {
1423 path.setFillType(SkPath::kEvenOdd_FillType);
1424 }
1425 canvas->clipPath(path);
1426
edisonn@google.com3aa35552013-08-14 18:26:20 +00001427 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(pdfContext->fGraphicsState.fNonStroking.fPattern) != kNone_SkPdfNativeObjectType) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001428 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState.fNonStroking.fPattern;
1429
1430 // TODO(edisonn): constants
1431 // TODO(edisonn): colored
1432 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001433 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
1434 // it will change the result iterating in reverse
1435 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
1436 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001437
1438 SkRect bounds = path.getBounds();
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001439
1440 // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001441 // TODO(edisonn): don't do that!
1442 bounds.sort();
1443
1444 SkScalar x;
1445 SkScalar y;
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001446
1447 y = bounds.top();
1448 int totalx = 0;
1449 int totaly = 0;
1450 while (y < bounds.bottom()) {
1451 x = bounds.left();
1452 totalx = 0;
1453
1454 while (x < bounds.right()) {
1455 doXObject(pdfContext, canvas, pattern);
1456
edisonn@google.com0f901902013-08-07 11:56:16 +00001457 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(xStep), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001458 totalx += xStep;
1459 x += SkIntToScalar(xStep);
1460 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001461 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(-totalx), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001462
edisonn@google.com0f901902013-08-07 11:56:16 +00001463 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(-yStep));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001464 totaly += yStep;
1465 y += SkIntToScalar(yStep);
1466 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001467 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(totaly));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001468 }
1469 }
1470
1471 // apply matrix
1472 // get xstep, y step, bbox ... for cliping, and bos of the path
1473
1474 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001475 } else {
1476 paint.setStyle(SkPaint::kFill_Style);
1477 if (evenOdd) {
1478 path.setFillType(SkPath::kEvenOdd_FillType);
1479 }
1480
1481 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1482
1483 canvas->drawPath(path, paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001484 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001485 }
1486
1487 if (stroke) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001488 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0) {
1489 // TODO(edisonn): implement Pattern for strokes
1490 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001491
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001492 paint.setColor(SK_ColorGREEN);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001493
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001494 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1495 canvas->drawPath(path, paint);
1496 } else {
1497 paint.setStyle(SkPaint::kStroke_Style);
1498
1499 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1500
1501 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1502 canvas->drawPath(path, paint);
1503 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001504 }
1505 }
1506
1507 pdfContext->fGraphicsState.fPath.reset();
1508 // todo zoom ... other stuff ?
1509
1510 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1511#ifndef PDF_DEBUG_NO_CLIPING
1512 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1513#endif
1514 }
1515
1516 //pdfContext->fGraphicsState.fClipPath.reset();
1517 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1518
edisonn@google.com3aa35552013-08-14 18:26:20 +00001519 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001520
1521}
1522
edisonn@google.com3aa35552013-08-14 18:26:20 +00001523static SkPdfResult PdfOp_S(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001524 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1525}
1526
edisonn@google.com3aa35552013-08-14 18:26:20 +00001527static SkPdfResult PdfOp_s(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001528 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1529}
1530
edisonn@google.com3aa35552013-08-14 18:26:20 +00001531static SkPdfResult PdfOp_F(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001532 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1533}
1534
edisonn@google.com3aa35552013-08-14 18:26:20 +00001535static SkPdfResult PdfOp_f(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001536 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1537}
1538
edisonn@google.com3aa35552013-08-14 18:26:20 +00001539static SkPdfResult PdfOp_f_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001540 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1541}
1542
edisonn@google.com3aa35552013-08-14 18:26:20 +00001543static SkPdfResult PdfOp_B(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001544 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1545}
1546
edisonn@google.com3aa35552013-08-14 18:26:20 +00001547static SkPdfResult PdfOp_B_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001548 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1549}
1550
edisonn@google.com3aa35552013-08-14 18:26:20 +00001551static SkPdfResult PdfOp_b(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001552 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1553}
1554
edisonn@google.com3aa35552013-08-14 18:26:20 +00001555static SkPdfResult PdfOp_b_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001556 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1557}
1558
edisonn@google.com3aa35552013-08-14 18:26:20 +00001559static SkPdfResult PdfOp_n(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001560 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001561 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1562#ifndef PDF_DEBUG_NO_CLIPING
1563 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1564#endif
1565 }
1566
1567 //pdfContext->fGraphicsState.fClipPath.reset();
1568 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1569
1570 pdfContext->fGraphicsState.fPathClosed = true;
1571
edisonn@google.com3aa35552013-08-14 18:26:20 +00001572 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001573}
1574
edisonn@google.com3aa35552013-08-14 18:26:20 +00001575static SkPdfResult PdfOp_BT(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001576 pdfContext->fGraphicsState.fTextBlock = true;
edisonn@google.come57c62d2013-08-07 18:04:15 +00001577 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
1578 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
1579 pdfContext->fGraphicsState.fMatrixTm = matrix;
1580 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001581
edisonn@google.com3aa35552013-08-14 18:26:20 +00001582 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001583}
1584
edisonn@google.com3aa35552013-08-14 18:26:20 +00001585static SkPdfResult PdfOp_ET(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001586 if (!pdfContext->fGraphicsState.fTextBlock) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001587 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001588 }
1589 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
edisonn@google.com3aa35552013-08-14 18:26:20 +00001590 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001591}
1592
edisonn@google.com33f11b62013-08-14 21:35:27 +00001593static SkPdfResult skpdfGraphicsStateApplyFontCore(SkPdfContext* pdfContext, const SkPdfNativeObject* fontName, double fontSize) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001594#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001595 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001596#endif
1597
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001598 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1599 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.com3aa35552013-08-14 18:26:20 +00001600 return kIgnoreError_SkPdfResult;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001601 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001602
edisonn@google.com3aa35552013-08-14 18:26:20 +00001603 SkPdfNativeObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001604 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001605 if (kNone_SkPdfNativeObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001606 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.com3aa35552013-08-14 18:26:20 +00001607 return kIgnoreError_SkPdfResult;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001608 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001609
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001610 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1611
1612 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1613
1614 if (skfont) {
1615 pdfContext->fGraphicsState.fSkFont = skfont;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001616 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001617 pdfContext->fGraphicsState.fCurFontSize = fontSize;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001618 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001619}
1620
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001621//font size Tf Set the text font, Tf
1622//, to font and the text font size, Tfs, to size. font is the name of a
1623//font resource in the Fontsubdictionary of the current resource dictionary; size is
1624//a number representing a scale factor. There is no initial value for either font or
1625//size; they must be specified explicitly using Tf before any text is shown.
edisonn@google.com3aa35552013-08-14 18:26:20 +00001626static SkPdfResult PdfOp_Tf(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001627 EXPECT_OPERANDS(pdfContext, 2);
1628 POP_NUMBER(pdfContext, fontSize);
1629 POP_NAME(pdfContext, fontName);
1630 CHECK_PARAMETERS();
1631
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001632 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1633}
1634
edisonn@google.com3aa35552013-08-14 18:26:20 +00001635static SkPdfResult PdfOp_Tj(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001636 EXPECT_OPERANDS(pdfContext, 1);
1637 POP_STRING(pdfContext, str);
1638 CHECK_PARAMETERS();
1639
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001640 if (!pdfContext->fGraphicsState.fTextBlock) {
1641 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.com3aa35552013-08-14 18:26:20 +00001642 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001643 }
1644
edisonn@google.combd2f3012013-08-22 14:18:04 +00001645 SkPdfResult ret = DrawText(pdfContext, str, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001646
1647 return ret;
1648}
1649
edisonn@google.com3aa35552013-08-14 18:26:20 +00001650static SkPdfResult PdfOp_quote(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001651 if (!pdfContext->fGraphicsState.fTextBlock) {
1652 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.com3aa35552013-08-14 18:26:20 +00001653 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001654 }
1655
1656 PdfOp_T_star(pdfContext, canvas, looper);
1657 // Do not pop, and push, just transfer the param to Tj
1658 return PdfOp_Tj(pdfContext, canvas, looper);
1659}
1660
edisonn@google.com3aa35552013-08-14 18:26:20 +00001661static SkPdfResult PdfOp_doublequote(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001662 if (!pdfContext->fGraphicsState.fTextBlock) {
1663 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.com3aa35552013-08-14 18:26:20 +00001664 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001665 }
1666
edisonn@google.combd2f3012013-08-22 14:18:04 +00001667 EXPECT_OPERANDS(pdfContext, 3);
1668 POP_OBJ(pdfContext, str);
1669 POP_OBJ(pdfContext, ac);
1670 POP_OBJ(pdfContext, aw);
1671 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001672
1673 pdfContext->fObjectStack.push(aw);
1674 PdfOp_Tw(pdfContext, canvas, looper);
1675
1676 pdfContext->fObjectStack.push(ac);
1677 PdfOp_Tc(pdfContext, canvas, looper);
1678
1679 pdfContext->fObjectStack.push(str);
1680 PdfOp_quote(pdfContext, canvas, looper);
1681
edisonn@google.com3aa35552013-08-14 18:26:20 +00001682 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001683}
1684
edisonn@google.com3aa35552013-08-14 18:26:20 +00001685static SkPdfResult PdfOp_TJ(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001686 EXPECT_OPERANDS(pdfContext, 1);
1687 POP_ARRAY(pdfContext, array);
1688 CHECK_PARAMETERS();
1689
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001690 if (!pdfContext->fGraphicsState.fTextBlock) {
1691 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.com3aa35552013-08-14 18:26:20 +00001692 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001693 }
1694
edisonn@google.com571c70b2013-07-10 17:09:50 +00001695 if (!array->isArray()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001696 return kIgnoreError_SkPdfResult;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001697 }
1698
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001699 for( int i=0; i<static_cast<int>(array->size()); i++ )
1700 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001701 if( (*array)[i]->isAnyString()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001702 SkPdfNativeObject* obj = (*array)[i];
edisonn@google.combd2f3012013-08-22 14:18:04 +00001703 DrawText(pdfContext, obj, canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001704 } else if ((*array)[i]->isNumber()) {
1705 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001706 SkMatrix matrix;
1707 matrix.setAll(SkDoubleToScalar(1),
1708 SkDoubleToScalar(0),
1709 // TODO(edisonn): use writing mode, vertical/horizontal.
1710 SkDoubleToScalar(-dx), // amount is substracted!!!
1711 SkDoubleToScalar(0),
1712 SkDoubleToScalar(1),
1713 SkDoubleToScalar(0),
1714 SkDoubleToScalar(0),
1715 SkDoubleToScalar(0),
1716 SkDoubleToScalar(1));
1717
1718 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1719 }
1720 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001721 return kPartial_SkPdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001722}
1723
edisonn@google.com3aa35552013-08-14 18:26:20 +00001724static SkPdfResult PdfOp_CS_cs(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001725 EXPECT_OPERANDS(pdfContext, 1);
1726 POP_NAME(pdfContext, name);
1727 CHECK_PARAMETERS();
edisonn@google.com4f898b72013-08-07 21:11:57 +00001728
1729 //Next, get the ColorSpace Dictionary from the Resource Dictionary:
1730 SkPdfDictionary* colorSpaceResource = pdfContext->fGraphicsState.fResources->ColorSpace(pdfContext->fPdfDoc);
1731
edisonn@google.com3aa35552013-08-14 18:26:20 +00001732 SkPdfNativeObject* colorSpace = colorSpaceResource ? pdfContext->fPdfDoc->resolveReference(colorSpaceResource->get(name)) : name;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001733
1734 if (colorSpace == NULL) {
1735 colorOperator->fColorSpace = name->strRef();
1736 } else {
1737#ifdef PDF_TRACE
1738 printf("CS = %s\n", colorSpace->toString(0, 0).c_str());
1739#endif // PDF_TRACE
1740 if (colorSpace->isName()) {
1741 colorOperator->fColorSpace = colorSpace->strRef();
1742 } else if (colorSpace->isArray()) {
1743 int cnt = colorSpace->size();
1744 if (cnt == 0) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001745 return kIgnoreError_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001746 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001747 SkPdfNativeObject* type = colorSpace->objAtAIndex(0);
edisonn@google.com4f898b72013-08-07 21:11:57 +00001748 type = pdfContext->fPdfDoc->resolveReference(type);
1749
1750 if (type->isName("ICCBased")) {
1751 if (cnt != 2) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001752 return kIgnoreError_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001753 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001754 SkPdfNativeObject* prop = colorSpace->objAtAIndex(1);
edisonn@google.com4f898b72013-08-07 21:11:57 +00001755 prop = pdfContext->fPdfDoc->resolveReference(prop);
1756#ifdef PDF_TRACE
1757 printf("ICCBased prop = %s\n", prop->toString(0, 0).c_str());
1758#endif // PDF_TRACE
1759 // TODO(edisonn): hack
1760 if (prop && prop->isDictionary() && prop->get("N") && prop->get("N")->isInteger() && prop->get("N")->intValue() == 3) {
1761 colorOperator->setColorSpace(&strings_DeviceRGB);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001762 return kPartial_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001763 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001764 return kNYI_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001765 }
1766 }
1767 }
1768
edisonn@google.com3aa35552013-08-14 18:26:20 +00001769 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001770}
1771
edisonn@google.com3aa35552013-08-14 18:26:20 +00001772static SkPdfResult PdfOp_CS(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001773 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1774}
1775
edisonn@google.com3aa35552013-08-14 18:26:20 +00001776static SkPdfResult PdfOp_cs(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001777 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1778}
1779
edisonn@google.com3aa35552013-08-14 18:26:20 +00001780static SkPdfResult PdfOp_SC_sc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001781 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001782// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001783
1784 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1785
1786 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001787 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001788 doubles = false;
1789 }
1790
1791#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001792 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001793#endif
1794
edisonn@google.combd2f3012013-08-22 14:18:04 +00001795 EXPECT_OPERANDS(pdfContext, n);
1796
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001797 for (int i = n - 1; i >= 0 ; i--) {
1798 if (doubles) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001799 POP_NUMBER_INTO(pdfContext, c[i]);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001800// } else {
1801// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001802 }
1803 }
edisonn@google.combd2f3012013-08-22 14:18:04 +00001804 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001805
1806 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001807 // TODO(edisonn): do possible field values to enum at parsing time!
1808 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001809 if (colorOperator->fColorSpace.equals("DeviceRGB") || colorOperator->fColorSpace.equals("RGB")) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001810 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]), (U8CPU)(255*c[1]), (U8CPU)(255*c[2])));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001811 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001812 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001813}
1814
edisonn@google.com3aa35552013-08-14 18:26:20 +00001815static SkPdfResult PdfOp_SC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001816 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1817}
1818
edisonn@google.com3aa35552013-08-14 18:26:20 +00001819static SkPdfResult PdfOp_sc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001820 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1821}
1822
edisonn@google.com3aa35552013-08-14 18:26:20 +00001823static SkPdfResult PdfOp_SCN_scn(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001824 if (pdfContext->fObjectStack.count() > 0 && pdfContext->fObjectStack.top()->isName()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001825 SkPdfNativeObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com276fed92013-08-01 21:20:47 +00001826
1827 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001828 SkPdfDictionary* patternResources = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
edisonn@google.com276fed92013-08-01 21:20:47 +00001829
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001830 if (patternResources == NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001831#ifdef PDF_TRACE
1832 printf("ExtGState is NULL!\n");
1833#endif
edisonn@google.com3aa35552013-08-14 18:26:20 +00001834 return kIgnoreError_SkPdfResult;
edisonn@google.com276fed92013-08-01 21:20:47 +00001835 }
1836
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001837 colorOperator->setPatternColorSpace(pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001838 }
1839
1840 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1841 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1842
edisonn@google.com3aa35552013-08-14 18:26:20 +00001843 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001844}
1845
edisonn@google.com3aa35552013-08-14 18:26:20 +00001846static SkPdfResult PdfOp_SCN(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001847 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1848}
1849
edisonn@google.com3aa35552013-08-14 18:26:20 +00001850static SkPdfResult PdfOp_scn(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001851 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1852}
1853
edisonn@google.com3aa35552013-08-14 18:26:20 +00001854static SkPdfResult PdfOp_G_g(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001855 EXPECT_OPERANDS(pdfContext, 1);
1856 POP_NUMBER(pdfContext, gray);
1857 CHECK_PARAMETERS();
1858
1859 colorOperator->fColorSpace = strings_DeviceRGB; // TODO(edisonn): HACK - it should be device gray, but not suported right now
1860 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*gray), (U8CPU)(255*gray), (U8CPU)(255*gray)));
1861
1862 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001863}
1864
edisonn@google.com3aa35552013-08-14 18:26:20 +00001865static SkPdfResult PdfOp_G(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001866 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1867}
1868
edisonn@google.com3aa35552013-08-14 18:26:20 +00001869static SkPdfResult PdfOp_g(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001870 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1871}
1872
edisonn@google.com3aa35552013-08-14 18:26:20 +00001873static SkPdfResult PdfOp_RG_rg(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001874 EXPECT_OPERANDS(pdfContext, 3);
1875 POP_NUMBER(pdfContext, b);
1876 POP_NUMBER(pdfContext, g);
1877 POP_NUMBER(pdfContext, r);
1878 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001879
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001880 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001881 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
edisonn@google.com3aa35552013-08-14 18:26:20 +00001882 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001883}
1884
edisonn@google.com3aa35552013-08-14 18:26:20 +00001885static SkPdfResult PdfOp_RG(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001886 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1887}
1888
edisonn@google.com3aa35552013-08-14 18:26:20 +00001889static SkPdfResult PdfOp_rg(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001890 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1891}
1892
edisonn@google.com3aa35552013-08-14 18:26:20 +00001893static SkPdfResult PdfOp_K_k(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001894 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.combd2f3012013-08-22 14:18:04 +00001895 EXPECT_OPERANDS(pdfContext, 4);
1896 POP_NUMBER(pdfContext, k);
1897 POP_NUMBER(pdfContext, y);
1898 POP_NUMBER(pdfContext, m);
1899 POP_NUMBER(pdfContext, c);
1900 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001901
edisonn@google.combd2f3012013-08-22 14:18:04 +00001902 // TODO(edisonn): silly way to remove compiler warning
1903 if (k + y + m + c == 0) {
1904 return kNYI_SkPdfResult;
1905 }
1906
1907 //colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001908 // TODO(edisonn): Set color.
edisonn@google.com3aa35552013-08-14 18:26:20 +00001909 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001910}
1911
edisonn@google.com3aa35552013-08-14 18:26:20 +00001912static SkPdfResult PdfOp_K(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001913 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1914}
1915
edisonn@google.com3aa35552013-08-14 18:26:20 +00001916static SkPdfResult PdfOp_k(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001917 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1918}
1919
edisonn@google.com3aa35552013-08-14 18:26:20 +00001920static SkPdfResult PdfOp_W(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001921 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1922 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1923
edisonn@google.com3aa35552013-08-14 18:26:20 +00001924 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001925}
1926
edisonn@google.com3aa35552013-08-14 18:26:20 +00001927static SkPdfResult PdfOp_W_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001928 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1929
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001930 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1931 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1932
edisonn@google.com3aa35552013-08-14 18:26:20 +00001933 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001934}
1935
edisonn@google.com3aa35552013-08-14 18:26:20 +00001936static SkPdfResult PdfOp_BX(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001937 *looper = new PdfCompatibilitySectionLooper();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001938 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001939}
1940
edisonn@google.com3aa35552013-08-14 18:26:20 +00001941static SkPdfResult PdfOp_EX(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001942#ifdef ASSERT_BAD_PDF_OPS
1943 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1944 // have the assert when testing good pdfs.
1945#endif
edisonn@google.com3aa35552013-08-14 18:26:20 +00001946 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001947}
1948
edisonn@google.com3aa35552013-08-14 18:26:20 +00001949static SkPdfResult PdfOp_BI(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001950 *looper = new PdfInlineImageLooper();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001951 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001952}
1953
edisonn@google.com3aa35552013-08-14 18:26:20 +00001954static SkPdfResult PdfOp_ID(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001955#ifdef ASSERT_BAD_PDF_OPS
1956 SkASSERT(false); // must be processed in inline image looper, but let's
1957 // have the assert when testing good pdfs.
1958#endif
edisonn@google.com3aa35552013-08-14 18:26:20 +00001959 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001960}
1961
edisonn@google.com3aa35552013-08-14 18:26:20 +00001962static SkPdfResult PdfOp_EI(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001963#ifdef ASSERT_BAD_PDF_OPS
1964 SkASSERT(false); // must be processed in inline image looper, but let's
1965 // have the assert when testing good pdfs.
1966#endif
edisonn@google.com3aa35552013-08-14 18:26:20 +00001967 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001968}
1969
edisonn@google.com24cdf132013-07-30 16:06:12 +00001970
1971// TODO(edisonn): security review here, make sure all parameters are valid, and safe.
edisonn@google.com33f11b62013-08-14 21:35:27 +00001972static SkPdfResult skpdfGraphicsStateApply_ca(SkPdfContext* pdfContext, double ca) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001973 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001974 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001975}
1976
edisonn@google.com33f11b62013-08-14 21:35:27 +00001977static SkPdfResult skpdfGraphicsStateApply_CA(SkPdfContext* pdfContext, double CA) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001978 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001979 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001980}
1981
edisonn@google.com33f11b62013-08-14 21:35:27 +00001982static SkPdfResult skpdfGraphicsStateApplyLW(SkPdfContext* pdfContext, double lineWidth) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001983 pdfContext->fGraphicsState.fLineWidth = lineWidth;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001984 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001985}
1986
edisonn@google.com33f11b62013-08-14 21:35:27 +00001987static SkPdfResult skpdfGraphicsStateApplyLC(SkPdfContext* pdfContext, int64_t lineCap) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001988 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001989 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001990}
1991
edisonn@google.com33f11b62013-08-14 21:35:27 +00001992static SkPdfResult skpdfGraphicsStateApplyLJ(SkPdfContext* pdfContext, int64_t lineJoin) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001993 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001994 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001995}
1996
edisonn@google.com33f11b62013-08-14 21:35:27 +00001997static SkPdfResult skpdfGraphicsStateApplyML(SkPdfContext* pdfContext, double miterLimit) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001998 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001999 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002000}
2001
2002// TODO(edisonn): implement all rules, as of now 3) and 5) and 6) do not seem suported by skia, but I am not sure
2003/*
20041) [ ] 0 No dash; solid, unbroken lines
20052) [3] 0 3 units on, 3 units off, …
20063) [2] 1 1 on, 2 off, 2 on, 2 off, …
20074) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
20085) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
20096) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
2010 */
2011
edisonn@google.com33f11b62013-08-14 21:35:27 +00002012static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* intervals, SkPdfNativeObject* phase) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002013 int cnt = intervals->size();
2014 if (cnt >= 256) {
2015 // TODO(edisonn): report error/warning, unsuported;
2016 // TODO(edisonn): alloc memory
edisonn@google.com3aa35552013-08-14 18:26:20 +00002017 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002018 }
2019 for (int i = 0; i < cnt; i++) {
2020 if (!intervals->objAtAIndex(i)->isNumber()) {
2021 // TODO(edisonn): report error/warning
edisonn@google.com3aa35552013-08-14 18:26:20 +00002022 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002023 }
2024 }
2025
edisonn@google.com24cdf132013-07-30 16:06:12 +00002026 double total = 0;
2027 for (int i = 0 ; i < cnt; i++) {
2028 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
2029 total += pdfContext->fGraphicsState.fDashArray[i];
2030 }
edisonn@google.comf111a4b2013-07-31 18:22:36 +00002031 if (cnt & 1) {
2032 if (cnt == 1) {
2033 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
2034 cnt++;
2035 } else {
2036 // TODO(edisonn): report error/warning
edisonn@google.com3aa35552013-08-14 18:26:20 +00002037 return kNYI_SkPdfResult;
edisonn@google.comf111a4b2013-07-31 18:22:36 +00002038 }
2039 }
2040 pdfContext->fGraphicsState.fDashArrayLength = cnt;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002041 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
2042 if (pdfContext->fGraphicsState.fDashPhase == 0) {
2043 // other rules, changes?
edisonn@google.com063d7072013-08-16 15:05:08 +00002044 pdfContext->fGraphicsState.fDashPhase = SkDoubleToScalar(total);
edisonn@google.com24cdf132013-07-30 16:06:12 +00002045 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002046
edisonn@google.com3aa35552013-08-14 18:26:20 +00002047 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002048}
2049
edisonn@google.com33f11b62013-08-14 21:35:27 +00002050static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* dash) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002051 // TODO(edisonn): verify input
2052 if (!dash || dash->isArray() || dash->size() != 2 || !dash->objAtAIndex(0)->isArray() || !dash->objAtAIndex(1)->isNumber()) {
2053 // TODO(edisonn): report error/warning
edisonn@google.com3aa35552013-08-14 18:26:20 +00002054 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002055 }
2056 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0), dash->objAtAIndex(1));
2057}
2058
edisonn@google.com33f11b62013-08-14 21:35:27 +00002059static void skpdfGraphicsStateApplyFont(SkPdfContext* pdfContext, SkPdfArray* fontAndSize) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002060 if (!fontAndSize || fontAndSize->isArray() || fontAndSize->size() != 2 || !fontAndSize->objAtAIndex(0)->isName() || !fontAndSize->objAtAIndex(1)->isNumber()) {
2061 // TODO(edisonn): report error/warning
2062 return;
2063 }
2064 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), fontAndSize->objAtAIndex(1)->numberValue());
2065}
2066
2067
2068//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002069static SkPdfResult PdfOp_w(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002070 EXPECT_OPERANDS(pdfContext, 1);
2071 POP_NUMBER(pdfContext, lw);
2072 CHECK_PARAMETERS();
2073
edisonn@google.com24cdf132013-07-30 16:06:12 +00002074 return skpdfGraphicsStateApplyLW(pdfContext, lw);
2075}
2076
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002077//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002078static SkPdfResult PdfOp_J(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com063d7072013-08-16 15:05:08 +00002079 // TODO(edisonn): round/ceil to int?
edisonn@google.combd2f3012013-08-22 14:18:04 +00002080 EXPECT_OPERANDS(pdfContext, 1);
2081 POP_NUMBER(pdfContext, lc);
2082 CHECK_PARAMETERS();
2083
2084 return skpdfGraphicsStateApplyLC(pdfContext, (int)lc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002085}
2086
2087//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002088static SkPdfResult PdfOp_j(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com063d7072013-08-16 15:05:08 +00002089 // TODO(edisonn): round/ceil to int?
edisonn@google.combd2f3012013-08-22 14:18:04 +00002090 EXPECT_OPERANDS(pdfContext, 1);
2091 POP_NUMBER(pdfContext, lj);
2092 CHECK_PARAMETERS();
2093
2094 return skpdfGraphicsStateApplyLJ(pdfContext, (int)lj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002095}
2096
2097//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002098static SkPdfResult PdfOp_M(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002099 EXPECT_OPERANDS(pdfContext, 1);
2100 POP_NUMBER(pdfContext, ml);
2101 CHECK_PARAMETERS();
edisonn@google.com24cdf132013-07-30 16:06:12 +00002102 return skpdfGraphicsStateApplyML(pdfContext, ml);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002103}
2104
2105//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
2106//page 155).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002107static SkPdfResult PdfOp_d(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002108 EXPECT_OPERANDS(pdfContext, 2);
2109 POP_OBJ(pdfContext, phase);
2110 POP_ARRAY(pdfContext, array);
2111 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002112
edisonn@google.combd2f3012013-08-22 14:18:04 +00002113 return skpdfGraphicsStateApplyD(pdfContext, array, phase);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002114}
2115
2116//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002117static SkPdfResult PdfOp_ri(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002118 pdfContext->fObjectStack.pop();
2119
edisonn@google.com3aa35552013-08-14 18:26:20 +00002120 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002121}
2122
2123//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
2124//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
2125//fies the output device’s default flatness tolerance.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002126static SkPdfResult PdfOp_i(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002127 EXPECT_OPERANDS(pdfContext, 1);
2128 POP_NUMBER(pdfContext, flatness);
2129 CHECK_PARAMETERS();
2130
2131 if (flatness < 0 || flatness > 100) {
2132 return kError_SkPdfResult;
2133 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002134
edisonn@google.com3aa35552013-08-14 18:26:20 +00002135 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002136}
2137
edisonn@google.come878e722013-07-29 19:10:58 +00002138SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
2139
2140class InitBlendModes {
2141public:
2142 InitBlendModes() {
2143 // TODO(edisonn): use the python code generator?
2144 // TABLE 7.2 Standard separable blend modes
2145 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
2146 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
2147 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
2148 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
2149 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
2150 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
2151 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
2152 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
2153 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
2154 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
2155 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
2156 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
2157
2158 // TABLE 7.3 Standard nonseparable blend modes
2159 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
2160 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
2161 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
2162 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
2163 }
2164};
2165
2166InitBlendModes _gDummyInniter;
2167
edisonn@google.com33f11b62013-08-14 21:35:27 +00002168static SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
edisonn@google.come878e722013-07-29 19:10:58 +00002169 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2170 if (gPdfBlendModes.find(blendMode, len, &mode)) {
2171 return mode;
2172 }
2173
2174 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2175}
2176
edisonn@google.com063d7072013-08-16 15:05:08 +00002177static void skpdfGraphicsStateApplyBM_name(SkPdfContext* pdfContext, const SkString& blendMode) {
2178 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.size());
edisonn@google.come878e722013-07-29 19:10:58 +00002179 if (mode <= SkXfermode::kLastMode) {
2180 pdfContext->fGraphicsState.fBlendModesLength = 1;
2181 pdfContext->fGraphicsState.fBlendModes[0] = mode;
2182 } else {
2183 // TODO(edisonn): report unknown blend mode
2184 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002185}
2186
edisonn@google.com33f11b62013-08-14 21:35:27 +00002187static void skpdfGraphicsStateApplyBM_array(SkPdfContext* pdfContext, SkPdfArray* blendModes) {
edisonn@google.come878e722013-07-29 19:10:58 +00002188 if (!blendModes || blendModes->isArray() || blendModes->size() == 0 || blendModes->size() > 256) {
2189 // TODO(edisonn): report error/warning
2190 return;
2191 }
2192 SkXfermode::Mode modes[256];
2193 int cnt = blendModes->size();
2194 for (int i = 0; i < cnt; i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00002195 SkPdfNativeObject* name = blendModes->objAtAIndex(i);
edisonn@google.come878e722013-07-29 19:10:58 +00002196 if (!name->isName()) {
2197 // TODO(edisonn): report error/warning
2198 return;
2199 }
2200 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
2201 if (mode > SkXfermode::kLastMode) {
2202 // TODO(edisonn): report error/warning
2203 return;
2204 }
2205 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002206
edisonn@google.come878e722013-07-29 19:10:58 +00002207 pdfContext->fGraphicsState.fBlendModesLength = cnt;
2208 for (int i = 0; i < cnt; i++) {
2209 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
2210 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002211}
2212
edisonn@google.com33f11b62013-08-14 21:35:27 +00002213static void skpdfGraphicsStateApplySMask_dict(SkPdfContext* pdfContext, SkPdfDictionary* sMask) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002214 // TODO(edisonn): verify input
edisonn@google.come878e722013-07-29 19:10:58 +00002215 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002216 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
edisonn@google.come878e722013-07-29 19:10:58 +00002217 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
2218 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
2219 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
2220 } else {
2221 // TODO (edisonn): report error/warning
2222 }
2223}
2224
edisonn@google.com063d7072013-08-16 15:05:08 +00002225static void skpdfGraphicsStateApplySMask_name(SkPdfContext* pdfContext, const SkString& sMask) {
2226 if (sMask.equals("None")) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002227 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002228 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002229 return;
2230 }
2231
edisonn@google.come878e722013-07-29 19:10:58 +00002232 //Next, get the ExtGState Dictionary from the Resource Dictionary:
2233 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2234
2235 if (extGStateDictionary == NULL) {
2236#ifdef PDF_TRACE
2237 printf("ExtGState is NULL!\n");
2238#endif
2239 // TODO (edisonn): report error/warning
2240 return;
2241 }
2242
edisonn@google.com3aa35552013-08-14 18:26:20 +00002243 SkPdfNativeObject* obj = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
edisonn@google.come878e722013-07-29 19:10:58 +00002244 if (!obj || !obj->isDictionary()) {
2245 // TODO (edisonn): report error/warning
2246 return;
2247 }
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002248
2249 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002250 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002251
edisonn@google.come878e722013-07-29 19:10:58 +00002252 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002253}
2254
edisonn@google.com33f11b62013-08-14 21:35:27 +00002255static void skpdfGraphicsStateApplyAIS(SkPdfContext* pdfContext, bool alphaSource) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002256 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2257}
2258
2259
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002260//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2261//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002262static SkPdfResult PdfOp_gs(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002263 EXPECT_OPERANDS(pdfContext, 1);
2264 POP_NAME(pdfContext, name);
2265 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002266
2267#ifdef PDF_TRACE
edisonn@google.com063d7072013-08-16 15:05:08 +00002268 SkString str;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002269#endif
2270
2271 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00002272 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002273
2274 if (extGStateDictionary == NULL) {
2275#ifdef PDF_TRACE
2276 printf("ExtGState is NULL!\n");
2277#endif
edisonn@google.com3aa35552013-08-14 18:26:20 +00002278 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002279 }
2280
edisonn@google.com3aa35552013-08-14 18:26:20 +00002281 SkPdfNativeObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002282
edisonn@google.com3aa35552013-08-14 18:26:20 +00002283 if (kNone_SkPdfNativeObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2284 return kIgnoreError_SkPdfResult;
edisonn@google.com571c70b2013-07-10 17:09:50 +00002285 }
2286 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002287
2288 // TODO(edisonn): now load all those properties in graphic state.
2289 if (gs == NULL) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00002290 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002291 }
2292
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002293 if (gs->has_LW()) {
2294 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2295 }
2296
2297 if (gs->has_LC()) {
2298 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2299 }
2300
2301 if (gs->has_LJ()) {
2302 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2303 }
2304
2305 if (gs->has_ML()) {
2306 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2307 }
2308
2309 if (gs->has_D()) {
2310 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2311 }
2312
2313 if (gs->has_Font()) {
2314 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2315 }
2316
2317 if (gs->has_BM()) {
2318 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2319 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2320 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2321 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2322 } else {
2323 // TODO(edisonn): report/warn
2324 }
2325 }
2326
2327 if (gs->has_SMask()) {
2328 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2329 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2330 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
2331 skpdfGraphicsStateApplySMask_dict(pdfContext, gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
2332 } else {
2333 // TODO(edisonn): report/warn
2334 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002335 }
2336
2337 if (gs->has_ca()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002338 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002339 }
2340
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002341 if (gs->has_CA()) {
2342 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2343 }
2344
2345 if (gs->has_AIS()) {
2346 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002347 }
2348
edisonn@google.com3aa35552013-08-14 18:26:20 +00002349 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002350}
2351
2352//charSpace Tc Set the character spacing, Tc
2353//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2354//Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002355SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002356 EXPECT_OPERANDS(pdfContext, 1);
2357 POP_NUMBER(pdfContext, charSpace);
2358 CHECK_PARAMETERS();
2359
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002360 pdfContext->fGraphicsState.fCharSpace = charSpace;
2361
edisonn@google.com3aa35552013-08-14 18:26:20 +00002362 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002363}
2364
2365//wordSpace Tw Set the word spacing, T
2366//w
2367//, to wordSpace, which is a number expressed in unscaled
2368//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2369//value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002370SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002371 EXPECT_OPERANDS(pdfContext, 1);
2372 POP_NUMBER(pdfContext, wordSpace);
2373 CHECK_PARAMETERS();
2374
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002375 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2376
edisonn@google.com3aa35552013-08-14 18:26:20 +00002377 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002378}
2379
2380//scale Tz Set the horizontal scaling, Th
2381//, to (scale ˜ 100). scale is a number specifying the
2382//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002383static SkPdfResult PdfOp_Tz(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002384 EXPECT_OPERANDS(pdfContext, 1);
2385 POP_NUMBER(pdfContext, scale);
2386 CHECK_PARAMETERS();
2387
2388 if (scale < 0) {
2389 return kError_SkPdfResult;
2390 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002391
edisonn@google.com3aa35552013-08-14 18:26:20 +00002392 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002393}
2394
2395//render Tr Set the text rendering mode, T
2396//mode, to render, which is an integer. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002397static SkPdfResult PdfOp_Tr(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002398 EXPECT_OPERANDS(pdfContext, 1);
2399 POP_INTEGER(pdfContext, mode);
2400 CHECK_PARAMETERS();
2401
2402 if (mode < 0) { // TODO(edisonn): function/enums with supported modes
2403 return kError_SkPdfResult;
2404 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002405
edisonn@google.com3aa35552013-08-14 18:26:20 +00002406 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002407}
2408//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2409//units. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002410static SkPdfResult PdfOp_Ts(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002411 EXPECT_OPERANDS(pdfContext, 1);
2412 POP_NUMBER(pdfContext, rise);
2413 CHECK_PARAMETERS();
2414
2415 if (rise < 0) {
2416 return kNYI_SkPdfResult;
2417 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002418
edisonn@google.com3aa35552013-08-14 18:26:20 +00002419 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002420}
2421
2422//wx wy d0
edisonn@google.com3aa35552013-08-14 18:26:20 +00002423static SkPdfResult PdfOp_d0(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002424 EXPECT_OPERANDS(pdfContext, 2);
2425 POP_NUMBER(pdfContext, wy);
2426 POP_NUMBER(pdfContext, wx);
2427 CHECK_PARAMETERS();
2428
2429 if (wx < 0 || wy < 0) {
2430 return kError_SkPdfResult;
2431 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002432
edisonn@google.com3aa35552013-08-14 18:26:20 +00002433 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002434}
2435
2436//wx wy llx lly urx ury d1
edisonn@google.com3aa35552013-08-14 18:26:20 +00002437static SkPdfResult PdfOp_d1(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002438 EXPECT_OPERANDS(pdfContext, 6);
2439 POP_NUMBER(pdfContext, ury);
2440 POP_NUMBER(pdfContext, urx);
2441 POP_NUMBER(pdfContext, lly);
2442 POP_NUMBER(pdfContext, llx);
2443 POP_NUMBER(pdfContext, wy);
2444 POP_NUMBER(pdfContext, wx);
2445 CHECK_PARAMETERS();
2446
2447 if (wx + wy + llx + lly + urx + ury) {
2448 return kNYI_SkPdfResult;
2449 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002450
edisonn@google.com3aa35552013-08-14 18:26:20 +00002451 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002452}
2453
2454//name sh
edisonn@google.com3aa35552013-08-14 18:26:20 +00002455static SkPdfResult PdfOp_sh(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002456 EXPECT_OPERANDS(pdfContext, 1);
2457 POP_NAME(pdfContext, name);
2458 CHECK_PARAMETERS();
2459
2460 if (name == NULL) {
2461 return kError_SkPdfResult;
2462 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002463
edisonn@google.com3aa35552013-08-14 18:26:20 +00002464 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002465}
2466
2467//name Do
edisonn@google.com3aa35552013-08-14 18:26:20 +00002468static SkPdfResult PdfOp_Do(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002469 EXPECT_OPERANDS(pdfContext, 1);
2470 POP_NAME(pdfContext, name);
2471 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002472
edisonn@google.com571c70b2013-07-10 17:09:50 +00002473 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002474
2475 if (xObject == NULL) {
2476#ifdef PDF_TRACE
2477 printf("XObject is NULL!\n");
2478#endif
edisonn@google.com3aa35552013-08-14 18:26:20 +00002479 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002480 }
2481
edisonn@google.com3aa35552013-08-14 18:26:20 +00002482 SkPdfNativeObject* value = xObject->get(name);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002483 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002484
2485#ifdef PDF_TRACE
2486// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002487// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002488#endif
2489
edisonn@google.com571c70b2013-07-10 17:09:50 +00002490 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002491}
2492
2493//tag MP Designate a marked-content point. tag is a name object indicating the role or
2494//significance of the point.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002495static SkPdfResult PdfOp_MP(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002496 EXPECT_OPERANDS(pdfContext, 1);
2497 POP_OBJ(pdfContext, tag);
2498 CHECK_PARAMETERS();
2499
2500 if (tag == NULL) {
2501 return kNYI_SkPdfResult;
2502 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002503
edisonn@google.com3aa35552013-08-14 18:26:20 +00002504 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002505}
2506
2507//tag properties DP Designate a marked-content point with an associated property list. tag is a
2508//name object indicating the role or significance of the point; properties is
2509//either an inline dictionary containing the property list or a name object
2510//associated with it in the Properties subdictionary of the current resource
2511//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002512static SkPdfResult PdfOp_DP(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002513 EXPECT_OPERANDS(pdfContext, 2);
2514 POP_OBJ(pdfContext, properties);
2515 POP_OBJ(pdfContext, tag);
2516 CHECK_PARAMETERS();
2517
2518 if (tag == NULL || properties == NULL) {
2519 return kNYI_SkPdfResult;
2520 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002521
edisonn@google.com3aa35552013-08-14 18:26:20 +00002522 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002523}
2524
2525//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2526//tag is a name object indicating the role or significance of the sequence.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002527static SkPdfResult PdfOp_BMC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002528 EXPECT_OPERANDS(pdfContext, 1);
2529 POP_OBJ(pdfContext, tag);
2530 CHECK_PARAMETERS();
2531
2532 if (tag == NULL) {
2533 return kNYI_SkPdfResult;
2534 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002535
edisonn@google.com3aa35552013-08-14 18:26:20 +00002536 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002537}
2538
2539//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2540//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2541//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.com3aa35552013-08-14 18:26:20 +00002542static SkPdfResult PdfOp_BDC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00002543 EXPECT_OPERANDS(pdfContext, 2);
2544 POP_OBJ(pdfContext, properties);
2545 POP_OBJ(pdfContext, tag);
2546 CHECK_PARAMETERS();
2547
2548 if (tag == NULL || properties == NULL) {
2549 return kNYI_SkPdfResult;
2550 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002551
edisonn@google.com3aa35552013-08-14 18:26:20 +00002552 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002553}
2554
2555//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002556static SkPdfResult PdfOp_EMC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
2557 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002558}
2559
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002560static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002561 static bool gInitialized = false;
2562 if (gInitialized) {
2563 return;
2564 }
2565
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002566 gPdfOps.set("q", PdfOp_q);
2567 gPdfOps.set("Q", PdfOp_Q);
2568 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002569
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002570 gPdfOps.set("TD", PdfOp_TD);
2571 gPdfOps.set("Td", PdfOp_Td);
2572 gPdfOps.set("Tm", PdfOp_Tm);
2573 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002574
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002575 gPdfOps.set("m", PdfOp_m);
2576 gPdfOps.set("l", PdfOp_l);
2577 gPdfOps.set("c", PdfOp_c);
2578 gPdfOps.set("v", PdfOp_v);
2579 gPdfOps.set("y", PdfOp_y);
2580 gPdfOps.set("h", PdfOp_h);
2581 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002582
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002583 gPdfOps.set("S", PdfOp_S);
2584 gPdfOps.set("s", PdfOp_s);
2585 gPdfOps.set("f", PdfOp_f);
2586 gPdfOps.set("F", PdfOp_F);
2587 gPdfOps.set("f*", PdfOp_f_star);
2588 gPdfOps.set("B", PdfOp_B);
2589 gPdfOps.set("B*", PdfOp_B_star);
2590 gPdfOps.set("b", PdfOp_b);
2591 gPdfOps.set("b*", PdfOp_b_star);
2592 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002593
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002594 gPdfOps.set("BT", PdfOp_BT);
2595 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002596
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002597 gPdfOps.set("Tj", PdfOp_Tj);
2598 gPdfOps.set("'", PdfOp_quote);
2599 gPdfOps.set("\"", PdfOp_doublequote);
2600 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002601
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002602 gPdfOps.set("CS", PdfOp_CS);
2603 gPdfOps.set("cs", PdfOp_cs);
2604 gPdfOps.set("SC", PdfOp_SC);
2605 gPdfOps.set("SCN", PdfOp_SCN);
2606 gPdfOps.set("sc", PdfOp_sc);
2607 gPdfOps.set("scn", PdfOp_scn);
2608 gPdfOps.set("G", PdfOp_G);
2609 gPdfOps.set("g", PdfOp_g);
2610 gPdfOps.set("RG", PdfOp_RG);
2611 gPdfOps.set("rg", PdfOp_rg);
2612 gPdfOps.set("K", PdfOp_K);
2613 gPdfOps.set("k", PdfOp_k);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002614
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002615 gPdfOps.set("W", PdfOp_W);
2616 gPdfOps.set("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002617
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002618 gPdfOps.set("BX", PdfOp_BX);
2619 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002620
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002621 gPdfOps.set("BI", PdfOp_BI);
2622 gPdfOps.set("ID", PdfOp_ID);
2623 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002624
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002625 gPdfOps.set("w", PdfOp_w);
2626 gPdfOps.set("J", PdfOp_J);
2627 gPdfOps.set("j", PdfOp_j);
2628 gPdfOps.set("M", PdfOp_M);
2629 gPdfOps.set("d", PdfOp_d);
2630 gPdfOps.set("ri", PdfOp_ri);
2631 gPdfOps.set("i", PdfOp_i);
2632 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002633
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002634 gPdfOps.set("Tc", PdfOp_Tc);
2635 gPdfOps.set("Tw", PdfOp_Tw);
2636 gPdfOps.set("Tz", PdfOp_Tz);
2637 gPdfOps.set("TL", PdfOp_TL);
2638 gPdfOps.set("Tf", PdfOp_Tf);
2639 gPdfOps.set("Tr", PdfOp_Tr);
2640 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002641
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002642 gPdfOps.set("d0", PdfOp_d0);
2643 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002644
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002645 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002646
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002647 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002648
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002649 gPdfOps.set("MP", PdfOp_MP);
2650 gPdfOps.set("DP", PdfOp_DP);
2651 gPdfOps.set("BMC", PdfOp_BMC);
2652 gPdfOps.set("BDC", PdfOp_BDC);
2653 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002654
2655 gInitialized = true;
2656}
2657
2658class InitPdfOps {
2659public:
2660 InitPdfOps() {
2661 initPdfOperatorRenderes();
2662 }
2663};
2664
2665InitPdfOps gInitPdfOps;
2666
2667void reportPdfRenderStats() {
edisonn@google.com3aa35552013-08-14 18:26:20 +00002668 for (int i = 0 ; i < kCount_SkPdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002669 SkTDict<int>::Iter iter(gRenderStats[i]);
2670 const char* key;
2671 int value = 0;
2672 while ((key = iter.next(&value)) != NULL) {
2673 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002674 }
2675 }
2676}
2677
edisonn@google.com3aa35552013-08-14 18:26:20 +00002678SkPdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002679 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002680 {
2681 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002682 PdfOperatorRenderer pdfOperatorRenderer = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002683 if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) && pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002684 // caller, main work is done by pdfOperatorRenderer(...)
2685 PdfTokenLooper* childLooper = NULL;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002686 SkPdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002687
2688 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002689 gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
2690 gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002691
2692 if (childLooper) {
2693 childLooper->setUp(this);
2694 childLooper->loop();
2695 delete childLooper;
2696 }
2697 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002698 int cnt = 0;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002699 gRenderStats[kUnsupported_SkPdfResult].find(token.fKeyword, token.fKeywordLength, &cnt);
2700 gRenderStats[kUnsupported_SkPdfResult].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002701 }
2702 }
2703 else if (token.fType == kObject_TokenType)
2704 {
2705 fPdfContext->fObjectStack.push( token.fObject );
2706 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002707 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002708 // TODO(edisonn): deine or use assert not reached
edisonn@google.com3aa35552013-08-14 18:26:20 +00002709 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002710 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00002711 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002712}
2713
2714void PdfMainLooper::loop() {
2715 PdfToken token;
2716 while (readToken(fTokenizer, &token)) {
2717 consumeToken(token);
2718 }
2719}
2720
edisonn@google.com3aa35552013-08-14 18:26:20 +00002721SkPdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002722 SkASSERT(false);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002723 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002724}
2725
2726void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002727 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002728}
2729
edisonn@google.com3aa35552013-08-14 18:26:20 +00002730SkPdfResult PdfInlineImageLooper::done() {
2731 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002732}
2733
edisonn@google.com3aa35552013-08-14 18:26:20 +00002734SkPdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002735 return fParent->consumeToken(token);
2736}
2737
2738void PdfCompatibilitySectionLooper::loop() {
2739 // TODO(edisonn): save stacks position, or create a new stack?
2740 // TODO(edisonn): what happens if we pop out more variables then when we started?
2741 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2742 // pop-ing too much will not affect outside the section.
2743 PdfToken token;
2744 while (readToken(fTokenizer, &token)) {
2745 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2746 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2747 looper->setUp(this);
2748 looper->loop();
2749 delete looper;
2750 } else {
2751 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
2752 fParent->consumeToken(token);
2753 }
2754 }
2755 // TODO(edisonn): restore stack.
2756}
2757
2758// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2759// TODO(edisonn): Add API for Forms viewing and editing
2760// e.g. SkBitmap getPage(int page);
2761// int formsCount();
2762// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2763// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2764// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2765// resources needed, so the preview is fast.
2766// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2767// references automatically.
2768
edisonn@google.com3aa35552013-08-14 18:26:20 +00002769SkPdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00002770
edisonn@google.com444e25a2013-07-11 15:20:50 +00002771bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00002772 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002773 return false;
2774 }
2775
edisonn@google.com222382b2013-07-10 22:33:10 +00002776 if (page < 0 || page >= pages()) {
2777 return false;
2778 }
2779
edisonn@google.com3aa35552013-08-14 18:26:20 +00002780 SkPdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002781
edisonn@google.com222382b2013-07-10 22:33:10 +00002782 pdfContext.fOriginalMatrix = SkMatrix::I();
2783 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2784
2785 gPdfContext = &pdfContext;
2786
2787 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00002788 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00002789 SkScalar w = dst.width();
2790 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00002791
edisonn@google.comf68aed32013-08-22 15:37:21 +00002792 if (SkScalarTruncToInt(w) <= 0 || SkScalarTruncToInt(h) <= 0) {
2793 return true;
2794 }
2795
edisonn@google.com444e25a2013-07-11 15:20:50 +00002796 SkScalar wp = fPdfDoc->MediaBox(page).width();
2797 SkScalar hp = fPdfDoc->MediaBox(page).height();
2798
2799 SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(wp, z), SkPoint::Make(wp, hp), SkPoint::Make(z, hp)};
edisonn@google.com222382b2013-07-10 22:33:10 +00002800// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2801
2802 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2803 // TODO(edisonn): add flagg for no clipping.
2804 // Use larger image to make sure we do not draw anything outside of page
2805 // could be used in tests.
2806
2807#ifdef PDF_DEBUG_3X
2808 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)};
2809#else
2810 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2811#endif
2812 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2813 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2814
2815 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2816 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2817
2818 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2819 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2820
2821 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2822 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2823
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002824 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
edisonn@google.com0f901902013-08-07 11:56:16 +00002825 pdfContext.fGraphicsState.fContentStreamMatrix = pdfContext.fOriginalMatrix;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002826 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
2827 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
edisonn@google.com222382b2013-07-10 22:33:10 +00002828
edisonn@google.com222382b2013-07-10 22:33:10 +00002829#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00002830 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00002831#endif
2832
edisonn@google.com444e25a2013-07-11 15:20:50 +00002833 canvas->setMatrix(pdfContext.fOriginalMatrix);
2834
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002835 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2836
2837 // TODO(edisonn:) erase with white before draw?
edisonn@google.com222382b2013-07-10 22:33:10 +00002838// SkPaint paint;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002839// paint.setColor(SK_ColorWHITE);
edisonn@google.com222382b2013-07-10 22:33:10 +00002840// canvas->drawRect(rect, paint);
2841
edisonn@google.com222382b2013-07-10 22:33:10 +00002842
2843 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002844 return true;
2845}
edisonn@google.com222382b2013-07-10 22:33:10 +00002846
2847bool SkPdfRenderer::load(const SkString inputFileName) {
2848 unload();
2849
2850 // TODO(edisonn): create static function that could return NULL if there are errors
edisonn@google.com3aa35552013-08-14 18:26:20 +00002851 fPdfDoc = new SkPdfNativeDoc(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00002852 if (fPdfDoc->pages() == 0) {
2853 delete fPdfDoc;
2854 fPdfDoc = NULL;
2855 }
edisonn@google.com222382b2013-07-10 22:33:10 +00002856
2857 return fPdfDoc != NULL;
2858}
2859
edisonn@google.com147adb12013-07-24 15:56:19 +00002860bool SkPdfRenderer::load(SkStream* stream) {
2861 unload();
2862
2863 // TODO(edisonn): create static function that could return NULL if there are errors
edisonn@google.com3aa35552013-08-14 18:26:20 +00002864 fPdfDoc = new SkPdfNativeDoc(stream);
edisonn@google.com147adb12013-07-24 15:56:19 +00002865 if (fPdfDoc->pages() == 0) {
2866 delete fPdfDoc;
2867 fPdfDoc = NULL;
2868 }
2869
2870 return fPdfDoc != NULL;
2871}
2872
2873
edisonn@google.com222382b2013-07-10 22:33:10 +00002874int SkPdfRenderer::pages() const {
2875 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2876}
2877
2878void SkPdfRenderer::unload() {
2879 delete fPdfDoc;
2880 fPdfDoc = NULL;
2881}
2882
2883SkRect SkPdfRenderer::MediaBox(int page) const {
2884 SkASSERT(fPdfDoc);
2885 return fPdfDoc->MediaBox(page);
2886}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002887
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002888size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002889 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2890}
edisonn@google.com147adb12013-07-24 15:56:19 +00002891
2892bool SkPDFNativeRenderToBitmap(SkStream* stream,
2893 SkBitmap* output,
2894 int page,
2895 SkPdfContent content,
2896 double dpi) {
2897 SkASSERT(page >= 0);
2898 SkPdfRenderer renderer;
2899 renderer.load(stream);
2900 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
2901 return false;
2902 }
2903
2904 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
2905
2906 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2907 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2908
2909 rect = SkRect::MakeWH(width, height);
2910
2911 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
2912
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002913 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (*output)));
edisonn@google.com147adb12013-07-24 15:56:19 +00002914 SkCanvas canvas(device);
2915
2916 return renderer.renderPage(page, &canvas, rect);
2917}