blob: 04d9d28fde4da2cd33c3468d36646de6d53557cb [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.comaf54a512013-09-13 19:33:42 +00008#include "SkPdfRenderer.h"
9
edisonn@google.come91260c2013-09-04 17:29:06 +000010#include "SkBitmapDevice.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000011#include "SkCanvas.h"
12#include "SkDevice.h"
13#include "SkForceLinking.h"
14#include "SkGraphics.h"
15#include "SkImageDecoder.h"
16#include "SkImageEncoder.h"
17#include "SkOSFile.h"
18#include "SkPicture.h"
19#include "SkStream.h"
20#include "SkTypeface.h"
21#include "SkTArray.h"
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000022#include "SkTDict.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000023
edisonn@google.com3aa35552013-08-14 18:26:20 +000024#include "SkPdfGraphicsState.h"
edisonn@google.com15b11182013-07-11 14:43:15 +000025#include "SkPdfNativeTokenizer.h"
edisonn@google.comaf54a512013-09-13 19:33:42 +000026#include "SkPdfReporter.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000027
edisonn@google.com3aa35552013-08-14 18:26:20 +000028extern "C" SkPdfContext* gPdfContext;
edisonn@google.com15b11182013-07-11 14:43:15 +000029extern "C" SkBitmap* gDumpBitmap;
30extern "C" SkCanvas* gDumpCanvas;
31
edisonn@google.comaf54a512013-09-13 19:33:42 +000032
edisonn@google.com131d4ee2013-06-26 17:48:12 +000033__SK_FORCE_IMAGE_DECODER_LINKING;
34
35// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
36// keep for each object pos in file
37// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
38
39// TODO(edisonn): security - validate all the user input, all pdf!
40
edisonn@google.com6e49c342013-06-27 20:03:43 +000041// TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor changes in text positioning and font
42// this way, we look more at other features and layout in diffs
edisonn@google.com131d4ee2013-06-26 17:48:12 +000043
edisonn@google.com3aac1f92013-07-02 22:42:53 +000044// TODO(edisonn): move trace dump in the get functions, and mapper ones too so it ghappens automatically
45/*
46#ifdef PDF_TRACE
edisonn@google.com063d7072013-08-16 15:05:08 +000047 SkString str;
edisonn@google.com571c70b2013-07-10 17:09:50 +000048 pdfContext->fGraphicsState.fResources->native()->ToString(str);
edisonn@google.com3aac1f92013-07-02 22:42:53 +000049 printf("Print Tf Resources: %s\n", str.c_str());
50#endif
51 */
52
edisonn@google.com131d4ee2013-06-26 17:48:12 +000053#include "SkPdfHeaders_autogen.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +000054#include "SkPdfMapper_autogen.h"
edisonn@google.com222382b2013-07-10 22:33:10 +000055#include "SkPdfRenderer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000056
edisonn@google.com131d4ee2013-06-26 17:48:12 +000057#include "SkPdfUtils.h"
58
59#include "SkPdfFont.h"
60
edisonn@google.com131d4ee2013-06-26 17:48:12 +000061/*
62 * TODO(edisonn):
63 * - all font types and all ppdf font features
64 * - word spacing
65 * - load font for baidu.pdf
66 * - load font for youtube.pdf
67 * - parser for pdf from the definition already available in pdfspec_autogen.py
68 * - all docs from ~/work
edisonn@google.com571c70b2013-07-10 17:09:50 +000069 * - 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 +000070 * - load gs/ especially smask and already known prop (skp) ... in progress
71 * - wrapper on classes for customizations? e.g.
72 * SkPdfPageObjectVanila - has only the basic loaders/getters
73 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
74 * need to find a nice object model for all this with constructors and factories
75 * - deal with inheritable automatically ?
76 * - deal with specific type in spec directly, add all dictionary types to known types
77*/
78
edisonn@google.comaf54a512013-09-13 19:33:42 +000079#define EXPECT_OPERANDS(name,pdfContext,n) \
edisonn@google.combd2f3012013-08-22 14:18:04 +000080 bool __failed = pdfContext->fObjectStack.count() < n; \
edisonn@google.comaf54a512013-09-13 19:33:42 +000081 SkPdfREPORTCODE(const char* __operator_name = name); \
82 SkPdfREPORTCODE((void)__operator_name); \
83 SkPdfReportIf(pdfContext->fObjectStack.count() < n, kIgnoreError_SkPdfIssueSeverity, kStackOverflow_SkPdfIssue, "Not enought parameters.", NULL, pdfContext); \
edisonn@google.combd2f3012013-08-22 14:18:04 +000084 SkDEBUGCODE(int __cnt = n);
85
86#define POP_OBJ(pdfContext,name) \
87 SkDEBUGCODE(__cnt--); \
88 SkASSERT(__cnt >= 0); \
89 SkPdfNativeObject* name = NULL; \
90 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
91 if (pdfContext->fObjectStack.count() > 0) { \
92 name = pdfContext->fObjectStack.top(); \
93 pdfContext->fObjectStack.pop(); \
94 }
95
edisonn@google.comaf54a512013-09-13 19:33:42 +000096// TODO(edisonn): make all pop function to use name##_obj
edisonn@google.combd2f3012013-08-22 14:18:04 +000097#define POP_NUMBER(pdfContext,name) \
98 SkDEBUGCODE(__cnt--); \
99 SkASSERT(__cnt >= 0); \
100 double name = 0; \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000101 SkPdfNativeObject* name##_obj = NULL; \
edisonn@google.combd2f3012013-08-22 14:18:04 +0000102 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
103 if (pdfContext->fObjectStack.count() > 0) { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000104 name##_obj = pdfContext->fObjectStack.top(); \
edisonn@google.combd2f3012013-08-22 14:18:04 +0000105 pdfContext->fObjectStack.pop(); \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000106 if (!name##_obj || !name##_obj->isNumber()) { \
107 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, __operator_name, name##_obj, SkPdfNativeObject::_kNumber_PdfObjectType, NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000108 __failed = true;\
109 } else { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000110 name = name##_obj->numberValue(); \
edisonn@google.combd2f3012013-08-22 14:18:04 +0000111 } \
112 }
113
114#define POP_INTEGER(pdfContext,name) \
115 SkDEBUGCODE(__cnt--); \
116 SkASSERT(__cnt >= 0); \
117 int64_t name = 0; \
118 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000119 SkPdfNativeObject* name##_obj = NULL; \
edisonn@google.combd2f3012013-08-22 14:18:04 +0000120 if (pdfContext->fObjectStack.count() > 0) { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000121 name##_obj = pdfContext->fObjectStack.top(); \
edisonn@google.combd2f3012013-08-22 14:18:04 +0000122 pdfContext->fObjectStack.pop(); \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000123 if (!name##_obj || !name##_obj->isInteger()) { \
124 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, __operator_name, name##_obj, SkPdfNativeObject::kInteger_PdfObjectType, NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000125 __failed = true;\
126 } else { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000127 name = name##_obj->intValue(); \
edisonn@google.combd2f3012013-08-22 14:18:04 +0000128 } \
129 }
130
131#define POP_NUMBER_INTO(pdfContext,var) \
132 SkDEBUGCODE(__cnt--); \
133 SkASSERT(__cnt >= 0); \
134 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
135 if (pdfContext->fObjectStack.count() > 0) { \
136 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
137 pdfContext->fObjectStack.pop(); \
138 if (!tmp || !tmp->isNumber()) { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000139 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, __operator_name, tmp, SkPdfNativeObject::kInteger_PdfObjectType | SkPdfNativeObject::kReal_PdfObjectType, NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000140 __failed = true;\
141 } else { \
142 var = tmp->numberValue(); \
143 } \
144 }
145
146
147#define POP_NAME(pdfContext,name) \
148 SkDEBUGCODE(__cnt--); \
149 SkASSERT(__cnt >= 0); \
150 SkPdfNativeObject* name = NULL; \
151 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
152 if (pdfContext->fObjectStack.count() > 0) { \
153 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
154 pdfContext->fObjectStack.pop(); \
155 if (!tmp || !tmp->isName()) { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000156 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, __operator_name, tmp, SkPdfNativeObject::kName_PdfObjectType, NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000157 __failed = true;\
158 } else { \
159 name = tmp; \
160 } \
161 }
162
163#define POP_STRING(pdfContext,name) \
164 SkDEBUGCODE(__cnt--); \
165 SkASSERT(__cnt >= 0); \
166 SkPdfNativeObject* name = NULL; \
167 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
168 if (pdfContext->fObjectStack.count() > 0) { \
169 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
170 pdfContext->fObjectStack.pop(); \
171 if (!tmp || !tmp->isAnyString()) { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000172 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, __operator_name, tmp, SkPdfNativeObject::kString_PdfObjectType | SkPdfNativeObject::kHexString_PdfObjectType, NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000173 __failed = true;\
174 } else { \
175 name = tmp; \
176 } \
177 }
178
179#define POP_ARRAY(pdfContext,name) \
180 SkDEBUGCODE(__cnt--); \
181 SkASSERT(__cnt >= 0); \
182 SkPdfArray* name = NULL; \
183 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
184 if (pdfContext->fObjectStack.count() > 0) { \
185 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
186 pdfContext->fObjectStack.pop(); \
187 if (!tmp || !tmp->isArray()) { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000188 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, __operator_name, tmp, SkPdfNativeObject::kArray_PdfObjectType, NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000189 __failed = true;\
190 } else { \
191 name = (SkPdfArray*)tmp; \
192 } \
193 }
194
195// TODO(edisonn): ability to turn on asserts for known good files
196// log error - add name of function? location in file?
197#define CHECK_PARAMETERS() \
198 SkASSERT(__cnt == 0); \
199 if (__failed) return kIgnoreError_SkPdfResult;
200
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000201NotOwnedString strings_DeviceRGB;
202NotOwnedString strings_DeviceCMYK;
edisonn@google.com222382b2013-07-10 22:33:10 +0000203
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000204class StringsInit {
205public:
206 StringsInit() {
207 NotOwnedString::init(&strings_DeviceRGB, "DeviceRGB");
208 NotOwnedString::init(&strings_DeviceCMYK, "DeviceCMYK");
209 }
210};
211
212StringsInit gStringsInit;
edisonn@google.com222382b2013-07-10 22:33:10 +0000213
214// TODO(edisonn): Document PdfTokenLooper and subclasses.
215class PdfTokenLooper {
216protected:
217 PdfTokenLooper* fParent;
218 SkPdfNativeTokenizer* fTokenizer;
edisonn@google.com3aa35552013-08-14 18:26:20 +0000219 SkPdfContext* fPdfContext;
edisonn@google.com222382b2013-07-10 22:33:10 +0000220 SkCanvas* fCanvas;
221
222public:
223 PdfTokenLooper(PdfTokenLooper* parent,
224 SkPdfNativeTokenizer* tokenizer,
edisonn@google.com3aa35552013-08-14 18:26:20 +0000225 SkPdfContext* pdfContext,
edisonn@google.com222382b2013-07-10 22:33:10 +0000226 SkCanvas* canvas)
227 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
228
229 virtual ~PdfTokenLooper() {}
230
edisonn@google.com3aa35552013-08-14 18:26:20 +0000231 virtual SkPdfResult consumeToken(PdfToken& token) = 0;
edisonn@google.com222382b2013-07-10 22:33:10 +0000232 virtual void loop() = 0;
233
234 void setUp(PdfTokenLooper* parent) {
235 fParent = parent;
236 fTokenizer = parent->fTokenizer;
237 fPdfContext = parent->fPdfContext;
238 fCanvas = parent->fCanvas;
239 }
edisonn@google.com78b38b12013-07-15 18:20:58 +0000240
241 SkPdfNativeTokenizer* tokenizer() { return fTokenizer; }
edisonn@google.com222382b2013-07-10 22:33:10 +0000242};
243
244class PdfMainLooper : public PdfTokenLooper {
245public:
246 PdfMainLooper(PdfTokenLooper* parent,
247 SkPdfNativeTokenizer* tokenizer,
edisonn@google.com3aa35552013-08-14 18:26:20 +0000248 SkPdfContext* pdfContext,
edisonn@google.com222382b2013-07-10 22:33:10 +0000249 SkCanvas* canvas)
250 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
251
edisonn@google.com3aa35552013-08-14 18:26:20 +0000252 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000253 virtual void loop();
254};
255
256class PdfInlineImageLooper : public PdfTokenLooper {
257public:
258 PdfInlineImageLooper()
259 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
260
edisonn@google.com3aa35552013-08-14 18:26:20 +0000261 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000262 virtual void loop();
edisonn@google.com3aa35552013-08-14 18:26:20 +0000263 SkPdfResult done();
edisonn@google.com222382b2013-07-10 22:33:10 +0000264};
265
266class PdfCompatibilitySectionLooper : public PdfTokenLooper {
267public:
268 PdfCompatibilitySectionLooper()
269 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
270
edisonn@google.com3aa35552013-08-14 18:26:20 +0000271 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000272 virtual void loop();
273};
274
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000275// Utilities
276static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
277 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
278
279 bitmap->allocPixels();
280 bitmap->eraseColor(color);
281}
282
283// TODO(edisonn): synonyms? DeviceRGB and RGB ...
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000284static int GetColorSpaceComponents(NotOwnedString& colorSpace) {
285 if (colorSpace.equals("DeviceCMYK")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000286 return 4;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000287 } else if (colorSpace.equals("DeviceGray") ||
288 colorSpace.equals("CalGray") ||
289 colorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000290 return 1;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000291 } else if (colorSpace.equals("DeviceRGB") ||
292 colorSpace.equals("CalRGB") ||
293 colorSpace.equals("Lab")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000294 return 3;
295 } else {
296 return 0;
297 }
298}
299
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000300SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000301 SkMatrix matrix;
302 matrix.setAll(SkDoubleToScalar(array[0]),
303 SkDoubleToScalar(array[2]),
304 SkDoubleToScalar(array[4]),
305 SkDoubleToScalar(array[1]),
306 SkDoubleToScalar(array[3]),
307 SkDoubleToScalar(array[5]),
308 SkDoubleToScalar(0),
309 SkDoubleToScalar(0),
310 SkDoubleToScalar(1));
311
312 return matrix;
313}
314
315SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
316 double array[6];
317
318 // TODO(edisonn): security issue, ret if size() != 6
edisonn@google.comaf54a512013-09-13 19:33:42 +0000319 if (pdfArray == NULL) {
320 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue, "null array passed to build matrix", NULL, NULL);
321 return SkMatrix::I();
322 }
323
324 if (pdfArray->size() != 6) {
325 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnexpectedArraySize_SkPdfIssue, "null array passed to build matrix", pdfArray, NULL);
326 return SkMatrix::I();
327 }
328
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000329 for (int i = 0; i < 6; i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000330 const SkPdfNativeObject* elem = pdfArray->operator [](i);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000331 if (elem == NULL || !elem->isNumber()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000332 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, elem, SkPdfNativeObject::_kNumber_PdfObjectType, NULL);
333 return SkMatrix::I();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000334 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000335 array[i] = elem->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000336 }
337
338 return SkMatrixFromPdfMatrix(array);
339}
340
edisonn@google.com222382b2013-07-10 22:33:10 +0000341
edisonn@google.com3aa35552013-08-14 18:26:20 +0000342extern "C" SkPdfNativeDoc* gDoc;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000343SkBitmap* gDumpBitmap = NULL;
344SkCanvas* gDumpCanvas = NULL;
345char gLastKeyword[100] = "";
346int gLastOpKeyword = -1;
347char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
348int gReadOp = 0;
349
350
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000351#ifdef PDF_TRACE_DIFF_IN_PNG
352static bool hasVisualEffect(const char* pdfOp) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000353 return true;
354 if (*pdfOp == '\0') return false;
355
356 char markedPdfOp[100] = ",";
357 strcat(markedPdfOp, pdfOp);
358 strcat(markedPdfOp, ",");
359
360 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
361}
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000362#endif // PDF_TRACE_DIFF_IN_PNG
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000363
edisonn@google.com222382b2013-07-10 22:33:10 +0000364
365
edisonn@google.com3aa35552013-08-14 18:26:20 +0000366// TODO(edisonn): Pass SkPdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000367static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000368 bool ret = fTokenizer->readToken(token);
369
370 gReadOp++;
edisonn@google.com0f901902013-08-07 11:56:16 +0000371 gLastOpKeyword++;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000372#ifdef PDF_TRACE_DIFF_IN_PNG
373 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
374 // the numbar and name of last operation, so the file name will reflect op that changed.
edisonn@google.com0f901902013-08-07 11:56:16 +0000375 if (gLastKeyword[0] && hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000376 gDumpCanvas->flush();
377
378 SkBitmap bitmap;
379 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
380
381 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
382
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000383 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000384 SkCanvas canvas(device);
385
386 // draw context stuff here
387 SkPaint blueBorder;
388 blueBorder.setColor(SK_ColorBLUE);
389 blueBorder.setStyle(SkPaint::kStroke_Style);
390 blueBorder.setTextSize(SkDoubleToScalar(20));
391
392 SkString str;
393
394 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
395 if (clipStack) {
396 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
397 const SkClipStack::Element* elem;
398 double y = 0;
399 int total = 0;
edisonn@google.com91ce6982013-08-05 20:45:40 +0000400 while ((elem = iter.next()) != NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000401 total++;
402 y += 30;
403
404 switch (elem->getType()) {
405 case SkClipStack::Element::kRect_Type:
406 canvas.drawRect(elem->getRect(), blueBorder);
407 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
408 break;
409 case SkClipStack::Element::kPath_Type:
410 canvas.drawPath(elem->getPath(), blueBorder);
411 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
412 break;
413 case SkClipStack::Element::kEmpty_Type:
414 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
415 break;
416 default:
417 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
418 break;
419 }
420 }
421
422 y += 30;
423 str.printf("Number of clips in stack: %i", total);
424 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
425 }
426
427 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
428 SkPath clipPath;
429 if (clipRegion.getBoundaryPath(&clipPath)) {
430 SkPaint redBorder;
431 redBorder.setColor(SK_ColorRED);
432 redBorder.setStyle(SkPaint::kStroke_Style);
433 canvas.drawPath(clipPath, redBorder);
434 }
435
436 canvas.flush();
437
438 SkString out;
439
440 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
441 // ... and other properties, to be able to debug th code easily
442
443 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
444 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
445 }
edisonn@google.com0f901902013-08-07 11:56:16 +0000446
447 if (ret && token->fType == kKeyword_TokenType && token->fKeyword && token->fKeywordLength > 0 && token->fKeywordLength < 100) {
448 strncpy(gLastKeyword, token->fKeyword, token->fKeywordLength);
449 gLastKeyword[token->fKeywordLength] = '\0';
450 } else {
451 gLastKeyword[0] = '\0';
452 }
453
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000454#endif
455
456 return ret;
457}
458
459
460
edisonn@google.com3aa35552013-08-14 18:26:20 +0000461typedef SkPdfResult (*PdfOperatorRenderer)(SkPdfContext*, SkCanvas*, PdfTokenLooper**);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000462
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000463SkTDict<PdfOperatorRenderer> gPdfOps(100);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000464
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000465
466template <typename T> class SkTDictWithDefaultConstructor : public SkTDict<T> {
467public:
468 SkTDictWithDefaultConstructor() : SkTDict<T>(10) {}
469};
470
edisonn@google.com3aa35552013-08-14 18:26:20 +0000471SkTDictWithDefaultConstructor<int> gRenderStats[kCount_SkPdfResult];
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000472
edisonn@google.com3aa35552013-08-14 18:26:20 +0000473const char* gRenderStatsNames[kCount_SkPdfResult] = {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000474 "Success",
475 "Partially implemented",
476 "Not yet implemented",
477 "Ignore Error",
478 "Error",
479 "Unsupported/Unknown"
480};
481
edisonn@google.com3aa35552013-08-14 18:26:20 +0000482static SkPdfResult DrawText(SkPdfContext* pdfContext,
483 const SkPdfNativeObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000484 SkCanvas* canvas)
485{
486
487 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
488 if (skfont == NULL) {
489 skfont = SkPdfFont::Default();
490 }
491
edisonn@google.com571c70b2013-07-10 17:09:50 +0000492 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000493 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "DrawText", _str, SkPdfNativeObject::_kAnyString_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000494 return kIgnoreError_SkPdfResult;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000495 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000496 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000497
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000498 SkUnencodedText binary(str);
499
500 SkDecodedText decoded;
501
502 if (skfont->encoding() == NULL) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000503 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingEncoding_SkPdfIssue, "draw text", _str, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000504 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000505 }
506
507 skfont->encoding()->decodeText(binary, &decoded);
508
509 SkPaint paint;
510 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
511 // Or maybe just not call setTextSize at all?
512 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
513 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
514 }
515
516// if (fCurFont && fCurFont->GetFontScale() != 0) {
517// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
518// }
519
520 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
521
edisonn@google.com6e49c342013-06-27 20:03:43 +0000522 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000523
edisonn@google.com3aa35552013-08-14 18:26:20 +0000524 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000525}
526
527// TODO(edisonn): create header files with declarations!
edisonn@google.com3aa35552013-08-14 18:26:20 +0000528SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
529SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
530SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
531SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000532
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000533// TODO(edisonn): perf!!!
534
535static SkColorTable* getGrayColortable() {
536 static SkColorTable* grayColortable = NULL;
537 if (grayColortable == NULL) {
538 SkPMColor* colors = new SkPMColor[256];
539 for (int i = 0 ; i < 256; i++) {
540 colors[i] = SkPreMultiplyARGB(255, i, i, i);
541 }
542 grayColortable = new SkColorTable(colors, 256);
543 }
544 return grayColortable;
545}
546
edisonn@google.comaf54a512013-09-13 19:33:42 +0000547static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream,
548 size_t uncompressedStreamLength,
549 int width, int height, int bytesPerLine,
550 int bpc, const SkString& colorSpace,
551 bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000552 SkBitmap* bitmap = new SkBitmap();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000553
edisonn@google.com571c70b2013-07-10 17:09:50 +0000554 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000555//#define MAX_COMPONENTS 10
556
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000557 // TODO(edisonn): assume start of lines are aligned at 32 bits?
558 // Is there a faster way to load the uncompressed stream into a bitmap?
559
560 // minimal support for now
edisonn@google.com063d7072013-08-16 15:05:08 +0000561 if ((colorSpace.equals("DeviceRGB") || colorSpace.equals("RGB")) && bpc == 8) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000562 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
563
564 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000565 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000566 for (int w = 0 ; w < width; w++) {
567 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
568 uncompressedStream[3 * w + 1],
569 uncompressedStream[3 * w + 2]);
570 i++;
571 }
572 uncompressedStream += bytesPerLine;
573 }
574
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000575 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
576 bitmap->setPixels(uncompressedStreamArgb);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000577 }
edisonn@google.com063d7072013-08-16 15:05:08 +0000578 else if ((colorSpace.equals("DeviceGray") || colorSpace.equals("Gray")) && bpc == 8) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000579 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
580
581 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000582 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000583 for (int w = 0 ; w < width; w++) {
584 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
585 uncompressedStream[w];
586 i++;
587 }
588 uncompressedStream += bytesPerLine;
589 }
590
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000591 bitmap->setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000592 width, height);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000593 bitmap->setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000594 }
595
edisonn@google.comaf54a512013-09-13 19:33:42 +0000596 // TODO(edisonn): pass color space and context here?
597 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI", NULL, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000598 return bitmap;
599}
600
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000601// utils
602
603// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
604// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
605// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
606// skia format, through a table
607
608// this functions returns the image, it does not look at the smask.
609
edisonn@google.com3aa35552013-08-14 18:26:20 +0000610static SkBitmap* getImageFromObjectCore(SkPdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000611 if (image == NULL || !image->hasStream()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000612 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", image, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000613 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000614 }
615
edisonn@google.com33f11b62013-08-14 21:35:27 +0000616 int bpc = (int)image->BitsPerComponent(pdfContext->fPdfDoc);
617 int width = (int)image->Width(pdfContext->fPdfDoc);
618 int height = (int)image->Height(pdfContext->fPdfDoc);
edisonn@google.com063d7072013-08-16 15:05:08 +0000619 SkString colorSpace("DeviceRGB");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000620
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000621 bool indexed = false;
622 SkPMColor colors[256];
623 int cnt = 0;
624
edisonn@google.com571c70b2013-07-10 17:09:50 +0000625 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
626 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000627 } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) {
628 SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc);
629 if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") &&
630 (array->objAtAIndex(1)->isName("DeviceRGB") || array->objAtAIndex(1)->isName("RGB")) &&
631 array->objAtAIndex(2)->isInteger() &&
632 array->objAtAIndex(3)->isHexString()
633 ) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000634 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI", image, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000635 indexed = true;
edisonn@google.com33f11b62013-08-14 21:35:27 +0000636 cnt = (int)array->objAtAIndex(2)->intValue() + 1;
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000637 if (cnt > 256) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000638 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space feature NYI, cnt > 256", image, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000639 return NULL;
640 }
641 SkColorTable colorTable(cnt);
642 NotOwnedString data = array->objAtAIndex(3)->strRef();
643 if (data.fBytes != (unsigned int)cnt * 3) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000644 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, "Image color table mismatch color space specs", array, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000645 return NULL;
646 }
647 for (int i = 0 ; i < cnt; i++) {
648 colors[i] = SkPreMultiplyARGB(0xff, data.fBuffer[3 * i], data.fBuffer[3 * i + 1], data.fBuffer[3 * i + 2]);
649 }
650 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000651 }
652
653/*
654 bool imageMask = image->imageMask();
655
656 if (imageMask) {
657 if (bpc != 0 && bpc != 1) {
658 // TODO(edisonn): report warning to be used in testing.
659 return SkBitmap();
660 }
661 bpc = 1;
662 }
663*/
664
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000665 const unsigned char* uncompressedStream = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000666 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000667
edisonn@google.com571c70b2013-07-10 17:09:50 +0000668 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000669
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000670 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000671 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000672 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", stream, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000673 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000674 }
675
edisonn@google.com571c70b2013-07-10 17:09:50 +0000676 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
677
678 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
edisonn@google.com063d7072013-08-16 15:05:08 +0000679 streamDict->getFilterAsName(NULL).equals("DCTDecode")) ||
edisonn@google.com571c70b2013-07-10 17:09:50 +0000680 (streamDict->isFilterAArray(NULL) &&
681 streamDict->getFilterAsArray(NULL)->size() > 0 &&
682 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
edisonn@google.com063d7072013-08-16 15:05:08 +0000683 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2().equals("DCTDecode")))) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000684 SkBitmap* bitmap = new SkBitmap();
685 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000686 return bitmap;
687 }
688
689
690
691 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
692// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
693// obj.GetDictionary().GetKey(PdfName("Filter")));
694// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
695// value = resolveReferenceObject(pdfContext->fPdfDoc,
696// &value->GetArray()[0]);
697// }
698// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
699// SkStream stream = SkStream::
700// SkImageDecoder::Factory()
701// }
702
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000703 // TODO(edisonn): assumes RGB for now, since it is the only onwe implemented
704 if (indexed) {
705 SkBitmap* bitmap = new SkBitmap();
706 bitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
707 SkColorTable* colorTable = new SkColorTable(colors, cnt);
708 bitmap->setPixels((void*)uncompressedStream, colorTable);
709 return bitmap;
710 }
711
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000712 int bytesPerLine = (int)(uncompressedStreamLength / height);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000713#ifdef PDF_TRACE
714 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000715 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000716 }
717#endif
718
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000719 SkBitmap* bitmap = transferImageStreamToBitmap(
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000720 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000721 (int)width, (int)height, bytesPerLine,
722 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000723 transparencyMask);
724
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000725 return bitmap;
726}
727
edisonn@google.com3aa35552013-08-14 18:26:20 +0000728static SkBitmap* getImageFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000729 if (!transparencyMask) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000730 if (!image->hasData(SkPdfNativeObject::kBitmap_Data)) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000731 SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000732 image->setData(bitmap, SkPdfNativeObject::kBitmap_Data);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000733 }
edisonn@google.com3aa35552013-08-14 18:26:20 +0000734 return (SkBitmap*) image->data(SkPdfNativeObject::kBitmap_Data);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000735 } else {
736 return getImageFromObjectCore(pdfContext, image, transparencyMask);
737 }
738}
739
edisonn@google.com3aa35552013-08-14 18:26:20 +0000740static SkBitmap* getSmaskFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000741 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000742
743 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000744 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000745 }
746
747 // TODO(edisonn): implement GS SMask. Default to empty right now.
edisonn@google.comaf54a512013-09-13 19:33:42 +0000748 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "implement GS SMask. Default to empty right now.", obj, pdfContext);
749
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000750 return pdfContext->fGraphicsState.fSMask;
751}
752
edisonn@google.com3aa35552013-08-14 18:26:20 +0000753static SkPdfResult doXObject_Image(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000754 if (skpdfimage == NULL) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000755 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000756 }
757
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000758 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
759 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000760
761 canvas->save();
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000762 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000763
edisonn@google.com571c70b2013-07-10 17:09:50 +0000764 SkScalar z = SkIntToScalar(0);
765 SkScalar one = SkIntToScalar(1);
766
767 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
768 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
769 SkMatrix flip;
770 SkAssertResult(flip.setPolyToPoly(from, to, 4));
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000771 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000772 solveImageFlip.preConcat(flip);
773 canvas->setMatrix(solveImageFlip);
edisonn@google.com0f901902013-08-07 11:56:16 +0000774
775#ifdef PDF_TRACE
776 SkPoint final[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
777 solveImageFlip.mapPoints(final, 4);
778 printf("IMAGE rect = ");
779 for (int i = 0; i < 4; i++) {
780 printf("(%f %f) ", SkScalarToDouble(final[i].x()), SkScalarToDouble(final[i].y()));
781 }
782 printf("\n");
783#endif // PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +0000784
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000785 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
786
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000787 // TODO(edisonn): soft mask type? alpha/luminosity.
edisonn@google.comaf54a512013-09-13 19:33:42 +0000788 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "implement soft mask type", skpdfimage, pdfContext);
789
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000790 SkPaint paint;
791 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
792
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000793 if (!sMask || sMask->empty()) {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000794 canvas->drawBitmapRect(*image, dst, &paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000795 } else {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000796 canvas->saveLayer(&dst, &paint);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000797 canvas->drawBitmapRect(*image, dst, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000798 SkPaint xfer;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000799 // TODO(edisonn): is the blend mode specified already implicitly/explicitly in pdf?
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000800 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000801 canvas->drawBitmapRect(*sMask, dst, &xfer);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000802 canvas->restore();
803 }
804
805 canvas->restore();
806
edisonn@google.com3aa35552013-08-14 18:26:20 +0000807 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000808}
809
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000810//TODO(edisonn): options for implementing isolation and knockout
811// 1) emulate them (current solution)
812// PRO: simple
813// CON: will need to use readPixels, which means serious perf issues
814// 2) Compile a plan for an array of matrixes, compose the result at the end
815// PRO: might be faster then 1, no need to readPixels
816// CON: multiple drawings (but on smaller areas), pay a price at loading pdf to compute a pdf draw plan
817// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
818// 3) support them natively in SkCanvas
819// PRO: simple
820// CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1)
821// 4) compile a plan using pathops, and render once without any fancy rules with backdrop
822// PRO: simple, fast
823// CON: pathops must be bug free first + time to compute new paths
824// pay a price at loading pdf to compute a pdf draw plan
825// 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 +0000826// 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 +0000827
828
829// TODO(edisonn): draw plan from point! - list of draw ops of a point, like a tree!
830// TODO(edisonn): Minimal PDF to draw some points - remove everything that it is not needed, save pdf uncompressed
831
832
833
edisonn@google.com3aa35552013-08-14 18:26:20 +0000834static void doGroup_before(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup, bool page) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000835 SkRect bboxOrig = bbox;
836 SkBitmap backdrop;
837 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
838// bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000839 SkPaint paint;
840 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
841 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000842}
843
edisonn@google.com251176e2013-08-01 13:24:00 +0000844// TODO(edisonn): non isolation implemented in skia
edisonn@google.com3aa35552013-08-14 18:26:20 +0000845//static void doGroup_after(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup) {
edisonn@google.com251176e2013-08-01 13:24:00 +0000846// if not isolated
847// canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000848//}
849
edisonn@google.com3aa35552013-08-14 18:26:20 +0000850static SkPdfResult doXObject_Form(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000851 if (!skobj || !skobj->hasStream()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000852 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000853 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000854 }
855
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000856 if (!skobj->has_BBox()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000857 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox", skobj, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000858 return kIgnoreError_SkPdfResult;
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000859 }
860
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000861 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000862
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000863
edisonn@google.com571c70b2013-07-10 17:09:50 +0000864 if (skobj->Resources(pdfContext->fPdfDoc)) {
865 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000866 }
867
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000868 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000869
edisonn@google.com571c70b2013-07-10 17:09:50 +0000870 if (skobj->has_Matrix()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000871 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000872 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
873 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
874 pdfContext->fGraphicsState.fMatrixTm = matrix;
875 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000876 // TODO(edisonn) reset matrixTm and matricTlm also?
877 }
878
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000879 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com0f901902013-08-07 11:56:16 +0000880 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000881
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000882 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000883
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000884 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
885 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000886
887 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
888 // For this PdfContentsTokenizer needs to be extended.
889
edisonn@google.come878e722013-07-29 19:10:58 +0000890 // This is a group?
891 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000892 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
893 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
edisonn@google.come878e722013-07-29 19:10:58 +0000894 }
895
edisonn@google.com571c70b2013-07-10 17:09:50 +0000896 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000897
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000898 SkPdfNativeTokenizer* tokenizer =
899 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000900 if (tokenizer != NULL) {
901 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
902 looper.loop();
903 delete tokenizer;
904 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000905
906 // TODO(edisonn): should we restore the variable stack at the same state?
907 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000908
909 if (skobj->has_Group()) {
910 canvas->restore();
911 }
912
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000913 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000914 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000915}
916
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000917
918// TODO(edisonn): Extract a class like ObjWithStream
edisonn@google.com3aa35552013-08-14 18:26:20 +0000919static SkPdfResult doXObject_Pattern(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfType1PatternDictionary* skobj) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000920 if (!skobj || !skobj->hasStream()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000921 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000922 return kIgnoreError_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000923 }
924
925 if (!skobj->has_BBox()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000926 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox", skobj, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000927 return kIgnoreError_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000928 }
929
930 PdfOp_q(pdfContext, canvas, NULL);
931
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000932
933 if (skobj->Resources(pdfContext->fPdfDoc)) {
934 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
935 }
936
edisonn@google.com0f901902013-08-07 11:56:16 +0000937 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000938
939 if (skobj->has_Matrix()) {
edisonn@google.com0f901902013-08-07 11:56:16 +0000940 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000941 }
942
edisonn@google.com0f901902013-08-07 11:56:16 +0000943 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000944
edisonn@google.com0f901902013-08-07 11:56:16 +0000945 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix);
946 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000947
948 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
949 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
950
951 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
952 // For this PdfContentsTokenizer needs to be extended.
953
954 SkPdfStream* stream = (SkPdfStream*)skobj;
955
956 SkPdfNativeTokenizer* tokenizer =
957 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
958 if (tokenizer != NULL) {
959 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
960 looper.loop();
961 delete tokenizer;
962 }
963
964 // TODO(edisonn): should we restore the variable stack at the same state?
965 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
966
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000967 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000968 return kPartial_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000969}
970
971
edisonn@google.com3aa35552013-08-14 18:26:20 +0000972//static SkPdfResult doXObject_PS(SkPdfContext* pdfContext, SkCanvas* canvas, const SkPdfNativeObject* obj) {
973// return kNYI_SkPdfResult;
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000974//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000975
edisonn@google.com3aa35552013-08-14 18:26:20 +0000976SkPdfResult doType3Char(SkPdfContext* pdfContext, SkCanvas* canvas, const SkPdfNativeObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000977 if (!skobj || !skobj->hasStream()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +0000978 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000979 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000980 }
981
982 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000983
984 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
985 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000986 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000987
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000988 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.come57c62d2013-08-07 18:04:15 +0000989 pdfContext->fGraphicsState.fCTM.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000990
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000991 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000992
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000993 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000994
995 SkRect rm = bBox;
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000996 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000997
998 SkTraceRect(rm, "bbox mapped");
999
1000 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
1001
1002 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
1003 // For this PdfContentsTokenizer needs to be extended.
1004
edisonn@google.com571c70b2013-07-10 17:09:50 +00001005 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001006
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001007 SkPdfNativeTokenizer* tokenizer =
1008 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001009 if (tokenizer != NULL) {
1010 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
1011 looper.loop();
1012 delete tokenizer;
1013 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001014
1015 // TODO(edisonn): should we restore the variable stack at the same state?
1016 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001017 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001018
edisonn@google.com3aa35552013-08-14 18:26:20 +00001019 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001020}
1021
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001022class CheckRecursiveRendering {
edisonn@google.com063d7072013-08-16 15:05:08 +00001023 SkPdfNativeObject* fObj;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001024public:
edisonn@google.com063d7072013-08-16 15:05:08 +00001025 CheckRecursiveRendering(SkPdfNativeObject* obj) : fObj(obj) {
1026 SkASSERT(!obj->inRendering());
1027 obj->startRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001028 }
1029
1030 ~CheckRecursiveRendering() {
edisonn@google.com063d7072013-08-16 15:05:08 +00001031 SkASSERT(fObj->inRendering());
1032 fObj->doneRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001033 }
1034
edisonn@google.com3aa35552013-08-14 18:26:20 +00001035 static bool IsInRendering(const SkPdfNativeObject* obj) {
edisonn@google.com063d7072013-08-16 15:05:08 +00001036 return obj->inRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001037 }
1038};
1039
edisonn@google.com063d7072013-08-16 15:05:08 +00001040static SkPdfResult doXObject(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfNativeObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001041 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001042 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kRecursiveReferencing_SkPdfIssue, "Recursive reverencing is invalid in draw objects", obj, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001043 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001044 }
1045
edisonn@google.com571c70b2013-07-10 17:09:50 +00001046 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001047
edisonn@google.com571c70b2013-07-10 17:09:50 +00001048 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001049 {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001050 case kImageDictionary_SkPdfNativeObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001051 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001052 case kType1FormDictionary_SkPdfNativeObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001053 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001054 //case kObjectDictionaryXObjectPS_SkPdfNativeObjectType:
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001055 //return doXObject_PS(skxobj.asPS());
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001056 default: {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001057 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != kNone_SkPdfNativeObjectType) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001058 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
1059 return doXObject_Pattern(pdfContext, canvas, pattern);
1060 }
edisonn@google.comaf54a512013-09-13 19:33:42 +00001061 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "doXObject", obj, pdfContext);
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001062 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001063 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001064 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001065}
1066
edisonn@google.com3aa35552013-08-14 18:26:20 +00001067static SkPdfResult doPage(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfPageObjectDictionary* skobj) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001068 if (!skobj || !skobj->isContentsAStream(pdfContext->fPdfDoc)) {
1069 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001070 return kNYI_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001071 }
1072
1073 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
1074
1075 if (!stream) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001076 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001077 return kIgnoreError_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001078 }
1079
edisonn@google.com73613c12013-08-22 15:48:35 +00001080 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
1081
1082 if (!pdfContext->fGraphicsState.fResources) {
1083 return kIgnoreError_SkPdfResult; // probably it is null because we have not implemented yet inheritance
1084 }
1085
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001086 if (CheckRecursiveRendering::IsInRendering(skobj)) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001087 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kRecursiveReferencing_SkPdfIssue, "Recursive reverencing is invalid in draw objects", skobj, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001088 return kIgnoreError_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001089 }
1090 CheckRecursiveRendering checkRecursion(skobj);
1091
1092
1093 PdfOp_q(pdfContext, canvas, NULL);
1094
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001095 // TODO(edisonn): MediaBox can be inherited!!!!
edisonn@google.comaf54a512013-09-13 19:33:42 +00001096 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MediaBox inheritance NYI", NULL, pdfContext);
1097
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001098 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001099 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001100 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
1101 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
1102 } else {
1103 canvas->save();
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001104 }
1105
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001106
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001107 SkPdfNativeTokenizer* tokenizer =
1108 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
1109 if (tokenizer != NULL) {
1110 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
1111 looper.loop();
1112 delete tokenizer;
1113 }
1114
1115 // TODO(edisonn): should we restore the variable stack at the same state?
1116 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1117 canvas->restore();
1118 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001119 return kPartial_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001120}
1121
edisonn@google.com3aa35552013-08-14 18:26:20 +00001122SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001123 // TODO(edisonn): create a new stack of parameters, so once we start a q,
1124 // it is not possible to see under the previous q?
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001125 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1126 canvas->save();
edisonn@google.comf68aed32013-08-22 15:37:21 +00001127 pdfContext->fObjectStack.nest();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001128 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001129}
1130
edisonn@google.com3aa35552013-08-14 18:26:20 +00001131SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comf68aed32013-08-22 15:37:21 +00001132 if (pdfContext->fStateStack.count() > 0) {
1133 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1134 pdfContext->fStateStack.pop();
1135 canvas->restore();
1136
edisonn@google.comaf54a512013-09-13 19:33:42 +00001137 if (pdfContext->fObjectStack.nests() == 0) {
1138 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackNestingOverflow_SkPdfIssue, "stack nesting overflow (q/Q)", NULL, pdfContext);
edisonn@google.comf68aed32013-08-22 15:37:21 +00001139 return kIgnoreError_SkPdfResult;
1140 } else {
1141 pdfContext->fObjectStack.unnest();
1142 }
1143 } else {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001144 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackOverflow_SkPdfIssue, "stack overflow (q/Q)", NULL, pdfContext);
edisonn@google.comf68aed32013-08-22 15:37:21 +00001145 return kIgnoreError_SkPdfResult;
1146 }
1147
edisonn@google.com3aa35552013-08-14 18:26:20 +00001148 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001149}
1150
edisonn@google.com3aa35552013-08-14 18:26:20 +00001151static SkPdfResult PdfOp_cm(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001152 EXPECT_OPERANDS("cm", pdfContext, 6);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001153 POP_NUMBER(pdfContext, f);
1154 POP_NUMBER(pdfContext, e);
1155 POP_NUMBER(pdfContext, d);
1156 POP_NUMBER(pdfContext, c);
1157 POP_NUMBER(pdfContext, b);
1158 POP_NUMBER(pdfContext, a);
1159 CHECK_PARAMETERS();
1160 double array[6] = {a, b, c, d, e, f};
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001161
1162 // a b
1163 // c d
1164 // e f
1165
1166 // 0 1
1167 // 2 3
1168 // 4 5
1169
1170 // sx ky
1171 // kx sy
1172 // tx ty
1173 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1174
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001175 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001176
1177#ifdef PDF_TRACE
1178 printf("cm ");
1179 for (int i = 0 ; i < 6 ; i++) {
1180 printf("%f ", array[i]);
1181 }
1182 printf("\n");
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001183 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001184#endif
1185
edisonn@google.com3aa35552013-08-14 18:26:20 +00001186 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001187}
1188
1189//leading TL Set the text leading, Tl
1190//, to leading, which is a number expressed in unscaled text
1191//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00001192static SkPdfResult PdfOp_TL(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001193 EXPECT_OPERANDS("TL", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001194 POP_NUMBER(pdfContext, ty);
1195 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001196
1197 pdfContext->fGraphicsState.fTextLeading = ty;
1198
edisonn@google.com3aa35552013-08-14 18:26:20 +00001199 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001200}
1201
edisonn@google.com3aa35552013-08-14 18:26:20 +00001202static SkPdfResult PdfOp_Td(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001203 EXPECT_OPERANDS("Td", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001204 POP_NUMBER(pdfContext, ty);
1205 POP_NUMBER(pdfContext, tx);
1206 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001207
edisonn@google.come57c62d2013-08-07 18:04:15 +00001208 double array[6] = {1, 0, 0, 1, tx, -ty};
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001209 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1210
1211 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1212 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1213
edisonn@google.com3aa35552013-08-14 18:26:20 +00001214 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001215}
1216
edisonn@google.com3aa35552013-08-14 18:26:20 +00001217static SkPdfResult PdfOp_TD(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001218 EXPECT_OPERANDS("TD", pdfContext, 2)
edisonn@google.combd2f3012013-08-22 14:18:04 +00001219 POP_NUMBER(pdfContext, ty);
1220 POP_NUMBER(pdfContext, tx);
1221 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001222
edisonn@google.com571c70b2013-07-10 17:09:50 +00001223 // TODO(edisonn): Create factory methods or constructors so native is hidden
1224 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001225 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001226
1227 PdfOp_TL(pdfContext, canvas, looper);
1228
edisonn@google.com571c70b2013-07-10 17:09:50 +00001229 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001230 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001231
edisonn@google.com571c70b2013-07-10 17:09:50 +00001232 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001233 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001234
edisonn@google.com3aa35552013-08-14 18:26:20 +00001235 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001236
1237 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
1238 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001239
1240 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001241}
1242
edisonn@google.com3aa35552013-08-14 18:26:20 +00001243static SkPdfResult PdfOp_Tm(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001244 EXPECT_OPERANDS("Tm", pdfContext, 6);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001245 POP_NUMBER(pdfContext, f);
1246 POP_NUMBER(pdfContext, e);
1247 POP_NUMBER(pdfContext, d);
1248 POP_NUMBER(pdfContext, c);
1249 POP_NUMBER(pdfContext, b);
1250 POP_NUMBER(pdfContext, a);
1251 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001252
1253 double array[6];
1254 array[0] = a;
1255 array[1] = b;
1256 array[2] = c;
1257 array[3] = d;
1258 array[4] = e;
1259 array[5] = f;
1260
1261 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001262 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
edisonn@google.come57c62d2013-08-07 18:04:15 +00001263 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001264
1265 // TODO(edisonn): Text positioning.
edisonn@google.comaf54a512013-09-13 19:33:42 +00001266 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Text positioning not implemented for 2+ chars", NULL, pdfContext);
1267
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001268 pdfContext->fGraphicsState.fMatrixTm = matrix;
1269 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1270
edisonn@google.com3aa35552013-08-14 18:26:20 +00001271 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001272}
1273
1274//— T* Move to the start of the next line. This operator has the same effect as the code
1275//0 Tl Td
1276//where Tl is the current leading parameter in the text state
edisonn@google.com3aa35552013-08-14 18:26:20 +00001277static SkPdfResult PdfOp_T_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001278 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
1279 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001280
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001281 pdfContext->fObjectStack.push(zero);
1282 pdfContext->fObjectStack.push(tl);
1283
edisonn@google.com3aa35552013-08-14 18:26:20 +00001284 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001285
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001286 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001287}
1288
edisonn@google.com3aa35552013-08-14 18:26:20 +00001289static SkPdfResult PdfOp_m(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001290 if (pdfContext->fGraphicsState.fPathClosed) {
1291 pdfContext->fGraphicsState.fPath.reset();
1292 pdfContext->fGraphicsState.fPathClosed = false;
1293 }
1294
edisonn@google.comaf54a512013-09-13 19:33:42 +00001295 EXPECT_OPERANDS("m", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001296 POP_NUMBER(pdfContext, y);
1297 POP_NUMBER(pdfContext, x);
1298 CHECK_PARAMETERS();
1299
1300 pdfContext->fGraphicsState.fCurPosY = y;
1301 pdfContext->fGraphicsState.fCurPosX = x;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001302
1303 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001304 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001305
edisonn@google.com3aa35552013-08-14 18:26:20 +00001306 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001307}
1308
edisonn@google.com3aa35552013-08-14 18:26:20 +00001309static SkPdfResult PdfOp_l(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001310 if (pdfContext->fGraphicsState.fPathClosed) {
1311 pdfContext->fGraphicsState.fPath.reset();
1312 pdfContext->fGraphicsState.fPathClosed = false;
1313 }
1314
edisonn@google.comaf54a512013-09-13 19:33:42 +00001315 EXPECT_OPERANDS("l", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001316 POP_NUMBER(pdfContext, y);
1317 POP_NUMBER(pdfContext, x);
1318 CHECK_PARAMETERS();
1319
1320 pdfContext->fGraphicsState.fCurPosY = y;
1321 pdfContext->fGraphicsState.fCurPosX = x;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001322
1323 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001324 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001325
edisonn@google.com3aa35552013-08-14 18:26:20 +00001326 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001327}
1328
edisonn@google.com3aa35552013-08-14 18:26:20 +00001329static SkPdfResult PdfOp_c(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001330 if (pdfContext->fGraphicsState.fPathClosed) {
1331 pdfContext->fGraphicsState.fPath.reset();
1332 pdfContext->fGraphicsState.fPathClosed = false;
1333 }
1334
edisonn@google.comaf54a512013-09-13 19:33:42 +00001335 EXPECT_OPERANDS("c", pdfContext, 6);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001336 POP_NUMBER(pdfContext, y3);
1337 POP_NUMBER(pdfContext, x3);
1338 POP_NUMBER(pdfContext, y2);
1339 POP_NUMBER(pdfContext, x2);
1340 POP_NUMBER(pdfContext, y1);
1341 POP_NUMBER(pdfContext, x1);
1342 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001343
1344 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001345 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1346 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001347
1348 pdfContext->fGraphicsState.fCurPosX = x3;
1349 pdfContext->fGraphicsState.fCurPosY = y3;
1350
edisonn@google.com3aa35552013-08-14 18:26:20 +00001351 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001352}
1353
edisonn@google.com3aa35552013-08-14 18:26:20 +00001354static SkPdfResult PdfOp_v(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001355 if (pdfContext->fGraphicsState.fPathClosed) {
1356 pdfContext->fGraphicsState.fPath.reset();
1357 pdfContext->fGraphicsState.fPathClosed = false;
1358 }
1359
edisonn@google.comaf54a512013-09-13 19:33:42 +00001360 EXPECT_OPERANDS("v", pdfContext, 4);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001361 POP_NUMBER(pdfContext, y3);
1362 POP_NUMBER(pdfContext, x3);
1363 POP_NUMBER(pdfContext, y2);
1364 POP_NUMBER(pdfContext, x2);
1365 CHECK_PARAMETERS();
1366
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001367 double y1 = pdfContext->fGraphicsState.fCurPosY;
1368 double x1 = pdfContext->fGraphicsState.fCurPosX;
1369
1370 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001371 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1372 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001373
1374 pdfContext->fGraphicsState.fCurPosX = x3;
1375 pdfContext->fGraphicsState.fCurPosY = y3;
1376
edisonn@google.com3aa35552013-08-14 18:26:20 +00001377 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001378}
1379
edisonn@google.com3aa35552013-08-14 18:26:20 +00001380static SkPdfResult PdfOp_y(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001381 if (pdfContext->fGraphicsState.fPathClosed) {
1382 pdfContext->fGraphicsState.fPath.reset();
1383 pdfContext->fGraphicsState.fPathClosed = false;
1384 }
1385
edisonn@google.comaf54a512013-09-13 19:33:42 +00001386 EXPECT_OPERANDS("y", pdfContext, 4);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001387 POP_NUMBER(pdfContext, y3);
1388 POP_NUMBER(pdfContext, x3);
1389 POP_NUMBER(pdfContext, y1);
1390 POP_NUMBER(pdfContext, x1);
1391 CHECK_PARAMETERS();
1392
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001393 double y2 = pdfContext->fGraphicsState.fCurPosY;
1394 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001395
1396 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001397 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1398 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001399
1400 pdfContext->fGraphicsState.fCurPosX = x3;
1401 pdfContext->fGraphicsState.fCurPosY = y3;
1402
edisonn@google.com3aa35552013-08-14 18:26:20 +00001403 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001404}
1405
edisonn@google.com3aa35552013-08-14 18:26:20 +00001406static SkPdfResult PdfOp_re(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001407 if (pdfContext->fGraphicsState.fPathClosed) {
1408 pdfContext->fGraphicsState.fPath.reset();
1409 pdfContext->fGraphicsState.fPathClosed = false;
1410 }
1411
edisonn@google.comaf54a512013-09-13 19:33:42 +00001412 EXPECT_OPERANDS("re", pdfContext, 4);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001413 POP_NUMBER(pdfContext, height);
1414 POP_NUMBER(pdfContext, width);
1415 POP_NUMBER(pdfContext, y);
1416 POP_NUMBER(pdfContext, x);
1417 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001418
1419 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
edisonn@google.combd2f3012013-08-22 14:18:04 +00001420 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001421
1422 pdfContext->fGraphicsState.fCurPosX = x;
1423 pdfContext->fGraphicsState.fCurPosY = y + height;
1424
edisonn@google.com3aa35552013-08-14 18:26:20 +00001425 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001426}
1427
edisonn@google.com3aa35552013-08-14 18:26:20 +00001428static SkPdfResult PdfOp_h(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001429 pdfContext->fGraphicsState.fPath.close();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001430 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001431}
1432
edisonn@google.com3aa35552013-08-14 18:26:20 +00001433static SkPdfResult PdfOp_fillAndStroke(SkPdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001434 SkPath path = pdfContext->fGraphicsState.fPath;
1435
1436 if (close) {
1437 path.close();
1438 }
1439
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001440 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001441
1442 SkPaint paint;
1443
1444 SkPoint line[2];
1445 if (fill && !stroke && path.isLine(line)) {
1446 paint.setStyle(SkPaint::kStroke_Style);
1447
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001448 // TODO(edisonn): implement this with patterns
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001449 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1450 paint.setStrokeWidth(SkDoubleToScalar(0));
1451
1452 canvas->drawPath(path, paint);
1453 } else {
1454 if (fill) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001455 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0 &&
1456 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1457
1458 // TODO(edisonn): we can use a shader here, like imageshader to draw fast. ultimately,
1459 // if this is not possible, and we are in rasper mode, and the cells don't intersect, we could even have multiple cpus.
1460
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001461 PdfOp_q(pdfContext, canvas, NULL);
1462
1463 if (evenOdd) {
1464 path.setFillType(SkPath::kEvenOdd_FillType);
1465 }
1466 canvas->clipPath(path);
1467
edisonn@google.com3aa35552013-08-14 18:26:20 +00001468 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(pdfContext->fGraphicsState.fNonStroking.fPattern) != kNone_SkPdfNativeObjectType) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001469 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState.fNonStroking.fPattern;
1470
1471 // TODO(edisonn): constants
1472 // TODO(edisonn): colored
1473 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001474 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
1475 // it will change the result iterating in reverse
1476 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
1477 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001478
edisonn@google.comaf54a512013-09-13 19:33:42 +00001479 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "paterns x/y step is forced to positive number", pattern, pdfContext);
1480
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001481 SkRect bounds = path.getBounds();
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001482
1483 // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001484 // TODO(edisonn): don't do that!
1485 bounds.sort();
1486
1487 SkScalar x;
1488 SkScalar y;
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001489
1490 y = bounds.top();
1491 int totalx = 0;
1492 int totaly = 0;
1493 while (y < bounds.bottom()) {
1494 x = bounds.left();
1495 totalx = 0;
1496
1497 while (x < bounds.right()) {
1498 doXObject(pdfContext, canvas, pattern);
1499
edisonn@google.com0f901902013-08-07 11:56:16 +00001500 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(xStep), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001501 totalx += xStep;
1502 x += SkIntToScalar(xStep);
1503 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001504 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(-totalx), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001505
edisonn@google.com0f901902013-08-07 11:56:16 +00001506 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(-yStep));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001507 totaly += yStep;
1508 y += SkIntToScalar(yStep);
1509 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001510 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(totaly));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001511 }
1512 }
1513
1514 // apply matrix
1515 // get xstep, y step, bbox ... for cliping, and bos of the path
1516
1517 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001518 } else {
1519 paint.setStyle(SkPaint::kFill_Style);
1520 if (evenOdd) {
1521 path.setFillType(SkPath::kEvenOdd_FillType);
1522 }
1523
1524 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1525
1526 canvas->drawPath(path, paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001527 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001528 }
1529
1530 if (stroke) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001531 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0) {
1532 // TODO(edisonn): implement Pattern for strokes
1533 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001534
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001535 paint.setColor(SK_ColorGREEN);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001536
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001537 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1538 canvas->drawPath(path, paint);
1539 } else {
1540 paint.setStyle(SkPaint::kStroke_Style);
1541
1542 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1543
1544 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1545 canvas->drawPath(path, paint);
1546 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001547 }
1548 }
1549
1550 pdfContext->fGraphicsState.fPath.reset();
1551 // todo zoom ... other stuff ?
1552
1553 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1554#ifndef PDF_DEBUG_NO_CLIPING
1555 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1556#endif
1557 }
1558
1559 //pdfContext->fGraphicsState.fClipPath.reset();
1560 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1561
edisonn@google.com3aa35552013-08-14 18:26:20 +00001562 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001563
1564}
1565
edisonn@google.com3aa35552013-08-14 18:26:20 +00001566static SkPdfResult PdfOp_S(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001567 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1568}
1569
edisonn@google.com3aa35552013-08-14 18:26:20 +00001570static SkPdfResult PdfOp_s(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001571 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1572}
1573
edisonn@google.com3aa35552013-08-14 18:26:20 +00001574static SkPdfResult PdfOp_F(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001575 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1576}
1577
edisonn@google.com3aa35552013-08-14 18:26:20 +00001578static SkPdfResult PdfOp_f(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001579 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1580}
1581
edisonn@google.com3aa35552013-08-14 18:26:20 +00001582static SkPdfResult PdfOp_f_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001583 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1584}
1585
edisonn@google.com3aa35552013-08-14 18:26:20 +00001586static SkPdfResult PdfOp_B(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001587 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1588}
1589
edisonn@google.com3aa35552013-08-14 18:26:20 +00001590static SkPdfResult PdfOp_B_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001591 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1592}
1593
edisonn@google.com3aa35552013-08-14 18:26:20 +00001594static SkPdfResult PdfOp_b(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001595 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1596}
1597
edisonn@google.com3aa35552013-08-14 18:26:20 +00001598static SkPdfResult PdfOp_b_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001599 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1600}
1601
edisonn@google.com3aa35552013-08-14 18:26:20 +00001602static SkPdfResult PdfOp_n(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001603 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001604 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1605#ifndef PDF_DEBUG_NO_CLIPING
1606 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1607#endif
1608 }
1609
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001610 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1611
1612 pdfContext->fGraphicsState.fPathClosed = true;
1613
edisonn@google.com3aa35552013-08-14 18:26:20 +00001614 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001615}
1616
edisonn@google.com3aa35552013-08-14 18:26:20 +00001617static SkPdfResult PdfOp_BT(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001618 pdfContext->fGraphicsState.fTextBlock = true;
edisonn@google.come57c62d2013-08-07 18:04:15 +00001619 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
1620 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
1621 pdfContext->fGraphicsState.fMatrixTm = matrix;
1622 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001623
edisonn@google.com3aa35552013-08-14 18:26:20 +00001624 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001625}
1626
edisonn@google.com3aa35552013-08-14 18:26:20 +00001627static SkPdfResult PdfOp_ET(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001628 if (!pdfContext->fGraphicsState.fTextBlock) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001629 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "ET without BT", NULL, pdfContext);
1630
edisonn@google.com3aa35552013-08-14 18:26:20 +00001631 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001632 }
1633 // 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 +00001634 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001635}
1636
edisonn@google.com33f11b62013-08-14 21:35:27 +00001637static SkPdfResult skpdfGraphicsStateApplyFontCore(SkPdfContext* pdfContext, const SkPdfNativeObject* fontName, double fontSize) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001638#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001639 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001640#endif
1641
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001642 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1643 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.comaf54a512013-09-13 19:33:42 +00001644 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingFont_SkPdfIssue, "No font", fontName, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001645 return kIgnoreError_SkPdfResult;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001646 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001647
edisonn@google.com3aa35552013-08-14 18:26:20 +00001648 SkPdfNativeObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001649 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001650 if (kNone_SkPdfNativeObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001651 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.comaf54a512013-09-13 19:33:42 +00001652 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kInvalidFont_SkPdfIssue, "Invalid font", objFont, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001653 return kIgnoreError_SkPdfResult;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001654 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001655
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001656 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1657
1658 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1659
1660 if (skfont) {
1661 pdfContext->fGraphicsState.fSkFont = skfont;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001662 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001663 pdfContext->fGraphicsState.fCurFontSize = fontSize;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001664 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001665}
1666
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001667//font size Tf Set the text font, Tf
1668//, to font and the text font size, Tfs, to size. font is the name of a
1669//font resource in the Fontsubdictionary of the current resource dictionary; size is
1670//a number representing a scale factor. There is no initial value for either font or
1671//size; they must be specified explicitly using Tf before any text is shown.
edisonn@google.com3aa35552013-08-14 18:26:20 +00001672static SkPdfResult PdfOp_Tf(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001673 EXPECT_OPERANDS("Tf", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001674 POP_NUMBER(pdfContext, fontSize);
1675 POP_NAME(pdfContext, fontName);
1676 CHECK_PARAMETERS();
1677
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001678 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1679}
1680
edisonn@google.com3aa35552013-08-14 18:26:20 +00001681static SkPdfResult PdfOp_Tj(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001682 EXPECT_OPERANDS("Tj", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001683 POP_STRING(pdfContext, str);
1684 CHECK_PARAMETERS();
1685
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001686 if (!pdfContext->fGraphicsState.fTextBlock) {
1687 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.comaf54a512013-09-13 19:33:42 +00001688 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "Tj without BT", NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001689 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001690 }
1691
edisonn@google.combd2f3012013-08-22 14:18:04 +00001692 SkPdfResult ret = DrawText(pdfContext, str, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001693
1694 return ret;
1695}
1696
edisonn@google.com3aa35552013-08-14 18:26:20 +00001697static SkPdfResult PdfOp_quote(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001698 if (!pdfContext->fGraphicsState.fTextBlock) {
1699 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.comaf54a512013-09-13 19:33:42 +00001700 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "' without BT", NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001701 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001702 }
1703
1704 PdfOp_T_star(pdfContext, canvas, looper);
1705 // Do not pop, and push, just transfer the param to Tj
1706 return PdfOp_Tj(pdfContext, canvas, looper);
1707}
1708
edisonn@google.com3aa35552013-08-14 18:26:20 +00001709static SkPdfResult PdfOp_doublequote(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001710 if (!pdfContext->fGraphicsState.fTextBlock) {
1711 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.comaf54a512013-09-13 19:33:42 +00001712 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "\" without BT", NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001713 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001714 }
1715
edisonn@google.comaf54a512013-09-13 19:33:42 +00001716 EXPECT_OPERANDS("\"", pdfContext, 3);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001717 POP_OBJ(pdfContext, str);
1718 POP_OBJ(pdfContext, ac);
1719 POP_OBJ(pdfContext, aw);
1720 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001721
1722 pdfContext->fObjectStack.push(aw);
1723 PdfOp_Tw(pdfContext, canvas, looper);
1724
1725 pdfContext->fObjectStack.push(ac);
1726 PdfOp_Tc(pdfContext, canvas, looper);
1727
1728 pdfContext->fObjectStack.push(str);
1729 PdfOp_quote(pdfContext, canvas, looper);
1730
edisonn@google.com3aa35552013-08-14 18:26:20 +00001731 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001732}
1733
edisonn@google.com3aa35552013-08-14 18:26:20 +00001734static SkPdfResult PdfOp_TJ(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001735 EXPECT_OPERANDS("Tf", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001736 POP_ARRAY(pdfContext, array);
1737 CHECK_PARAMETERS();
1738
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001739 if (!pdfContext->fGraphicsState.fTextBlock) {
1740 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.comaf54a512013-09-13 19:33:42 +00001741 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "TJ without BT", NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001742 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001743 }
1744
edisonn@google.com571c70b2013-07-10 17:09:50 +00001745 if (!array->isArray()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001746 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, array, SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001747 return kIgnoreError_SkPdfResult;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001748 }
1749
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001750 for( int i=0; i<static_cast<int>(array->size()); i++ )
1751 {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001752 if (!(*array)[i]) {
1753 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "element [i] is null, no element should be null", array, SkPdfNativeObject::_kAnyString_PdfObjectType || SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
1754 } else if( (*array)[i]->isAnyString()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001755 SkPdfNativeObject* obj = (*array)[i];
edisonn@google.combd2f3012013-08-22 14:18:04 +00001756 DrawText(pdfContext, obj, canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001757 } else if ((*array)[i]->isNumber()) {
1758 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001759 SkMatrix matrix;
1760 matrix.setAll(SkDoubleToScalar(1),
1761 SkDoubleToScalar(0),
1762 // TODO(edisonn): use writing mode, vertical/horizontal.
1763 SkDoubleToScalar(-dx), // amount is substracted!!!
1764 SkDoubleToScalar(0),
1765 SkDoubleToScalar(1),
1766 SkDoubleToScalar(0),
1767 SkDoubleToScalar(0),
1768 SkDoubleToScalar(0),
1769 SkDoubleToScalar(1));
1770
1771 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
edisonn@google.comaf54a512013-09-13 19:33:42 +00001772 } else {
1773 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", (*array)[i], SkPdfNativeObject::kArray_PdfObjectType || SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001774 }
1775 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001776 return kPartial_SkPdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001777}
1778
edisonn@google.com3aa35552013-08-14 18:26:20 +00001779static SkPdfResult PdfOp_CS_cs(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001780 EXPECT_OPERANDS("CS/cs", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001781 POP_NAME(pdfContext, name);
1782 CHECK_PARAMETERS();
edisonn@google.com4f898b72013-08-07 21:11:57 +00001783
1784 //Next, get the ColorSpace Dictionary from the Resource Dictionary:
1785 SkPdfDictionary* colorSpaceResource = pdfContext->fGraphicsState.fResources->ColorSpace(pdfContext->fPdfDoc);
1786
edisonn@google.com3aa35552013-08-14 18:26:20 +00001787 SkPdfNativeObject* colorSpace = colorSpaceResource ? pdfContext->fPdfDoc->resolveReference(colorSpaceResource->get(name)) : name;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001788
1789 if (colorSpace == NULL) {
1790 colorOperator->fColorSpace = name->strRef();
1791 } else {
1792#ifdef PDF_TRACE
1793 printf("CS = %s\n", colorSpace->toString(0, 0).c_str());
1794#endif // PDF_TRACE
1795 if (colorSpace->isName()) {
1796 colorOperator->fColorSpace = colorSpace->strRef();
1797 } else if (colorSpace->isArray()) {
1798 int cnt = colorSpace->size();
1799 if (cnt == 0) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001800 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, "color space has length 0", colorSpace, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001801 return kIgnoreError_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001802 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001803 SkPdfNativeObject* type = colorSpace->objAtAIndex(0);
edisonn@google.com4f898b72013-08-07 21:11:57 +00001804 type = pdfContext->fPdfDoc->resolveReference(type);
1805
1806 if (type->isName("ICCBased")) {
1807 if (cnt != 2) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001808 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, "ICCBased color space must have an array with 2 elements", colorSpace, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001809 return kIgnoreError_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001810 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001811 SkPdfNativeObject* prop = colorSpace->objAtAIndex(1);
edisonn@google.com4f898b72013-08-07 21:11:57 +00001812 prop = pdfContext->fPdfDoc->resolveReference(prop);
1813#ifdef PDF_TRACE
1814 printf("ICCBased prop = %s\n", prop->toString(0, 0).c_str());
1815#endif // PDF_TRACE
1816 // TODO(edisonn): hack
1817 if (prop && prop->isDictionary() && prop->get("N") && prop->get("N")->isInteger() && prop->get("N")->intValue() == 3) {
1818 colorOperator->setColorSpace(&strings_DeviceRGB);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001819 return kPartial_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001820 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001821 return kNYI_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001822 }
1823 }
1824 }
1825
edisonn@google.com3aa35552013-08-14 18:26:20 +00001826 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001827}
1828
edisonn@google.com3aa35552013-08-14 18:26:20 +00001829static SkPdfResult PdfOp_CS(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001830 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1831}
1832
edisonn@google.com3aa35552013-08-14 18:26:20 +00001833static SkPdfResult PdfOp_cs(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001834 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1835}
1836
edisonn@google.com3aa35552013-08-14 18:26:20 +00001837static SkPdfResult PdfOp_SC_sc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001838 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001839// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001840
1841 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1842
1843 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001844 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001845 doubles = false;
1846 }
1847
1848#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001849 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001850#endif
1851
edisonn@google.comaf54a512013-09-13 19:33:42 +00001852 EXPECT_OPERANDS("SC/sc", pdfContext, n);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001853
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001854 for (int i = n - 1; i >= 0 ; i--) {
1855 if (doubles) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001856 POP_NUMBER_INTO(pdfContext, c[i]);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001857// } else {
1858// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001859 }
1860 }
edisonn@google.combd2f3012013-08-22 14:18:04 +00001861 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001862
1863 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001864 // TODO(edisonn): do possible field values to enum at parsing time!
1865 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001866 if (colorOperator->fColorSpace.equals("DeviceRGB") || colorOperator->fColorSpace.equals("RGB")) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001867 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]), (U8CPU)(255*c[1]), (U8CPU)(255*c[2])));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001868 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001869 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001870}
1871
edisonn@google.com3aa35552013-08-14 18:26:20 +00001872static SkPdfResult PdfOp_SC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001873 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1874}
1875
edisonn@google.com3aa35552013-08-14 18:26:20 +00001876static SkPdfResult PdfOp_sc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001877 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1878}
1879
edisonn@google.com3aa35552013-08-14 18:26:20 +00001880static SkPdfResult PdfOp_SCN_scn(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001881 if (pdfContext->fObjectStack.count() > 0 && pdfContext->fObjectStack.top()->isName()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001882 SkPdfNativeObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com276fed92013-08-01 21:20:47 +00001883
1884 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001885 SkPdfDictionary* patternResources = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
edisonn@google.com276fed92013-08-01 21:20:47 +00001886
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001887 if (patternResources == NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001888#ifdef PDF_TRACE
1889 printf("ExtGState is NULL!\n");
1890#endif
edisonn@google.com3aa35552013-08-14 18:26:20 +00001891 return kIgnoreError_SkPdfResult;
edisonn@google.com276fed92013-08-01 21:20:47 +00001892 }
1893
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001894 colorOperator->setPatternColorSpace(pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001895 }
1896
1897 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1898 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1899
edisonn@google.com3aa35552013-08-14 18:26:20 +00001900 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001901}
1902
edisonn@google.com3aa35552013-08-14 18:26:20 +00001903static SkPdfResult PdfOp_SCN(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001904 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1905}
1906
edisonn@google.com3aa35552013-08-14 18:26:20 +00001907static SkPdfResult PdfOp_scn(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001908 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1909}
1910
edisonn@google.com3aa35552013-08-14 18:26:20 +00001911static SkPdfResult PdfOp_G_g(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001912 EXPECT_OPERANDS("G/g", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001913 POP_NUMBER(pdfContext, gray);
1914 CHECK_PARAMETERS();
1915
1916 colorOperator->fColorSpace = strings_DeviceRGB; // TODO(edisonn): HACK - it should be device gray, but not suported right now
1917 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*gray), (U8CPU)(255*gray), (U8CPU)(255*gray)));
1918
1919 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001920}
1921
edisonn@google.com3aa35552013-08-14 18:26:20 +00001922static SkPdfResult PdfOp_G(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001923 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1924}
1925
edisonn@google.com3aa35552013-08-14 18:26:20 +00001926static SkPdfResult PdfOp_g(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001927 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1928}
1929
edisonn@google.com3aa35552013-08-14 18:26:20 +00001930static SkPdfResult PdfOp_RG_rg(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001931 EXPECT_OPERANDS("RG/rg", pdfContext, 3);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001932 POP_NUMBER(pdfContext, b);
1933 POP_NUMBER(pdfContext, g);
1934 POP_NUMBER(pdfContext, r);
1935 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001936
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001937 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001938 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
edisonn@google.com3aa35552013-08-14 18:26:20 +00001939 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001940}
1941
edisonn@google.com3aa35552013-08-14 18:26:20 +00001942static SkPdfResult PdfOp_RG(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001943 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1944}
1945
edisonn@google.com3aa35552013-08-14 18:26:20 +00001946static SkPdfResult PdfOp_rg(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001947 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1948}
1949
edisonn@google.com3aa35552013-08-14 18:26:20 +00001950static SkPdfResult PdfOp_K_k(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001951 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.comaf54a512013-09-13 19:33:42 +00001952 EXPECT_OPERANDS("K/k", pdfContext, 4);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001953 POP_NUMBER(pdfContext, k);
1954 POP_NUMBER(pdfContext, y);
1955 POP_NUMBER(pdfContext, m);
1956 POP_NUMBER(pdfContext, c);
1957 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001958
edisonn@google.combd2f3012013-08-22 14:18:04 +00001959 // TODO(edisonn): silly way to remove compiler warning
1960 if (k + y + m + c == 0) {
1961 return kNYI_SkPdfResult;
1962 }
1963
1964 //colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001965 // TODO(edisonn): Set color.
edisonn@google.com3aa35552013-08-14 18:26:20 +00001966 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001967}
1968
edisonn@google.com3aa35552013-08-14 18:26:20 +00001969static SkPdfResult PdfOp_K(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001970 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1971}
1972
edisonn@google.com3aa35552013-08-14 18:26:20 +00001973static SkPdfResult PdfOp_k(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001974 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1975}
1976
edisonn@google.com3aa35552013-08-14 18:26:20 +00001977static SkPdfResult PdfOp_W(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001978 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1979 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1980
edisonn@google.com3aa35552013-08-14 18:26:20 +00001981 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001982}
1983
edisonn@google.com3aa35552013-08-14 18:26:20 +00001984static SkPdfResult PdfOp_W_star(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001985 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1986
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001987 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1988 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1989
edisonn@google.com3aa35552013-08-14 18:26:20 +00001990 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001991}
1992
edisonn@google.com3aa35552013-08-14 18:26:20 +00001993static SkPdfResult PdfOp_BX(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001994 *looper = new PdfCompatibilitySectionLooper();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001995 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001996}
1997
edisonn@google.com3aa35552013-08-14 18:26:20 +00001998static SkPdfResult PdfOp_EX(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001999 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue, "EX operator should not be called, it is habdled in a looper, unless the file is corrupted, we should assert", NULL, pdfContext);
2000
edisonn@google.com3aa35552013-08-14 18:26:20 +00002001 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002002}
2003
edisonn@google.com3aa35552013-08-14 18:26:20 +00002004static SkPdfResult PdfOp_BI(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002005 *looper = new PdfInlineImageLooper();
edisonn@google.com3aa35552013-08-14 18:26:20 +00002006 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002007}
2008
edisonn@google.com3aa35552013-08-14 18:26:20 +00002009static SkPdfResult PdfOp_ID(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002010 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue, "ID operator should not be called, it is habdled in a looper, unless the file is corrupted, we should assert", NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002011 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002012}
2013
edisonn@google.com3aa35552013-08-14 18:26:20 +00002014static SkPdfResult PdfOp_EI(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002015 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue, "EI operator should not be called, it is habdled in a looper, unless the file is corrupted, we should assert", NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002016 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002017}
2018
edisonn@google.com24cdf132013-07-30 16:06:12 +00002019// TODO(edisonn): security review here, make sure all parameters are valid, and safe.
edisonn@google.com33f11b62013-08-14 21:35:27 +00002020static SkPdfResult skpdfGraphicsStateApply_ca(SkPdfContext* pdfContext, double ca) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002021 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002022 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002023}
2024
edisonn@google.com33f11b62013-08-14 21:35:27 +00002025static SkPdfResult skpdfGraphicsStateApply_CA(SkPdfContext* pdfContext, double CA) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002026 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002027 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002028}
2029
edisonn@google.com33f11b62013-08-14 21:35:27 +00002030static SkPdfResult skpdfGraphicsStateApplyLW(SkPdfContext* pdfContext, double lineWidth) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002031 pdfContext->fGraphicsState.fLineWidth = lineWidth;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002032 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002033}
2034
edisonn@google.com33f11b62013-08-14 21:35:27 +00002035static SkPdfResult skpdfGraphicsStateApplyLC(SkPdfContext* pdfContext, int64_t lineCap) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002036 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002037 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002038}
2039
edisonn@google.com33f11b62013-08-14 21:35:27 +00002040static SkPdfResult skpdfGraphicsStateApplyLJ(SkPdfContext* pdfContext, int64_t lineJoin) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002041 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002042 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002043}
2044
edisonn@google.com33f11b62013-08-14 21:35:27 +00002045static SkPdfResult skpdfGraphicsStateApplyML(SkPdfContext* pdfContext, double miterLimit) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002046 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002047 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002048}
2049
2050// TODO(edisonn): implement all rules, as of now 3) and 5) and 6) do not seem suported by skia, but I am not sure
2051/*
20521) [ ] 0 No dash; solid, unbroken lines
20532) [3] 0 3 units on, 3 units off, …
20543) [2] 1 1 on, 2 off, 2 on, 2 off, …
20554) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
20565) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
20576) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
2058 */
2059
edisonn@google.com33f11b62013-08-14 21:35:27 +00002060static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* intervals, SkPdfNativeObject* phase) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002061 if (intervals == NULL) {
2062 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, intervals, SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
2063 return kIgnoreError_SkPdfResult;
2064 }
2065
2066 if (phase == NULL) {
2067 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, phase, SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
2068 return kIgnoreError_SkPdfResult;
2069 }
2070
edisonn@google.com24cdf132013-07-30 16:06:12 +00002071 int cnt = intervals->size();
2072 if (cnt >= 256) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002073 // TODO(edisonn): alloc memory
edisonn@google.comaf54a512013-09-13 19:33:42 +00002074 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "dash array size unssuported, cnt > 256", intervals, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002075 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002076 }
2077 for (int i = 0; i < cnt; i++) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002078 if (!intervals->objAtAIndex(i) || !intervals->objAtAIndex(i)->isNumber()) {
2079 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, intervals->objAtAIndex(i), SkPdfNativeObject::_kNumber_PdfObjectType, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002080 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002081 }
2082 }
2083
edisonn@google.com24cdf132013-07-30 16:06:12 +00002084 double total = 0;
2085 for (int i = 0 ; i < cnt; i++) {
2086 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
2087 total += pdfContext->fGraphicsState.fDashArray[i];
2088 }
edisonn@google.comf111a4b2013-07-31 18:22:36 +00002089 if (cnt & 1) {
2090 if (cnt == 1) {
2091 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
2092 cnt++;
2093 } else {
2094 // TODO(edisonn): report error/warning
edisonn@google.com3aa35552013-08-14 18:26:20 +00002095 return kNYI_SkPdfResult;
edisonn@google.comf111a4b2013-07-31 18:22:36 +00002096 }
2097 }
2098 pdfContext->fGraphicsState.fDashArrayLength = cnt;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002099 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
2100 if (pdfContext->fGraphicsState.fDashPhase == 0) {
2101 // other rules, changes?
edisonn@google.com063d7072013-08-16 15:05:08 +00002102 pdfContext->fGraphicsState.fDashPhase = SkDoubleToScalar(total);
edisonn@google.com24cdf132013-07-30 16:06:12 +00002103 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002104
edisonn@google.com3aa35552013-08-14 18:26:20 +00002105 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002106}
2107
edisonn@google.com33f11b62013-08-14 21:35:27 +00002108static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* dash) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002109 if (!dash || dash->isArray()) {
2110 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash, SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002111 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002112 }
edisonn@google.comaf54a512013-09-13 19:33:42 +00002113
2114 if (dash->size() != 2) {
2115 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, "hash array must have 2 elements", dash, pdfContext);
2116 return kIgnoreError_SkPdfResult;
2117 }
2118
2119 if (!dash->objAtAIndex(0) || !dash->objAtAIndex(0)->isArray()) {
2120 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash->objAtAIndex(0), SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
2121 return kIgnoreError_SkPdfResult;
2122 }
2123
2124 if (!dash->objAtAIndex(1) || !dash->objAtAIndex(1)->isNumber()) {
2125 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash->objAtAIndex(1), SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
2126 return kIgnoreError_SkPdfResult;
2127 }
2128
edisonn@google.com24cdf132013-07-30 16:06:12 +00002129 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0), dash->objAtAIndex(1));
2130}
2131
edisonn@google.com33f11b62013-08-14 21:35:27 +00002132static void skpdfGraphicsStateApplyFont(SkPdfContext* pdfContext, SkPdfArray* fontAndSize) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002133 if (!fontAndSize || !fontAndSize->isArray()) {
2134 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, fontAndSize, SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.com24cdf132013-07-30 16:06:12 +00002135 return;
2136 }
edisonn@google.comaf54a512013-09-13 19:33:42 +00002137
2138 if (fontAndSize->size() != 2) {
2139 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, "font array must have 2 elements", fontAndSize, pdfContext);
2140 return;
2141 }
2142
2143 if (!fontAndSize->objAtAIndex(0) || !fontAndSize->objAtAIndex(0)->isName()) {
2144 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, fontAndSize->objAtAIndex(0), SkPdfNativeObject::kName_PdfObjectType, pdfContext);
2145 return;
2146 }
2147
2148
2149 if (!fontAndSize->objAtAIndex(1) || !fontAndSize->objAtAIndex(1)->isNumber()) {
2150 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, fontAndSize->objAtAIndex(0), SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
2151 return;
2152 }
2153
edisonn@google.com24cdf132013-07-30 16:06:12 +00002154 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), fontAndSize->objAtAIndex(1)->numberValue());
2155}
2156
2157
2158//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002159static SkPdfResult PdfOp_w(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002160 EXPECT_OPERANDS("w", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002161 POP_NUMBER(pdfContext, lw);
2162 CHECK_PARAMETERS();
2163
edisonn@google.com24cdf132013-07-30 16:06:12 +00002164 return skpdfGraphicsStateApplyLW(pdfContext, lw);
2165}
2166
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002167//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 +00002168static SkPdfResult PdfOp_J(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com063d7072013-08-16 15:05:08 +00002169 // TODO(edisonn): round/ceil to int?
edisonn@google.comaf54a512013-09-13 19:33:42 +00002170 EXPECT_OPERANDS("J", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002171 POP_NUMBER(pdfContext, lc);
2172 CHECK_PARAMETERS();
2173
2174 return skpdfGraphicsStateApplyLC(pdfContext, (int)lc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002175}
2176
2177//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 +00002178static SkPdfResult PdfOp_j(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com063d7072013-08-16 15:05:08 +00002179 // TODO(edisonn): round/ceil to int?
edisonn@google.comaf54a512013-09-13 19:33:42 +00002180 EXPECT_OPERANDS("j", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002181 POP_NUMBER(pdfContext, lj);
2182 CHECK_PARAMETERS();
2183
2184 return skpdfGraphicsStateApplyLJ(pdfContext, (int)lj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002185}
2186
2187//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002188static SkPdfResult PdfOp_M(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002189 EXPECT_OPERANDS("M", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002190 POP_NUMBER(pdfContext, ml);
2191 CHECK_PARAMETERS();
edisonn@google.com24cdf132013-07-30 16:06:12 +00002192 return skpdfGraphicsStateApplyML(pdfContext, ml);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002193}
2194
2195//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
2196//page 155).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002197static SkPdfResult PdfOp_d(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002198 EXPECT_OPERANDS("d", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002199 POP_OBJ(pdfContext, phase);
2200 POP_ARRAY(pdfContext, array);
2201 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002202
edisonn@google.combd2f3012013-08-22 14:18:04 +00002203 return skpdfGraphicsStateApplyD(pdfContext, array, phase);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002204}
2205
2206//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 +00002207static SkPdfResult PdfOp_ri(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002208 pdfContext->fObjectStack.pop();
2209
edisonn@google.comaf54a512013-09-13 19:33:42 +00002210 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "render intent NYI", NULL, pdfContext);
2211
edisonn@google.com3aa35552013-08-14 18:26:20 +00002212 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002213}
2214
2215//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
2216//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
2217//fies the output device’s default flatness tolerance.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002218static SkPdfResult PdfOp_i(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002219 EXPECT_OPERANDS("i", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002220 POP_NUMBER(pdfContext, flatness);
2221 CHECK_PARAMETERS();
2222
2223 if (flatness < 0 || flatness > 100) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002224 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, "flatness must be a real in [0, 100] range", flatness_obj, pdfContext);
2225 return kIgnoreError_SkPdfResult;
edisonn@google.combd2f3012013-08-22 14:18:04 +00002226 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002227
edisonn@google.com3aa35552013-08-14 18:26:20 +00002228 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002229}
2230
edisonn@google.come878e722013-07-29 19:10:58 +00002231SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
2232
2233class InitBlendModes {
2234public:
2235 InitBlendModes() {
2236 // TODO(edisonn): use the python code generator?
2237 // TABLE 7.2 Standard separable blend modes
2238 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
2239 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
2240 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
2241 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
2242 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
2243 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
2244 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
2245 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
2246 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
2247 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
2248 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
2249 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
2250
2251 // TABLE 7.3 Standard nonseparable blend modes
2252 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
2253 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
2254 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
2255 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
2256 }
2257};
2258
2259InitBlendModes _gDummyInniter;
2260
edisonn@google.com33f11b62013-08-14 21:35:27 +00002261static SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
edisonn@google.come878e722013-07-29 19:10:58 +00002262 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2263 if (gPdfBlendModes.find(blendMode, len, &mode)) {
2264 return mode;
2265 }
2266
2267 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2268}
2269
edisonn@google.com063d7072013-08-16 15:05:08 +00002270static void skpdfGraphicsStateApplyBM_name(SkPdfContext* pdfContext, const SkString& blendMode) {
2271 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.size());
edisonn@google.come878e722013-07-29 19:10:58 +00002272 if (mode <= SkXfermode::kLastMode) {
2273 pdfContext->fGraphicsState.fBlendModesLength = 1;
2274 pdfContext->fGraphicsState.fBlendModes[0] = mode;
2275 } else {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002276 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue, blendMode.c_str(), NULL, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002277 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002278}
2279
edisonn@google.com33f11b62013-08-14 21:35:27 +00002280static void skpdfGraphicsStateApplyBM_array(SkPdfContext* pdfContext, SkPdfArray* blendModes) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002281 if (!blendModes || !blendModes->isArray()) {
2282 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, blendModes, SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002283 return;
2284 }
edisonn@google.comaf54a512013-09-13 19:33:42 +00002285
2286 if (blendModes->size() == 0 || blendModes->size() > 256) {
2287 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, "length of blendmodes, 0, is an erro, 256+, is NYI", blendModes, pdfContext);
2288 return;
2289 }
2290
edisonn@google.come878e722013-07-29 19:10:58 +00002291 SkXfermode::Mode modes[256];
2292 int cnt = blendModes->size();
2293 for (int i = 0; i < cnt; i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00002294 SkPdfNativeObject* name = blendModes->objAtAIndex(i);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002295 if (!name || !name->isName()) {
2296 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name, SkPdfNativeObject::kName_PdfObjectType, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002297 return;
2298 }
2299 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
2300 if (mode > SkXfermode::kLastMode) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002301 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue, NULL, name, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002302 return;
2303 }
2304 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002305
edisonn@google.come878e722013-07-29 19:10:58 +00002306 pdfContext->fGraphicsState.fBlendModesLength = cnt;
2307 for (int i = 0; i < cnt; i++) {
2308 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
2309 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002310}
2311
edisonn@google.com33f11b62013-08-14 21:35:27 +00002312static void skpdfGraphicsStateApplySMask_dict(SkPdfContext* pdfContext, SkPdfDictionary* sMask) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002313 if (!sMask || !sMask->isName()) {
2314 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, sMask, SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
2315 return;
2316 }
2317
edisonn@google.come878e722013-07-29 19:10:58 +00002318 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002319 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
edisonn@google.come878e722013-07-29 19:10:58 +00002320 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
2321 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
2322 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
2323 } else {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002324 // TODO(edisonn): make the dictionary types an enum?
2325 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Dictionary must be SoftMask, or SoftMaskImage", sMask, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002326 }
2327}
2328
edisonn@google.com063d7072013-08-16 15:05:08 +00002329static void skpdfGraphicsStateApplySMask_name(SkPdfContext* pdfContext, const SkString& sMask) {
2330 if (sMask.equals("None")) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002331 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002332 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002333 return;
2334 }
2335
edisonn@google.come878e722013-07-29 19:10:58 +00002336 //Next, get the ExtGState Dictionary from the Resource Dictionary:
2337 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2338
2339 if (extGStateDictionary == NULL) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002340 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL, pdfContext->fGraphicsState.fResources, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002341 return;
2342 }
2343
edisonn@google.com3aa35552013-08-14 18:26:20 +00002344 SkPdfNativeObject* obj = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
edisonn@google.come878e722013-07-29 19:10:58 +00002345 if (!obj || !obj->isDictionary()) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002346 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, obj, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002347 return;
2348 }
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002349
2350 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002351 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002352
edisonn@google.come878e722013-07-29 19:10:58 +00002353 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002354}
2355
edisonn@google.com33f11b62013-08-14 21:35:27 +00002356static void skpdfGraphicsStateApplyAIS(SkPdfContext* pdfContext, bool alphaSource) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002357 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2358}
2359
2360
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002361//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2362//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 +00002363static SkPdfResult PdfOp_gs(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002364 EXPECT_OPERANDS("gs", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002365 POP_NAME(pdfContext, name);
2366 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002367
2368#ifdef PDF_TRACE
edisonn@google.com063d7072013-08-16 15:05:08 +00002369 SkString str;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002370#endif
2371
2372 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00002373 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002374
2375 if (extGStateDictionary == NULL) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002376 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL, pdfContext->fGraphicsState.fResources, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002377 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002378 }
2379
edisonn@google.com3aa35552013-08-14 18:26:20 +00002380 SkPdfNativeObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002381
edisonn@google.com3aa35552013-08-14 18:26:20 +00002382 if (kNone_SkPdfNativeObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2383 return kIgnoreError_SkPdfResult;
edisonn@google.com571c70b2013-07-10 17:09:50 +00002384 }
2385 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002386
2387 // TODO(edisonn): now load all those properties in graphic state.
2388 if (gs == NULL) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002389 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, gs, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002390 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002391 }
2392
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002393 if (gs->has_LW()) {
2394 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2395 }
2396
2397 if (gs->has_LC()) {
2398 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2399 }
2400
2401 if (gs->has_LJ()) {
2402 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2403 }
2404
2405 if (gs->has_ML()) {
2406 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2407 }
2408
2409 if (gs->has_D()) {
2410 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2411 }
2412
2413 if (gs->has_Font()) {
2414 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2415 }
2416
2417 if (gs->has_BM()) {
2418 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2419 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2420 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2421 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2422 } else {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002423 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", gs->get("BM"), SkPdfNativeObject::kArray_PdfObjectType || SkPdfNativeObject::kName_PdfObjectType, pdfContext);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002424 }
2425 }
2426
2427 if (gs->has_SMask()) {
2428 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2429 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2430 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
2431 skpdfGraphicsStateApplySMask_dict(pdfContext, gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
2432 } else {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002433 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", gs->get("BM"), SkPdfNativeObject::kDictionary_PdfObjectType || SkPdfNativeObject::kName_PdfObjectType, pdfContext);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002434 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002435 }
2436
2437 if (gs->has_ca()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002438 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002439 }
2440
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002441 if (gs->has_CA()) {
2442 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2443 }
2444
2445 if (gs->has_AIS()) {
2446 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002447 }
2448
edisonn@google.com3aa35552013-08-14 18:26:20 +00002449 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002450}
2451
2452//charSpace Tc Set the character spacing, Tc
2453//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2454//Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002455SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002456 EXPECT_OPERANDS("Tc", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002457 POP_NUMBER(pdfContext, charSpace);
2458 CHECK_PARAMETERS();
2459
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002460 pdfContext->fGraphicsState.fCharSpace = charSpace;
2461
edisonn@google.com3aa35552013-08-14 18:26:20 +00002462 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002463}
2464
2465//wordSpace Tw Set the word spacing, T
2466//w
2467//, to wordSpace, which is a number expressed in unscaled
2468//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2469//value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002470SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002471 EXPECT_OPERANDS("Tw", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002472 POP_NUMBER(pdfContext, wordSpace);
2473 CHECK_PARAMETERS();
2474
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002475 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2476
edisonn@google.com3aa35552013-08-14 18:26:20 +00002477 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002478}
2479
2480//scale Tz Set the horizontal scaling, Th
2481//, to (scale ˜ 100). scale is a number specifying the
2482//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002483static SkPdfResult PdfOp_Tz(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002484 EXPECT_OPERANDS("Tz", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002485 POP_NUMBER(pdfContext, scale);
2486 CHECK_PARAMETERS();
2487
2488 if (scale < 0) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002489 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, "scale must a positive real number", scale_obj, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002490 return kError_SkPdfResult;
2491 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002492
edisonn@google.com3aa35552013-08-14 18:26:20 +00002493 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002494}
2495
2496//render Tr Set the text rendering mode, T
2497//mode, to render, which is an integer. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002498static SkPdfResult PdfOp_Tr(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002499 EXPECT_OPERANDS("Tr", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002500 POP_INTEGER(pdfContext, mode);
2501 CHECK_PARAMETERS();
2502
2503 if (mode < 0) { // TODO(edisonn): function/enums with supported modes
edisonn@google.comaf54a512013-09-13 19:33:42 +00002504 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, "mode must a positive integer or 0", mode_obj, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002505 return kError_SkPdfResult;
2506 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002507
edisonn@google.com3aa35552013-08-14 18:26:20 +00002508 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002509}
2510//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2511//units. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002512static SkPdfResult PdfOp_Ts(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002513 EXPECT_OPERANDS("Ts", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002514 POP_NUMBER(pdfContext, rise);
2515 CHECK_PARAMETERS();
2516
2517 if (rise < 0) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002518 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, "rise must a positive real number", rise_obj, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002519 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//wx wy d0
edisonn@google.com3aa35552013-08-14 18:26:20 +00002526static SkPdfResult PdfOp_d0(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002527 EXPECT_OPERANDS("d0", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002528 POP_NUMBER(pdfContext, wy);
2529 POP_NUMBER(pdfContext, wx);
2530 CHECK_PARAMETERS();
2531
edisonn@google.comaf54a512013-09-13 19:33:42 +00002532 if (wx < 0) {
2533 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, "wx must a positive real number", wx_obj, pdfContext);
2534 return kError_SkPdfResult;
2535 }
2536
2537 if (wy < 0) {
2538 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, "wy must a positive real number", wy_obj, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002539 return kError_SkPdfResult;
2540 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002541
edisonn@google.com3aa35552013-08-14 18:26:20 +00002542 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002543}
2544
2545//wx wy llx lly urx ury d1
edisonn@google.com3aa35552013-08-14 18:26:20 +00002546static SkPdfResult PdfOp_d1(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002547 EXPECT_OPERANDS("d1", pdfContext, 6);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002548 POP_NUMBER(pdfContext, ury);
2549 POP_NUMBER(pdfContext, urx);
2550 POP_NUMBER(pdfContext, lly);
2551 POP_NUMBER(pdfContext, llx);
2552 POP_NUMBER(pdfContext, wy);
2553 POP_NUMBER(pdfContext, wx);
2554 CHECK_PARAMETERS();
2555
edisonn@google.comaf54a512013-09-13 19:33:42 +00002556 // TODO(edisonn): silly way to remove warning
edisonn@google.combd2f3012013-08-22 14:18:04 +00002557 if (wx + wy + llx + lly + urx + ury) {
2558 return kNYI_SkPdfResult;
2559 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002560
edisonn@google.com3aa35552013-08-14 18:26:20 +00002561 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002562}
2563
2564//name sh
edisonn@google.com3aa35552013-08-14 18:26:20 +00002565static SkPdfResult PdfOp_sh(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002566 EXPECT_OPERANDS("sh", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002567 POP_NAME(pdfContext, name);
2568 CHECK_PARAMETERS();
2569
2570 if (name == NULL) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002571 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name, SkPdfNativeObject::kName_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002572 return kError_SkPdfResult;
2573 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002574
edisonn@google.com3aa35552013-08-14 18:26:20 +00002575 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002576}
2577
2578//name Do
edisonn@google.com3aa35552013-08-14 18:26:20 +00002579static SkPdfResult PdfOp_Do(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002580 EXPECT_OPERANDS("Do", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002581 POP_NAME(pdfContext, name);
2582 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002583
edisonn@google.com571c70b2013-07-10 17:09:50 +00002584 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002585
2586 if (xObject == NULL) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002587 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingXObject_SkPdfIssue, NULL, pdfContext->fGraphicsState.fResources, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002588 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002589 }
2590
edisonn@google.com3aa35552013-08-14 18:26:20 +00002591 SkPdfNativeObject* value = xObject->get(name);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002592 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002593
edisonn@google.com571c70b2013-07-10 17:09:50 +00002594 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002595}
2596
2597//tag MP Designate a marked-content point. tag is a name object indicating the role or
2598//significance of the point.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002599static SkPdfResult PdfOp_MP(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002600 EXPECT_OPERANDS("MP", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002601 POP_OBJ(pdfContext, tag);
2602 CHECK_PARAMETERS();
2603
2604 if (tag == NULL) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002605 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag, SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002606 return kNYI_SkPdfResult;
2607 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002608
edisonn@google.comaf54a512013-09-13 19:33:42 +00002609 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MP NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002610 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002611}
2612
2613//tag properties DP Designate a marked-content point with an associated property list. tag is a
2614//name object indicating the role or significance of the point; properties is
2615//either an inline dictionary containing the property list or a name object
2616//associated with it in the Properties subdictionary of the current resource
2617//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002618static SkPdfResult PdfOp_DP(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002619 EXPECT_OPERANDS("DP", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002620 POP_OBJ(pdfContext, properties);
2621 POP_OBJ(pdfContext, tag);
2622 CHECK_PARAMETERS();
2623
edisonn@google.comaf54a512013-09-13 19:33:42 +00002624 if (tag == NULL) {
2625 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag, SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002626 return kNYI_SkPdfResult;
2627 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002628
edisonn@google.comaf54a512013-09-13 19:33:42 +00002629 if (properties == NULL) {
2630 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties, SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2631 return kNYI_SkPdfResult;
2632 }
2633
2634 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "DP NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002635 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002636}
2637
2638//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2639//tag is a name object indicating the role or significance of the sequence.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002640static SkPdfResult PdfOp_BMC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002641 EXPECT_OPERANDS("BMC", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002642 POP_OBJ(pdfContext, tag);
2643 CHECK_PARAMETERS();
2644
2645 if (tag == NULL) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002646 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag, SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002647 return kNYI_SkPdfResult;
2648 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002649
edisonn@google.comaf54a512013-09-13 19:33:42 +00002650 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BMC NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002651 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002652}
2653
2654//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2655//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2656//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 +00002657static SkPdfResult PdfOp_BDC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002658 EXPECT_OPERANDS("BDC", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002659 POP_OBJ(pdfContext, properties);
2660 POP_OBJ(pdfContext, tag);
2661 CHECK_PARAMETERS();
2662
edisonn@google.comaf54a512013-09-13 19:33:42 +00002663 if (tag == NULL) {
2664 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag, SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002665 return kNYI_SkPdfResult;
2666 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002667
edisonn@google.comaf54a512013-09-13 19:33:42 +00002668 if (properties == NULL) {
2669 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties, SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2670 return kNYI_SkPdfResult;
2671 }
2672
2673 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BDC NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002674 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002675}
2676
2677//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002678static SkPdfResult PdfOp_EMC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002679 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "EMC NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002680 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002681}
2682
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002683static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002684 static bool gInitialized = false;
2685 if (gInitialized) {
2686 return;
2687 }
2688
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002689 gPdfOps.set("q", PdfOp_q);
2690 gPdfOps.set("Q", PdfOp_Q);
2691 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002692
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002693 gPdfOps.set("TD", PdfOp_TD);
2694 gPdfOps.set("Td", PdfOp_Td);
2695 gPdfOps.set("Tm", PdfOp_Tm);
2696 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002697
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002698 gPdfOps.set("m", PdfOp_m);
2699 gPdfOps.set("l", PdfOp_l);
2700 gPdfOps.set("c", PdfOp_c);
2701 gPdfOps.set("v", PdfOp_v);
2702 gPdfOps.set("y", PdfOp_y);
2703 gPdfOps.set("h", PdfOp_h);
2704 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002705
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002706 gPdfOps.set("S", PdfOp_S);
2707 gPdfOps.set("s", PdfOp_s);
2708 gPdfOps.set("f", PdfOp_f);
2709 gPdfOps.set("F", PdfOp_F);
2710 gPdfOps.set("f*", PdfOp_f_star);
2711 gPdfOps.set("B", PdfOp_B);
2712 gPdfOps.set("B*", PdfOp_B_star);
2713 gPdfOps.set("b", PdfOp_b);
2714 gPdfOps.set("b*", PdfOp_b_star);
2715 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002716
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002717 gPdfOps.set("BT", PdfOp_BT);
2718 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002719
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002720 gPdfOps.set("Tj", PdfOp_Tj);
2721 gPdfOps.set("'", PdfOp_quote);
2722 gPdfOps.set("\"", PdfOp_doublequote);
2723 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002724
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002725 gPdfOps.set("CS", PdfOp_CS);
2726 gPdfOps.set("cs", PdfOp_cs);
2727 gPdfOps.set("SC", PdfOp_SC);
2728 gPdfOps.set("SCN", PdfOp_SCN);
2729 gPdfOps.set("sc", PdfOp_sc);
2730 gPdfOps.set("scn", PdfOp_scn);
2731 gPdfOps.set("G", PdfOp_G);
2732 gPdfOps.set("g", PdfOp_g);
2733 gPdfOps.set("RG", PdfOp_RG);
2734 gPdfOps.set("rg", PdfOp_rg);
2735 gPdfOps.set("K", PdfOp_K);
2736 gPdfOps.set("k", PdfOp_k);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002737
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002738 gPdfOps.set("W", PdfOp_W);
2739 gPdfOps.set("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002740
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002741 gPdfOps.set("BX", PdfOp_BX);
2742 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002743
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002744 gPdfOps.set("BI", PdfOp_BI);
2745 gPdfOps.set("ID", PdfOp_ID);
2746 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002747
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002748 gPdfOps.set("w", PdfOp_w);
2749 gPdfOps.set("J", PdfOp_J);
2750 gPdfOps.set("j", PdfOp_j);
2751 gPdfOps.set("M", PdfOp_M);
2752 gPdfOps.set("d", PdfOp_d);
2753 gPdfOps.set("ri", PdfOp_ri);
2754 gPdfOps.set("i", PdfOp_i);
2755 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002756
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002757 gPdfOps.set("Tc", PdfOp_Tc);
2758 gPdfOps.set("Tw", PdfOp_Tw);
2759 gPdfOps.set("Tz", PdfOp_Tz);
2760 gPdfOps.set("TL", PdfOp_TL);
2761 gPdfOps.set("Tf", PdfOp_Tf);
2762 gPdfOps.set("Tr", PdfOp_Tr);
2763 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002764
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002765 gPdfOps.set("d0", PdfOp_d0);
2766 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002767
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002768 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002769
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002770 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002771
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002772 gPdfOps.set("MP", PdfOp_MP);
2773 gPdfOps.set("DP", PdfOp_DP);
2774 gPdfOps.set("BMC", PdfOp_BMC);
2775 gPdfOps.set("BDC", PdfOp_BDC);
2776 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002777
2778 gInitialized = true;
2779}
2780
2781class InitPdfOps {
2782public:
2783 InitPdfOps() {
2784 initPdfOperatorRenderes();
2785 }
2786};
2787
2788InitPdfOps gInitPdfOps;
2789
2790void reportPdfRenderStats() {
edisonn@google.com3aa35552013-08-14 18:26:20 +00002791 for (int i = 0 ; i < kCount_SkPdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002792 SkTDict<int>::Iter iter(gRenderStats[i]);
2793 const char* key;
2794 int value = 0;
2795 while ((key = iter.next(&value)) != NULL) {
2796 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002797 }
2798 }
2799}
2800
edisonn@google.com3aa35552013-08-14 18:26:20 +00002801SkPdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002802 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002803 {
2804 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002805 PdfOperatorRenderer pdfOperatorRenderer = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002806 if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) && pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002807 // caller, main work is done by pdfOperatorRenderer(...)
2808 PdfTokenLooper* childLooper = NULL;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002809 SkPdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002810
2811 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002812 gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
2813 gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002814
2815 if (childLooper) {
2816 childLooper->setUp(this);
2817 childLooper->loop();
2818 delete childLooper;
2819 }
2820 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002821 int cnt = 0;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002822 gRenderStats[kUnsupported_SkPdfResult].find(token.fKeyword, token.fKeywordLength, &cnt);
2823 gRenderStats[kUnsupported_SkPdfResult].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002824 }
2825 }
2826 else if (token.fType == kObject_TokenType)
2827 {
2828 fPdfContext->fObjectStack.push( token.fObject );
2829 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002830 else {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002831 // TODO(edisonn): store the keyword as a object, so we can track the location in file, and report it
2832 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, token.fKeyword, NULL, fPdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002833 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002834 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00002835 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002836}
2837
2838void PdfMainLooper::loop() {
2839 PdfToken token;
2840 while (readToken(fTokenizer, &token)) {
2841 consumeToken(token);
2842 }
2843}
2844
edisonn@google.com3aa35552013-08-14 18:26:20 +00002845SkPdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002846 SkASSERT(false);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002847 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002848}
2849
2850void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002851 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002852}
2853
edisonn@google.com3aa35552013-08-14 18:26:20 +00002854SkPdfResult PdfInlineImageLooper::done() {
2855 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002856}
2857
edisonn@google.com3aa35552013-08-14 18:26:20 +00002858SkPdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002859 return fParent->consumeToken(token);
2860}
2861
2862void PdfCompatibilitySectionLooper::loop() {
2863 // TODO(edisonn): save stacks position, or create a new stack?
2864 // TODO(edisonn): what happens if we pop out more variables then when we started?
2865 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2866 // pop-ing too much will not affect outside the section.
2867 PdfToken token;
2868 while (readToken(fTokenizer, &token)) {
2869 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2870 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2871 looper->setUp(this);
2872 looper->loop();
2873 delete looper;
2874 } else {
2875 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
2876 fParent->consumeToken(token);
2877 }
2878 }
2879 // TODO(edisonn): restore stack.
2880}
2881
2882// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2883// TODO(edisonn): Add API for Forms viewing and editing
2884// e.g. SkBitmap getPage(int page);
2885// int formsCount();
2886// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2887// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2888// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2889// resources needed, so the preview is fast.
2890// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2891// references automatically.
2892
edisonn@google.com3aa35552013-08-14 18:26:20 +00002893SkPdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00002894
edisonn@google.com444e25a2013-07-11 15:20:50 +00002895bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00002896 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002897 return false;
2898 }
2899
edisonn@google.com222382b2013-07-10 22:33:10 +00002900 if (page < 0 || page >= pages()) {
2901 return false;
2902 }
2903
edisonn@google.com3aa35552013-08-14 18:26:20 +00002904 SkPdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002905
edisonn@google.com222382b2013-07-10 22:33:10 +00002906 pdfContext.fOriginalMatrix = SkMatrix::I();
2907 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2908
2909 gPdfContext = &pdfContext;
2910
2911 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00002912 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00002913 SkScalar w = dst.width();
2914 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00002915
edisonn@google.comf68aed32013-08-22 15:37:21 +00002916 if (SkScalarTruncToInt(w) <= 0 || SkScalarTruncToInt(h) <= 0) {
2917 return true;
2918 }
2919
edisonn@google.com444e25a2013-07-11 15:20:50 +00002920 SkScalar wp = fPdfDoc->MediaBox(page).width();
2921 SkScalar hp = fPdfDoc->MediaBox(page).height();
2922
2923 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 +00002924// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2925
2926 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2927 // TODO(edisonn): add flagg for no clipping.
2928 // Use larger image to make sure we do not draw anything outside of page
2929 // could be used in tests.
2930
2931#ifdef PDF_DEBUG_3X
2932 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)};
2933#else
2934 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2935#endif
2936 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2937 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2938
2939 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2940 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2941
2942 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2943 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2944
2945 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2946 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2947
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002948 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
edisonn@google.com0f901902013-08-07 11:56:16 +00002949 pdfContext.fGraphicsState.fContentStreamMatrix = pdfContext.fOriginalMatrix;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002950 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
2951 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
edisonn@google.com222382b2013-07-10 22:33:10 +00002952
edisonn@google.com222382b2013-07-10 22:33:10 +00002953#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00002954 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00002955#endif
2956
edisonn@google.com444e25a2013-07-11 15:20:50 +00002957 canvas->setMatrix(pdfContext.fOriginalMatrix);
2958
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002959 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2960
2961 // TODO(edisonn:) erase with white before draw?
edisonn@google.com222382b2013-07-10 22:33:10 +00002962// SkPaint paint;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002963// paint.setColor(SK_ColorWHITE);
edisonn@google.com222382b2013-07-10 22:33:10 +00002964// canvas->drawRect(rect, paint);
2965
edisonn@google.com222382b2013-07-10 22:33:10 +00002966
2967 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002968 return true;
2969}
edisonn@google.com222382b2013-07-10 22:33:10 +00002970
2971bool SkPdfRenderer::load(const SkString inputFileName) {
2972 unload();
2973
2974 // TODO(edisonn): create static function that could return NULL if there are errors
edisonn@google.com3aa35552013-08-14 18:26:20 +00002975 fPdfDoc = new SkPdfNativeDoc(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00002976 if (fPdfDoc->pages() == 0) {
2977 delete fPdfDoc;
2978 fPdfDoc = NULL;
2979 }
edisonn@google.com222382b2013-07-10 22:33:10 +00002980
2981 return fPdfDoc != NULL;
2982}
2983
edisonn@google.com147adb12013-07-24 15:56:19 +00002984bool SkPdfRenderer::load(SkStream* stream) {
2985 unload();
2986
2987 // TODO(edisonn): create static function that could return NULL if there are errors
edisonn@google.com3aa35552013-08-14 18:26:20 +00002988 fPdfDoc = new SkPdfNativeDoc(stream);
edisonn@google.com147adb12013-07-24 15:56:19 +00002989 if (fPdfDoc->pages() == 0) {
2990 delete fPdfDoc;
2991 fPdfDoc = NULL;
2992 }
2993
2994 return fPdfDoc != NULL;
2995}
2996
2997
edisonn@google.com222382b2013-07-10 22:33:10 +00002998int SkPdfRenderer::pages() const {
2999 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
3000}
3001
3002void SkPdfRenderer::unload() {
3003 delete fPdfDoc;
3004 fPdfDoc = NULL;
3005}
3006
3007SkRect SkPdfRenderer::MediaBox(int page) const {
3008 SkASSERT(fPdfDoc);
3009 return fPdfDoc->MediaBox(page);
3010}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00003011
edisonn@google.com7b328fd2013-07-11 12:53:06 +00003012size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00003013 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
3014}
edisonn@google.com147adb12013-07-24 15:56:19 +00003015
3016bool SkPDFNativeRenderToBitmap(SkStream* stream,
3017 SkBitmap* output,
3018 int page,
3019 SkPdfContent content,
3020 double dpi) {
3021 SkASSERT(page >= 0);
3022 SkPdfRenderer renderer;
3023 renderer.load(stream);
3024 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
3025 return false;
3026 }
3027
3028 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
3029
3030 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
3031 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
3032
3033 rect = SkRect::MakeWH(width, height);
3034
3035 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
3036
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003037 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (*output)));
edisonn@google.com147adb12013-07-24 15:56:19 +00003038 SkCanvas canvas(device);
3039
3040 return renderer.renderPage(page, &canvas, rect);
3041}