blob: 2a787650418e3697dea07529fd7b9e1497826cce [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"
edisonn@google.come50d9a12013-10-10 20:58:22 +000019#include "SkPdfFont.h"
20#include "SkPdfGraphicsState.h"
21#include "SkPdfHeaders_autogen.h"
22#include "SkPdfMapper_autogen.h"
23#include "SkPdfNativeTokenizer.h"
24#include "SkPdfRenderer.h"
25#include "SkPdfReporter.h"
26#include "SkPdfUtils.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000027#include "SkStream.h"
28#include "SkTypeface.h"
29#include "SkTArray.h"
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000030#include "SkTDict.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000031
edisonn@google.come50d9a12013-10-10 20:58:22 +000032// TODO(edisonn): #ifdef these ones, as they are used only for debugging.
edisonn@google.com3aa35552013-08-14 18:26:20 +000033extern "C" SkPdfContext* gPdfContext;
edisonn@google.com15b11182013-07-11 14:43:15 +000034extern "C" SkBitmap* gDumpBitmap;
35extern "C" SkCanvas* gDumpCanvas;
36
edisonn@google.com131d4ee2013-06-26 17:48:12 +000037__SK_FORCE_IMAGE_DECODER_LINKING;
38
edisonn@google.come50d9a12013-10-10 20:58:22 +000039// TODO(edisonn): tool, show what objects were read during rendering - will help to identify
40// features with incomplete implementation
edisonn@google.com131d4ee2013-06-26 17:48:12 +000041// TODO(edisonn): security - validate all the user input, all pdf!
edisonn@google.come50d9a12013-10-10 20:58:22 +000042// TODO(edisonn): testability -add option to render without text, or only render text
edisonn@google.com131d4ee2013-06-26 17:48:12 +000043
edisonn@google.come50d9a12013-10-10 20:58:22 +000044// Helper macros to load variables from stack, and automatically check their type.
edisonn@google.comaf54a512013-09-13 19:33:42 +000045#define EXPECT_OPERANDS(name,pdfContext,n) \
edisonn@google.combd2f3012013-08-22 14:18:04 +000046 bool __failed = pdfContext->fObjectStack.count() < n; \
edisonn@google.comaf54a512013-09-13 19:33:42 +000047 SkPdfREPORTCODE(const char* __operator_name = name); \
48 SkPdfREPORTCODE((void)__operator_name); \
edisonn@google.come50d9a12013-10-10 20:58:22 +000049 SkPdfReportIf(pdfContext->fObjectStack.count() < n, \
50 kIgnoreError_SkPdfIssueSeverity, \
51 kStackOverflow_SkPdfIssue, \
52 "Not enought parameters.", NULL, pdfContext); \
edisonn@google.combd2f3012013-08-22 14:18:04 +000053 SkDEBUGCODE(int __cnt = n);
54
55#define POP_OBJ(pdfContext,name) \
56 SkDEBUGCODE(__cnt--); \
57 SkASSERT(__cnt >= 0); \
58 SkPdfNativeObject* name = NULL; \
59 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
60 if (pdfContext->fObjectStack.count() > 0) { \
61 name = pdfContext->fObjectStack.top(); \
62 pdfContext->fObjectStack.pop(); \
63 }
64
edisonn@google.comaf54a512013-09-13 19:33:42 +000065// TODO(edisonn): make all pop function to use name##_obj
edisonn@google.combd2f3012013-08-22 14:18:04 +000066#define POP_NUMBER(pdfContext,name) \
67 SkDEBUGCODE(__cnt--); \
68 SkASSERT(__cnt >= 0); \
69 double name = 0; \
edisonn@google.comaf54a512013-09-13 19:33:42 +000070 SkPdfNativeObject* name##_obj = NULL; \
edisonn@google.combd2f3012013-08-22 14:18:04 +000071 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
72 if (pdfContext->fObjectStack.count() > 0) { \
edisonn@google.comaf54a512013-09-13 19:33:42 +000073 name##_obj = pdfContext->fObjectStack.top(); \
edisonn@google.combd2f3012013-08-22 14:18:04 +000074 pdfContext->fObjectStack.pop(); \
edisonn@google.comaf54a512013-09-13 19:33:42 +000075 if (!name##_obj || !name##_obj->isNumber()) { \
edisonn@google.come50d9a12013-10-10 20:58:22 +000076 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
77 __operator_name, \
78 name##_obj, \
79 SkPdfNativeObject::_kNumber_PdfObjectType, \
80 NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +000081 __failed = true;\
82 } else { \
edisonn@google.comaf54a512013-09-13 19:33:42 +000083 name = name##_obj->numberValue(); \
edisonn@google.combd2f3012013-08-22 14:18:04 +000084 } \
85 }
86
87#define POP_INTEGER(pdfContext,name) \
88 SkDEBUGCODE(__cnt--); \
89 SkASSERT(__cnt >= 0); \
90 int64_t name = 0; \
91 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
edisonn@google.comaf54a512013-09-13 19:33:42 +000092 SkPdfNativeObject* name##_obj = NULL; \
edisonn@google.combd2f3012013-08-22 14:18:04 +000093 if (pdfContext->fObjectStack.count() > 0) { \
edisonn@google.comaf54a512013-09-13 19:33:42 +000094 name##_obj = pdfContext->fObjectStack.top(); \
edisonn@google.combd2f3012013-08-22 14:18:04 +000095 pdfContext->fObjectStack.pop(); \
edisonn@google.comaf54a512013-09-13 19:33:42 +000096 if (!name##_obj || !name##_obj->isInteger()) { \
edisonn@google.come50d9a12013-10-10 20:58:22 +000097 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
98 __operator_name, \
99 name##_obj, \
100 SkPdfNativeObject::kInteger_PdfObjectType, \
101 NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000102 __failed = true;\
103 } else { \
edisonn@google.comaf54a512013-09-13 19:33:42 +0000104 name = name##_obj->intValue(); \
edisonn@google.combd2f3012013-08-22 14:18:04 +0000105 } \
106 }
107
108#define POP_NUMBER_INTO(pdfContext,var) \
109 SkDEBUGCODE(__cnt--); \
110 SkASSERT(__cnt >= 0); \
111 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
112 if (pdfContext->fObjectStack.count() > 0) { \
113 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
114 pdfContext->fObjectStack.pop(); \
115 if (!tmp || !tmp->isNumber()) { \
edisonn@google.come50d9a12013-10-10 20:58:22 +0000116 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
117 __operator_name, \
118 tmp, \
119 SkPdfNativeObject::kInteger_PdfObjectType | \
120 SkPdfNativeObject::kReal_PdfObjectType, \
121 NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000122 __failed = true;\
123 } else { \
124 var = tmp->numberValue(); \
125 } \
126 }
127
128
129#define POP_NAME(pdfContext,name) \
130 SkDEBUGCODE(__cnt--); \
131 SkASSERT(__cnt >= 0); \
132 SkPdfNativeObject* name = NULL; \
133 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
134 if (pdfContext->fObjectStack.count() > 0) { \
135 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
136 pdfContext->fObjectStack.pop(); \
137 if (!tmp || !tmp->isName()) { \
edisonn@google.come50d9a12013-10-10 20:58:22 +0000138 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
139 __operator_name, \
140 tmp, \
141 SkPdfNativeObject::kName_PdfObjectType, \
142 NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000143 __failed = true;\
144 } else { \
145 name = tmp; \
146 } \
147 }
148
149#define POP_STRING(pdfContext,name) \
150 SkDEBUGCODE(__cnt--); \
151 SkASSERT(__cnt >= 0); \
152 SkPdfNativeObject* name = NULL; \
153 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
154 if (pdfContext->fObjectStack.count() > 0) { \
155 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
156 pdfContext->fObjectStack.pop(); \
157 if (!tmp || !tmp->isAnyString()) { \
edisonn@google.come50d9a12013-10-10 20:58:22 +0000158 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
159 __operator_name, \
160 tmp, \
161 SkPdfNativeObject::kString_PdfObjectType | \
162 SkPdfNativeObject::kHexString_PdfObjectType, \
163 NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000164 __failed = true;\
165 } else { \
166 name = tmp; \
167 } \
168 }
169
170#define POP_ARRAY(pdfContext,name) \
171 SkDEBUGCODE(__cnt--); \
172 SkASSERT(__cnt >= 0); \
173 SkPdfArray* name = NULL; \
174 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
175 if (pdfContext->fObjectStack.count() > 0) { \
176 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
177 pdfContext->fObjectStack.pop(); \
178 if (!tmp || !tmp->isArray()) { \
edisonn@google.come50d9a12013-10-10 20:58:22 +0000179 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
180 __operator_name, \
181 tmp, \
182 SkPdfNativeObject::kArray_PdfObjectType, \
183 NULL);\
edisonn@google.combd2f3012013-08-22 14:18:04 +0000184 __failed = true;\
185 } else { \
186 name = (SkPdfArray*)tmp; \
187 } \
188 }
189
edisonn@google.combd2f3012013-08-22 14:18:04 +0000190#define CHECK_PARAMETERS() \
191 SkASSERT(__cnt == 0); \
192 if (__failed) return kIgnoreError_SkPdfResult;
193
edisonn@google.come50d9a12013-10-10 20:58:22 +0000194
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000195NotOwnedString strings_DeviceRGB;
196NotOwnedString strings_DeviceCMYK;
edisonn@google.com222382b2013-07-10 22:33:10 +0000197
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000198class StringsInit {
199public:
200 StringsInit() {
201 NotOwnedString::init(&strings_DeviceRGB, "DeviceRGB");
202 NotOwnedString::init(&strings_DeviceCMYK, "DeviceCMYK");
203 }
204};
205
edisonn@google.come50d9a12013-10-10 20:58:22 +0000206// TODO(edisonn): this will not work in chrome! Find another solution!
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000207StringsInit gStringsInit;
edisonn@google.com222382b2013-07-10 22:33:10 +0000208
209// TODO(edisonn): Document PdfTokenLooper and subclasses.
210class PdfTokenLooper {
211protected:
212 PdfTokenLooper* fParent;
213 SkPdfNativeTokenizer* fTokenizer;
edisonn@google.com3aa35552013-08-14 18:26:20 +0000214 SkPdfContext* fPdfContext;
edisonn@google.com222382b2013-07-10 22:33:10 +0000215 SkCanvas* fCanvas;
216
217public:
218 PdfTokenLooper(PdfTokenLooper* parent,
219 SkPdfNativeTokenizer* tokenizer,
edisonn@google.com3aa35552013-08-14 18:26:20 +0000220 SkPdfContext* pdfContext,
edisonn@google.com222382b2013-07-10 22:33:10 +0000221 SkCanvas* canvas)
222 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
223
224 virtual ~PdfTokenLooper() {}
225
edisonn@google.com3aa35552013-08-14 18:26:20 +0000226 virtual SkPdfResult consumeToken(PdfToken& token) = 0;
edisonn@google.com222382b2013-07-10 22:33:10 +0000227 virtual void loop() = 0;
228
229 void setUp(PdfTokenLooper* parent) {
230 fParent = parent;
231 fTokenizer = parent->fTokenizer;
232 fPdfContext = parent->fPdfContext;
233 fCanvas = parent->fCanvas;
234 }
edisonn@google.com78b38b12013-07-15 18:20:58 +0000235
236 SkPdfNativeTokenizer* tokenizer() { return fTokenizer; }
edisonn@google.com222382b2013-07-10 22:33:10 +0000237};
238
239class PdfMainLooper : public PdfTokenLooper {
240public:
241 PdfMainLooper(PdfTokenLooper* parent,
242 SkPdfNativeTokenizer* tokenizer,
edisonn@google.com3aa35552013-08-14 18:26:20 +0000243 SkPdfContext* pdfContext,
edisonn@google.com222382b2013-07-10 22:33:10 +0000244 SkCanvas* canvas)
245 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
246
edisonn@google.com3aa35552013-08-14 18:26:20 +0000247 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000248 virtual void loop();
249};
250
251class PdfInlineImageLooper : public PdfTokenLooper {
252public:
253 PdfInlineImageLooper()
254 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
255
edisonn@google.com3aa35552013-08-14 18:26:20 +0000256 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000257 virtual void loop();
edisonn@google.com3aa35552013-08-14 18:26:20 +0000258 SkPdfResult done();
edisonn@google.com222382b2013-07-10 22:33:10 +0000259};
260
261class PdfCompatibilitySectionLooper : public PdfTokenLooper {
262public:
263 PdfCompatibilitySectionLooper()
264 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
265
edisonn@google.com3aa35552013-08-14 18:26:20 +0000266 virtual SkPdfResult consumeToken(PdfToken& token);
edisonn@google.com222382b2013-07-10 22:33:10 +0000267 virtual void loop();
268};
269
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000270// Utilities
271static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
272 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
273
274 bitmap->allocPixels();
275 bitmap->eraseColor(color);
276}
277
edisonn@google.come50d9a12013-10-10 20:58:22 +0000278// TODO(edisonn): synonyms? /DeviceRGB and /RGB mean the same thing. Context dependent.
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000279static int GetColorSpaceComponents(NotOwnedString& colorSpace) {
280 if (colorSpace.equals("DeviceCMYK")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000281 return 4;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000282 } else if (colorSpace.equals("DeviceGray") ||
283 colorSpace.equals("CalGray") ||
284 colorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000285 return 1;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000286 } else if (colorSpace.equals("DeviceRGB") ||
287 colorSpace.equals("CalRGB") ||
288 colorSpace.equals("Lab")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000289 return 3;
290 } else {
291 return 0;
292 }
293}
294
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000295SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000296 SkMatrix matrix;
297 matrix.setAll(SkDoubleToScalar(array[0]),
298 SkDoubleToScalar(array[2]),
299 SkDoubleToScalar(array[4]),
300 SkDoubleToScalar(array[1]),
301 SkDoubleToScalar(array[3]),
302 SkDoubleToScalar(array[5]),
303 SkDoubleToScalar(0),
304 SkDoubleToScalar(0),
305 SkDoubleToScalar(1));
306
307 return matrix;
308}
309
310SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
311 double array[6];
312
313 // TODO(edisonn): security issue, ret if size() != 6
edisonn@google.comaf54a512013-09-13 19:33:42 +0000314 if (pdfArray == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000315 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue,
316 "null array passed to build matrix", NULL, NULL);
edisonn@google.comaf54a512013-09-13 19:33:42 +0000317 return SkMatrix::I();
318 }
319
320 if (pdfArray->size() != 6) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000321 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnexpectedArraySize_SkPdfIssue,
322 "null array passed to build matrix", pdfArray, NULL);
edisonn@google.comaf54a512013-09-13 19:33:42 +0000323 return SkMatrix::I();
324 }
325
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000326 for (int i = 0; i < 6; i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000327 const SkPdfNativeObject* elem = pdfArray->operator [](i);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000328 if (elem == NULL || !elem->isNumber()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000329 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, elem,
330 SkPdfNativeObject::_kNumber_PdfObjectType, NULL);
edisonn@google.comaf54a512013-09-13 19:33:42 +0000331 return SkMatrix::I();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000332 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000333 array[i] = elem->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000334 }
335
336 return SkMatrixFromPdfMatrix(array);
337}
338
edisonn@google.come50d9a12013-10-10 20:58:22 +0000339// TODO(edisonn): debug code, used to analyze rendering when we find bugs.
edisonn@google.com3aa35552013-08-14 18:26:20 +0000340extern "C" SkPdfNativeDoc* gDoc;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000341SkBitmap* gDumpBitmap = NULL;
342SkCanvas* gDumpCanvas = NULL;
343char gLastKeyword[100] = "";
344int gLastOpKeyword = -1;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000345int gReadOp = 0;
346
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000347#ifdef PDF_TRACE_DIFF_IN_PNG
edisonn@google.come50d9a12013-10-10 20:58:22 +0000348char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000349static bool hasVisualEffect(const char* pdfOp) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000350 return true;
351 if (*pdfOp == '\0') return false;
352
353 char markedPdfOp[100] = ",";
354 strcat(markedPdfOp, pdfOp);
355 strcat(markedPdfOp, ",");
356
357 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
358}
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000359#endif // PDF_TRACE_DIFF_IN_PNG
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000360
edisonn@google.com3aa35552013-08-14 18:26:20 +0000361// TODO(edisonn): Pass SkPdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000362static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000363 bool ret = fTokenizer->readToken(token);
364
365 gReadOp++;
edisonn@google.com0f901902013-08-07 11:56:16 +0000366 gLastOpKeyword++;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000367#ifdef PDF_TRACE_DIFF_IN_PNG
edisonn@google.come50d9a12013-10-10 20:58:22 +0000368 // TODO(edisonn): this code is used to make a step by step history of all the draw operations
369 // so we could find the step where something is wrong.
370 if (gLastKeyword[0] && hasVisualEffect(gLastKeyword)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000371 gDumpCanvas->flush();
372
373 SkBitmap bitmap;
374 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
375
376 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
377
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000378 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000379 SkCanvas canvas(device);
380
381 // draw context stuff here
382 SkPaint blueBorder;
383 blueBorder.setColor(SK_ColorBLUE);
384 blueBorder.setStyle(SkPaint::kStroke_Style);
385 blueBorder.setTextSize(SkDoubleToScalar(20));
386
387 SkString str;
388
389 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
390 if (clipStack) {
391 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
392 const SkClipStack::Element* elem;
393 double y = 0;
394 int total = 0;
edisonn@google.com91ce6982013-08-05 20:45:40 +0000395 while ((elem = iter.next()) != NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000396 total++;
397 y += 30;
398
399 switch (elem->getType()) {
400 case SkClipStack::Element::kRect_Type:
401 canvas.drawRect(elem->getRect(), blueBorder);
edisonn@google.come50d9a12013-10-10 20:58:22 +0000402 canvas.drawText("Rect Clip", strlen("Rect Clip"),
403 SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000404 break;
405 case SkClipStack::Element::kPath_Type:
406 canvas.drawPath(elem->getPath(), blueBorder);
edisonn@google.come50d9a12013-10-10 20:58:22 +0000407 canvas.drawText("Path Clip", strlen("Path Clip"),
408 SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000409 break;
410 case SkClipStack::Element::kEmpty_Type:
edisonn@google.come50d9a12013-10-10 20:58:22 +0000411 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"),
412 SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000413 break;
414 default:
edisonn@google.come50d9a12013-10-10 20:58:22 +0000415 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"),
416 SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000417 break;
418 }
419 }
420
421 y += 30;
422 str.printf("Number of clips in stack: %i", total);
edisonn@google.come50d9a12013-10-10 20:58:22 +0000423 canvas.drawText(str.c_str(), str.size(),
424 SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000425 }
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
edisonn@google.come50d9a12013-10-10 20:58:22 +0000440 // TODO(edisonn): overlay on top of image inf about the clip , grafic state, the stack
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000441
edisonn@google.come50d9a12013-10-10 20:58:22 +0000442 out.appendf("/tmp/log_step_by_step/step-%i-%s.png",
443 gLastOpKeyword, gLastKeyword);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000444 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
445 }
edisonn@google.com0f901902013-08-07 11:56:16 +0000446
edisonn@google.come50d9a12013-10-10 20:58:22 +0000447 if (ret && token->fType == kKeyword_TokenType &&
448 token->fKeyword && token->fKeywordLength > 0 && token->fKeywordLength < 100) {
edisonn@google.com0f901902013-08-07 11:56:16 +0000449 strncpy(gLastKeyword, token->fKeyword, token->fKeywordLength);
450 gLastKeyword[token->fKeywordLength] = '\0';
451 } else {
452 gLastKeyword[0] = '\0';
453 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000454#endif
455
456 return ret;
457}
458
edisonn@google.come50d9a12013-10-10 20:58:22 +0000459// Signature for all the operations available in pdf.
edisonn@google.com3aa35552013-08-14 18:26:20 +0000460typedef SkPdfResult (*PdfOperatorRenderer)(SkPdfContext*, SkCanvas*, PdfTokenLooper**);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000461
edisonn@google.come50d9a12013-10-10 20:58:22 +0000462// Map of string to function pointer for all known draw operations.
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000463SkTDict<PdfOperatorRenderer> gPdfOps(100);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000464
edisonn@google.come50d9a12013-10-10 20:58:22 +0000465// Temp code to measure what operands fail.
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000466template <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{
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000486 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
487 if (skfont == NULL) {
488 skfont = SkPdfFont::Default();
489 }
490
edisonn@google.com571c70b2013-07-10 17:09:50 +0000491 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000492 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
493 "DrawText",
494 _str,
495 SkPdfNativeObject::_kAnyString_PdfObjectType,
496 pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000497 return kIgnoreError_SkPdfResult;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000498 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000499 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000500
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000501 SkUnencodedText binary(str);
502
503 SkDecodedText decoded;
504
505 if (skfont->encoding() == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000506 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingEncoding_SkPdfIssue,
507 "draw text", _str, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000508 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000509 }
510
511 skfont->encoding()->decodeText(binary, &decoded);
512
513 SkPaint paint;
edisonn@google.come50d9a12013-10-10 20:58:22 +0000514 // TODO(edisonn): does size 0 mean anything special?
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000515 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
516 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
517 }
518
edisonn@google.come50d9a12013-10-10 20:58:22 +0000519 // TODO(edisonn): implement font scaler
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000520// if (fCurFont && fCurFont->GetFontScale() != 0) {
521// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
522// }
523
524 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
525
edisonn@google.com6e49c342013-06-27 20:03:43 +0000526 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000527
edisonn@google.com3aa35552013-08-14 18:26:20 +0000528 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000529}
530
531// TODO(edisonn): create header files with declarations!
edisonn@google.com3aa35552013-08-14 18:26:20 +0000532SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
533SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
534SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
535SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000536
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000537// TODO(edisonn): perf!!!
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000538static SkColorTable* getGrayColortable() {
539 static SkColorTable* grayColortable = NULL;
540 if (grayColortable == NULL) {
541 SkPMColor* colors = new SkPMColor[256];
542 for (int i = 0 ; i < 256; i++) {
543 colors[i] = SkPreMultiplyARGB(255, i, i, i);
544 }
545 grayColortable = new SkColorTable(colors, 256);
546 }
547 return grayColortable;
548}
549
edisonn@google.comaf54a512013-09-13 19:33:42 +0000550static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream,
551 size_t uncompressedStreamLength,
552 int width, int height, int bytesPerLine,
553 int bpc, const SkString& colorSpace,
554 bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000555 SkBitmap* bitmap = new SkBitmap();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000556
edisonn@google.com571c70b2013-07-10 17:09:50 +0000557 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000558//#define MAX_COMPONENTS 10
559
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000560 // TODO(edisonn): assume start of lines are aligned at 32 bits?
561 // Is there a faster way to load the uncompressed stream into a bitmap?
562
563 // minimal support for now
edisonn@google.com063d7072013-08-16 15:05:08 +0000564 if ((colorSpace.equals("DeviceRGB") || colorSpace.equals("RGB")) && bpc == 8) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000565 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
566
567 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000568 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000569 for (int w = 0 ; w < width; w++) {
570 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
571 uncompressedStream[3 * w + 1],
572 uncompressedStream[3 * w + 2]);
573 i++;
574 }
575 uncompressedStream += bytesPerLine;
576 }
577
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000578 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
579 bitmap->setPixels(uncompressedStreamArgb);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000580 }
edisonn@google.com063d7072013-08-16 15:05:08 +0000581 else if ((colorSpace.equals("DeviceGray") || colorSpace.equals("Gray")) && bpc == 8) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000582 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
583
584 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000585 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000586 for (int w = 0 ; w < width; w++) {
587 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
588 uncompressedStream[w];
589 i++;
590 }
591 uncompressedStream += bytesPerLine;
592 }
593
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000594 bitmap->setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000595 width, height);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000596 bitmap->setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000597 }
598
edisonn@google.comaf54a512013-09-13 19:33:42 +0000599 // TODO(edisonn): pass color space and context here?
600 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI", NULL, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000601 return bitmap;
602}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000603// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
edisonn@google.come50d9a12013-10-10 20:58:22 +0000604// skia format.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000605
edisonn@google.come50d9a12013-10-10 20:58:22 +0000606// This functions returns the image, it does not look at the smask.
607static SkBitmap* getImageFromObjectCore(SkPdfContext* pdfContext,
608 SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000609 if (image == NULL || !image->hasStream()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000610 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", image,
611 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000612 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000613 }
614
edisonn@google.com33f11b62013-08-14 21:35:27 +0000615 int bpc = (int)image->BitsPerComponent(pdfContext->fPdfDoc);
616 int width = (int)image->Width(pdfContext->fPdfDoc);
617 int height = (int)image->Height(pdfContext->fPdfDoc);
edisonn@google.com063d7072013-08-16 15:05:08 +0000618 SkString colorSpace("DeviceRGB");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000619
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000620 bool indexed = false;
621 SkPMColor colors[256];
622 int cnt = 0;
623
edisonn@google.com571c70b2013-07-10 17:09:50 +0000624 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
625 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000626 } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) {
627 SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc);
628 if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") &&
edisonn@google.come50d9a12013-10-10 20:58:22 +0000629 (array->objAtAIndex(1)->isName("DeviceRGB") ||
630 array->objAtAIndex(1)->isName("RGB")) &&
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000631 array->objAtAIndex(2)->isInteger() &&
632 array->objAtAIndex(3)->isHexString()
633 ) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000634 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI",
635 image, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000636 indexed = true;
edisonn@google.com33f11b62013-08-14 21:35:27 +0000637 cnt = (int)array->objAtAIndex(2)->intValue() + 1;
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000638 if (cnt > 256) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000639 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
640 "Color space feature NYI, cnt > 256", image, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000641 return NULL;
642 }
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000643 NotOwnedString data = array->objAtAIndex(3)->strRef();
644 if (data.fBytes != (unsigned int)cnt * 3) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000645 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
646 "Image color table mismatch color space specs", array, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000647 return NULL;
648 }
649 for (int i = 0 ; i < cnt; i++) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000650 colors[i] = SkPreMultiplyARGB(0xff,
651 data.fBuffer[3 * i],
652 data.fBuffer[3 * i + 1],
653 data.fBuffer[3 * i + 2]);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000654 }
655 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000656 }
657
edisonn@google.come50d9a12013-10-10 20:58:22 +0000658 // TODO(edisonn): implement image masks.
659/* bool imageMask = image->imageMask();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000660 if (imageMask) {
661 if (bpc != 0 && bpc != 1) {
662 // TODO(edisonn): report warning to be used in testing.
663 return SkBitmap();
664 }
665 bpc = 1;
666 }
667*/
668
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000669 const unsigned char* uncompressedStream = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000670 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000671
edisonn@google.com571c70b2013-07-10 17:09:50 +0000672 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000673
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000674 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000675 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000676 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", stream,
677 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000678 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000679 }
680
edisonn@google.com571c70b2013-07-10 17:09:50 +0000681 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
682
edisonn@google.come50d9a12013-10-10 20:58:22 +0000683 if (streamDict->has_Filter() &&
684 ((streamDict->isFilterAName(NULL) &&
685 streamDict->getFilterAsName(NULL).equals("DCTDecode")) ||
686 (streamDict->isFilterAArray(NULL) &&
687 streamDict->getFilterAsArray(NULL)->size() > 0 &&
688 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
689 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2()
690 .equals("DCTDecode")))) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000691 SkBitmap* bitmap = new SkBitmap();
692 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000693 return bitmap;
694 }
695
edisonn@google.come50d9a12013-10-10 20:58:22 +0000696 // TODO(edisonn): assumes RGB for now, since it is the only one implemented
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000697 if (indexed) {
698 SkBitmap* bitmap = new SkBitmap();
699 bitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
700 SkColorTable* colorTable = new SkColorTable(colors, cnt);
701 bitmap->setPixels((void*)uncompressedStream, colorTable);
702 return bitmap;
703 }
704
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000705 int bytesPerLine = (int)(uncompressedStreamLength / height);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000706#ifdef PDF_TRACE
707 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000708 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000709 }
710#endif
711
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000712 SkBitmap* bitmap = transferImageStreamToBitmap(
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000713 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000714 (int)width, (int)height, bytesPerLine,
715 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000716 transparencyMask);
717
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000718 return bitmap;
719}
720
edisonn@google.come50d9a12013-10-10 20:58:22 +0000721static SkBitmap* getImageFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* image,
722 bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000723 if (!transparencyMask) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000724 if (!image->hasData(SkPdfNativeObject::kBitmap_Data)) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000725 SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000726 image->setData(bitmap, SkPdfNativeObject::kBitmap_Data);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000727 }
edisonn@google.com3aa35552013-08-14 18:26:20 +0000728 return (SkBitmap*) image->data(SkPdfNativeObject::kBitmap_Data);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000729 } else {
730 return getImageFromObjectCore(pdfContext, image, transparencyMask);
731 }
732}
733
edisonn@google.com3aa35552013-08-14 18:26:20 +0000734static SkBitmap* getSmaskFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000735 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000736
737 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000738 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000739 }
740
741 // TODO(edisonn): implement GS SMask. Default to empty right now.
edisonn@google.come50d9a12013-10-10 20:58:22 +0000742 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
743 "implement GS SMask. Default to empty right now.", obj, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +0000744
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000745 return pdfContext->fGraphicsState.fSMask;
746}
747
edisonn@google.come50d9a12013-10-10 20:58:22 +0000748static SkPdfResult doXObject_Image(SkPdfContext* pdfContext, SkCanvas* canvas,
749 SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000750 if (skpdfimage == NULL) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000751 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000752 }
753
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000754 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
755 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000756
757 canvas->save();
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000758 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000759
edisonn@google.com571c70b2013-07-10 17:09:50 +0000760 SkScalar z = SkIntToScalar(0);
761 SkScalar one = SkIntToScalar(1);
762
edisonn@google.come50d9a12013-10-10 20:58:22 +0000763 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z),
764 SkPoint::Make(one, one), SkPoint::Make(z, one)};
765 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one),
766 SkPoint::Make(one, z), SkPoint::Make(z, z)};
edisonn@google.com571c70b2013-07-10 17:09:50 +0000767 SkMatrix flip;
768 SkAssertResult(flip.setPolyToPoly(from, to, 4));
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000769 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000770 solveImageFlip.preConcat(flip);
771 canvas->setMatrix(solveImageFlip);
edisonn@google.com0f901902013-08-07 11:56:16 +0000772
773#ifdef PDF_TRACE
edisonn@google.come50d9a12013-10-10 20:58:22 +0000774 SkPoint final[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z),
775 SkPoint::Make(one, one), SkPoint::Make(z, one)};
edisonn@google.com0f901902013-08-07 11:56:16 +0000776 solveImageFlip.mapPoints(final, 4);
777 printf("IMAGE rect = ");
778 for (int i = 0; i < 4; i++) {
779 printf("(%f %f) ", SkScalarToDouble(final[i].x()), SkScalarToDouble(final[i].y()));
780 }
781 printf("\n");
782#endif // PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +0000783
edisonn@google.come50d9a12013-10-10 20:58:22 +0000784 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0),
785 SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000786
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000787 // TODO(edisonn): soft mask type? alpha/luminosity.
edisonn@google.come50d9a12013-10-10 20:58:22 +0000788 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
789 "implement soft mask type", skpdfimage, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +0000790
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000791 SkPaint paint;
792 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
793
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000794 if (!sMask || sMask->empty()) {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000795 canvas->drawBitmapRect(*image, dst, &paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000796 } else {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000797 canvas->saveLayer(&dst, &paint);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000798 canvas->drawBitmapRect(*image, dst, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000799 SkPaint xfer;
edisonn@google.come50d9a12013-10-10 20:58:22 +0000800 xfer.setXfermodeMode(SkXfermode::kSrcOut_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
edisonn@google.come50d9a12013-10-10 20:58:22 +0000816// CON: multiple drawings (but on smaller areas), pay a price at loading pdf to
817// compute a pdf draw plan
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000818// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
819// 3) support them natively in SkCanvas
820// PRO: simple
821// CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1)
822// 4) compile a plan using pathops, and render once without any fancy rules with backdrop
823// PRO: simple, fast
824// CON: pathops must be bug free first + time to compute new paths
825// pay a price at loading pdf to compute a pdf draw plan
826// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
edisonn@google.come50d9a12013-10-10 20:58:22 +0000827// 5) for knockout, render the objects in reverse order, and add every object to the clip, and any
828// new draw will be cliped
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000829
edisonn@google.come50d9a12013-10-10 20:58:22 +0000830static void doGroup_before(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox,
831 SkPdfTransparencyGroupDictionary* tgroup, bool page) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000832 SkRect bboxOrig = bbox;
833 SkBitmap backdrop;
834 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
835// bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000836 SkPaint paint;
837 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
838 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000839}
840
edisonn@google.come50d9a12013-10-10 20:58:22 +0000841// TODO(edisonn): non isolation should probably be implemented in skia
842//static void doGroup_after(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox,
843// SkPdfTransparencyGroupDictionary* tgroup) {
edisonn@google.com251176e2013-08-01 13:24:00 +0000844// if not isolated
845// canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000846//}
847
edisonn@google.come50d9a12013-10-10 20:58:22 +0000848static SkPdfResult doXObject_Form(SkPdfContext* pdfContext, SkCanvas* canvas,
849 SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000850 if (!skobj || !skobj->hasStream()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000851 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj,
852 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.come50d9a12013-10-10 20:58:22 +0000857 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox",
858 skobj, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000859 return kIgnoreError_SkPdfResult;
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000860 }
861
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000862 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000863
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000864
edisonn@google.com571c70b2013-07-10 17:09:50 +0000865 if (skobj->Resources(pdfContext->fPdfDoc)) {
866 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000867 }
868
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000869 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000870
edisonn@google.com571c70b2013-07-10 17:09:50 +0000871 if (skobj->has_Matrix()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000872 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000873 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
874 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
875 pdfContext->fGraphicsState.fMatrixTm = matrix;
876 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.come50d9a12013-10-10 20:58:22 +0000877 // TODO(edisonn): text matrixes mosltly NYI
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000878 }
879
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000880 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com0f901902013-08-07 11:56:16 +0000881 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000882
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000883 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000884
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000885 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
edisonn@google.come50d9a12013-10-10 20:58:22 +0000886 // TODO(edisonn): constants (AA) from settings.
887 canvas->clipRect(bbox, SkRegion::kIntersect_Op, false);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000888
edisonn@google.come878e722013-07-29 19:10:58 +0000889 // This is a group?
890 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000891 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
892 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
edisonn@google.come878e722013-07-29 19:10:58 +0000893 }
894
edisonn@google.com571c70b2013-07-10 17:09:50 +0000895 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000896
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000897 SkPdfNativeTokenizer* tokenizer =
898 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000899 if (tokenizer != NULL) {
900 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
901 looper.loop();
902 delete tokenizer;
903 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000904
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000905 if (skobj->has_Group()) {
906 canvas->restore();
907 }
908
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000909 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000910 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000911}
912
edisonn@google.come50d9a12013-10-10 20:58:22 +0000913static SkPdfResult doXObject_Pattern(SkPdfContext* pdfContext, SkCanvas* canvas,
914 SkPdfType1PatternDictionary* skobj) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000915 if (!skobj || !skobj->hasStream()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000916 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream",
917 skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000918 return kIgnoreError_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000919 }
920
921 if (!skobj->has_BBox()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000922 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox",
923 skobj, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000924 return kIgnoreError_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000925 }
926
927 PdfOp_q(pdfContext, canvas, NULL);
928
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000929
930 if (skobj->Resources(pdfContext->fPdfDoc)) {
931 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
932 }
933
edisonn@google.com0f901902013-08-07 11:56:16 +0000934 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000935
936 if (skobj->has_Matrix()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000937 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat(
938 skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000939 }
940
edisonn@google.com0f901902013-08-07 11:56:16 +0000941 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000942
edisonn@google.com0f901902013-08-07 11:56:16 +0000943 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix);
944 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000945
946 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
edisonn@google.come50d9a12013-10-10 20:58:22 +0000947 // TODO(edisonn): constants (AA) from settings.
948 canvas->clipRect(bbox, SkRegion::kIntersect_Op, false);
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000949
950 SkPdfStream* stream = (SkPdfStream*)skobj;
951
952 SkPdfNativeTokenizer* tokenizer =
953 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
954 if (tokenizer != NULL) {
955 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
956 looper.loop();
957 delete tokenizer;
958 }
959
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000960 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000961 return kPartial_SkPdfResult;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000962}
963
edisonn@google.come50d9a12013-10-10 20:58:22 +0000964// TODO(edisonn): PS NYI
965//static SkPdfResult doXObject_PS(SkPdfContext* pdfContext, SkCanvas* canvas,
966// const SkPdfNativeObject* obj) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000967// return kNYI_SkPdfResult;
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000968//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000969
edisonn@google.come50d9a12013-10-10 20:58:22 +0000970SkPdfResult doType3Char(SkPdfContext* pdfContext, SkCanvas* canvas, const SkPdfNativeObject* skobj,
971 SkRect bBox, SkMatrix matrix, double textSize) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000972 if (!skobj || !skobj->hasStream()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +0000973 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj,
974 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +0000975 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000976 }
977
978 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000979
980 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
edisonn@google.come50d9a12013-10-10 20:58:22 +0000981 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize),
982 SkDoubleToScalar(textSize));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000983 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000984
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000985 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.come57c62d2013-08-07 18:04:15 +0000986 pdfContext->fGraphicsState.fCTM.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000987
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000988 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000989
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000990 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000991
992 SkRect rm = bBox;
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000993 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000994
995 SkTraceRect(rm, "bbox mapped");
996
edisonn@google.come50d9a12013-10-10 20:58:22 +0000997 // TODO(edisonn): constants (AA) from settings.
998 canvas->clipRect(bBox, SkRegion::kIntersect_Op, false);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000999
edisonn@google.com571c70b2013-07-10 17:09:50 +00001000 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001001
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001002 SkPdfNativeTokenizer* tokenizer =
1003 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001004 if (tokenizer != NULL) {
1005 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
1006 looper.loop();
1007 delete tokenizer;
1008 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001009
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001010 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001011
edisonn@google.com3aa35552013-08-14 18:26:20 +00001012 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001013}
1014
edisonn@google.come50d9a12013-10-10 20:58:22 +00001015// The PDF could be corrupted so a dict refers recursively to the same dict, if this happens
1016// we end up with a stack overflow and crash.
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001017class CheckRecursiveRendering {
edisonn@google.com063d7072013-08-16 15:05:08 +00001018 SkPdfNativeObject* fObj;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001019public:
edisonn@google.com063d7072013-08-16 15:05:08 +00001020 CheckRecursiveRendering(SkPdfNativeObject* obj) : fObj(obj) {
1021 SkASSERT(!obj->inRendering());
1022 obj->startRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001023 }
1024
1025 ~CheckRecursiveRendering() {
edisonn@google.com063d7072013-08-16 15:05:08 +00001026 SkASSERT(fObj->inRendering());
1027 fObj->doneRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001028 }
1029
edisonn@google.com3aa35552013-08-14 18:26:20 +00001030 static bool IsInRendering(const SkPdfNativeObject* obj) {
edisonn@google.com063d7072013-08-16 15:05:08 +00001031 return obj->inRendering();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001032 }
1033};
1034
edisonn@google.com063d7072013-08-16 15:05:08 +00001035static SkPdfResult doXObject(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfNativeObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001036 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001037 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kRecursiveReferencing_SkPdfIssue,
1038 "Recursive reverencing is invalid in draw objects", obj, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001039 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001040 }
1041
edisonn@google.com571c70b2013-07-10 17:09:50 +00001042 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001043
edisonn@google.com571c70b2013-07-10 17:09:50 +00001044 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001045 {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001046 case kImageDictionary_SkPdfNativeObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001047 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001048 case kType1FormDictionary_SkPdfNativeObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001049 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001050 //case kObjectDictionaryXObjectPS_SkPdfNativeObjectType:
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001051 //return doXObject_PS(skxobj.asPS());
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001052 default: {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001053 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) !=
1054 kNone_SkPdfNativeObjectType) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001055 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
1056 return doXObject_Pattern(pdfContext, canvas, pattern);
1057 }
edisonn@google.come50d9a12013-10-10 20:58:22 +00001058 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "doXObject",
1059 obj, pdfContext);
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001060 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001061 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001062 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001063}
1064
edisonn@google.come50d9a12013-10-10 20:58:22 +00001065static SkPdfResult doPage(SkPdfContext* pdfContext, SkCanvas* canvas,
1066 SkPdfPageObjectDictionary* skobj) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001067 if (!skobj || !skobj->isContentsAStream(pdfContext->fPdfDoc)) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001068 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj,
1069 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.come50d9a12013-10-10 20:58:22 +00001076 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream",
1077 skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001078 return kIgnoreError_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001079 }
1080
edisonn@google.com73613c12013-08-22 15:48:35 +00001081 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
1082
1083 if (!pdfContext->fGraphicsState.fResources) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001084 // It might be null because we have not implemented yet inheritance.
1085 return kIgnoreError_SkPdfResult;
edisonn@google.com73613c12013-08-22 15:48:35 +00001086 }
1087
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001088 if (CheckRecursiveRendering::IsInRendering(skobj)) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001089 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kRecursiveReferencing_SkPdfIssue,
1090 "Recursive reverencing is invalid in draw objects", skobj, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001091 return kIgnoreError_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001092 }
1093 CheckRecursiveRendering checkRecursion(skobj);
1094
1095
1096 PdfOp_q(pdfContext, canvas, NULL);
1097
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001098 // TODO(edisonn): MediaBox can be inherited!!!!
edisonn@google.come50d9a12013-10-10 20:58:22 +00001099 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MediaBox inheritance NYI",
1100 NULL, pdfContext);
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001101 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001102 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001103 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
1104 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
1105 } else {
1106 canvas->save();
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001107 }
1108
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001109
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001110 SkPdfNativeTokenizer* tokenizer =
1111 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
1112 if (tokenizer != NULL) {
1113 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
1114 looper.loop();
1115 delete tokenizer;
1116 }
1117
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001118 canvas->restore();
1119 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001120 return kPartial_SkPdfResult;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00001121}
1122
edisonn@google.com3aa35552013-08-14 18:26:20 +00001123SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001124 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1125 canvas->save();
edisonn@google.comf68aed32013-08-22 15:37:21 +00001126 pdfContext->fObjectStack.nest();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001127 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001128}
1129
edisonn@google.com3aa35552013-08-14 18:26:20 +00001130SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comf68aed32013-08-22 15:37:21 +00001131 if (pdfContext->fStateStack.count() > 0) {
1132 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1133 pdfContext->fStateStack.pop();
1134 canvas->restore();
1135
edisonn@google.comaf54a512013-09-13 19:33:42 +00001136 if (pdfContext->fObjectStack.nests() == 0) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001137 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackNestingOverflow_SkPdfIssue,
1138 "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.come50d9a12013-10-10 20:58:22 +00001144 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackOverflow_SkPdfIssue,
1145 "stack overflow (q/Q)", NULL, pdfContext);
edisonn@google.comf68aed32013-08-22 15:37:21 +00001146 return kIgnoreError_SkPdfResult;
1147 }
1148
edisonn@google.com3aa35552013-08-14 18:26:20 +00001149 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001150}
1151
edisonn@google.com3aa35552013-08-14 18:26:20 +00001152static SkPdfResult PdfOp_cm(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001153 EXPECT_OPERANDS("cm", pdfContext, 6);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001154 POP_NUMBER(pdfContext, f);
1155 POP_NUMBER(pdfContext, e);
1156 POP_NUMBER(pdfContext, d);
1157 POP_NUMBER(pdfContext, c);
1158 POP_NUMBER(pdfContext, b);
1159 POP_NUMBER(pdfContext, a);
1160 CHECK_PARAMETERS();
1161 double array[6] = {a, b, c, d, e, f};
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001162
1163 // a b
1164 // c d
1165 // e f
1166
1167 // 0 1
1168 // 2 3
1169 // 4 5
1170
1171 // sx ky
1172 // kx sy
1173 // tx ty
1174 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1175
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001176 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001177
1178#ifdef PDF_TRACE
1179 printf("cm ");
1180 for (int i = 0 ; i < 6 ; i++) {
1181 printf("%f ", array[i]);
1182 }
1183 printf("\n");
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001184 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001185#endif
1186
edisonn@google.com3aa35552013-08-14 18:26:20 +00001187 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001188}
1189
1190//leading TL Set the text leading, Tl
1191//, to leading, which is a number expressed in unscaled text
1192//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00001193static SkPdfResult PdfOp_TL(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001194 EXPECT_OPERANDS("TL", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001195 POP_NUMBER(pdfContext, ty);
1196 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001197
1198 pdfContext->fGraphicsState.fTextLeading = ty;
1199
edisonn@google.com3aa35552013-08-14 18:26:20 +00001200 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001201}
1202
edisonn@google.com3aa35552013-08-14 18:26:20 +00001203static SkPdfResult PdfOp_Td(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001204 EXPECT_OPERANDS("Td", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001205 POP_NUMBER(pdfContext, ty);
1206 POP_NUMBER(pdfContext, tx);
1207 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001208
edisonn@google.come57c62d2013-08-07 18:04:15 +00001209 double array[6] = {1, 0, 0, 1, tx, -ty};
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001210 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1211
1212 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1213 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1214
edisonn@google.com3aa35552013-08-14 18:26:20 +00001215 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001216}
1217
edisonn@google.com3aa35552013-08-14 18:26:20 +00001218static SkPdfResult PdfOp_TD(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001219 EXPECT_OPERANDS("TD", pdfContext, 2)
edisonn@google.combd2f3012013-08-22 14:18:04 +00001220 POP_NUMBER(pdfContext, ty);
1221 POP_NUMBER(pdfContext, tx);
1222 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001223
edisonn@google.com571c70b2013-07-10 17:09:50 +00001224 // TODO(edisonn): Create factory methods or constructors so native is hidden
1225 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001226 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001227
1228 PdfOp_TL(pdfContext, canvas, looper);
1229
edisonn@google.com571c70b2013-07-10 17:09:50 +00001230 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001231 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001232
edisonn@google.com571c70b2013-07-10 17:09:50 +00001233 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001234 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001235
edisonn@google.com3aa35552013-08-14 18:26:20 +00001236 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001237
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001238 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001239}
1240
edisonn@google.com3aa35552013-08-14 18:26:20 +00001241static SkPdfResult PdfOp_Tm(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001242 EXPECT_OPERANDS("Tm", pdfContext, 6);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001243 POP_NUMBER(pdfContext, f);
1244 POP_NUMBER(pdfContext, e);
1245 POP_NUMBER(pdfContext, d);
1246 POP_NUMBER(pdfContext, c);
1247 POP_NUMBER(pdfContext, b);
1248 POP_NUMBER(pdfContext, a);
1249 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001250
1251 double array[6];
1252 array[0] = a;
1253 array[1] = b;
1254 array[2] = c;
1255 array[3] = d;
1256 array[4] = e;
1257 array[5] = f;
1258
1259 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001260 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
edisonn@google.come57c62d2013-08-07 18:04:15 +00001261 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001262
edisonn@google.come50d9a12013-10-10 20:58:22 +00001263 // TODO(edisonn): NYI - Text positioning.
1264 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
1265 "Text positioning not implemented for 2+ chars", NULL, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00001266
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001267 pdfContext->fGraphicsState.fMatrixTm = matrix;
1268 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1269
edisonn@google.com3aa35552013-08-14 18:26:20 +00001270 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001271}
1272
1273//— T* Move to the start of the next line. This operator has the same effect as the code
1274//0 Tl Td
1275//where Tl is the current leading parameter in the text state
edisonn@google.come50d9a12013-10-10 20:58:22 +00001276static SkPdfResult PdfOp_T_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1277 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
edisonn@google.come50d9a12013-10-10 20:58:22 +00001419 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x),
1420 SkDoubleToScalar(y),
1421 SkDoubleToScalar(x + width),
1422 SkDoubleToScalar(y + height));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001423
1424 pdfContext->fGraphicsState.fCurPosX = x;
1425 pdfContext->fGraphicsState.fCurPosY = y + height;
1426
edisonn@google.com3aa35552013-08-14 18:26:20 +00001427 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001428}
1429
edisonn@google.com3aa35552013-08-14 18:26:20 +00001430static SkPdfResult PdfOp_h(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001431 pdfContext->fGraphicsState.fPath.close();
edisonn@google.com3aa35552013-08-14 18:26:20 +00001432 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001433}
1434
edisonn@google.come50d9a12013-10-10 20:58:22 +00001435static SkPdfResult PdfOp_fillAndStroke(SkPdfContext* pdfContext, SkCanvas* canvas,
1436 bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001437 SkPath path = pdfContext->fGraphicsState.fPath;
1438
1439 if (close) {
1440 path.close();
1441 }
1442
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001443 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001444
1445 SkPaint paint;
1446
1447 SkPoint line[2];
1448 if (fill && !stroke && path.isLine(line)) {
1449 paint.setStyle(SkPaint::kStroke_Style);
1450
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001451 // TODO(edisonn): implement this with patterns
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001452 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1453 paint.setStrokeWidth(SkDoubleToScalar(0));
1454
1455 canvas->drawPath(path, paint);
1456 } else {
1457 if (fill) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001458 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer,
1459 "Pattern", strlen("Pattern")) == 0 &&
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001460 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1461
edisonn@google.come50d9a12013-10-10 20:58:22 +00001462 // TODO(edisonn): we can use a shader here, like imageshader to draw fast.
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001463
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001464 PdfOp_q(pdfContext, canvas, NULL);
1465
1466 if (evenOdd) {
1467 path.setFillType(SkPath::kEvenOdd_FillType);
1468 }
1469 canvas->clipPath(path);
1470
edisonn@google.come50d9a12013-10-10 20:58:22 +00001471 if (pdfContext->fPdfDoc
1472 ->mapper()
1473 ->mapType1PatternDictionary(pdfContext->fGraphicsState
1474 .fNonStroking
1475 .fPattern)
1476 != kNone_SkPdfNativeObjectType) {
1477 SkPdfType1PatternDictionary* pattern
1478 = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState
1479 .fNonStroking
1480 .fPattern;
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001481
edisonn@google.come50d9a12013-10-10 20:58:22 +00001482 // TODO(edisonn): make PaintType constants
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001483 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001484 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
1485 // it will change the result iterating in reverse
edisonn@google.come50d9a12013-10-10 20:58:22 +00001486 // remove then the following bounds.sort();
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001487 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
1488 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001489
edisonn@google.come50d9a12013-10-10 20:58:22 +00001490 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
1491 "paterns x/y step is forced to positive number",
1492 pattern, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00001493
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001494 SkRect bounds = path.getBounds();
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001495 bounds.sort();
1496
1497 SkScalar x;
1498 SkScalar y;
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001499
1500 y = bounds.top();
1501 int totalx = 0;
1502 int totaly = 0;
1503 while (y < bounds.bottom()) {
1504 x = bounds.left();
1505 totalx = 0;
1506
1507 while (x < bounds.right()) {
1508 doXObject(pdfContext, canvas, pattern);
1509
edisonn@google.come50d9a12013-10-10 20:58:22 +00001510 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1511 SkIntToScalar(xStep), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001512 totalx += xStep;
1513 x += SkIntToScalar(xStep);
1514 }
edisonn@google.come50d9a12013-10-10 20:58:22 +00001515 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1516 SkIntToScalar(-totalx), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001517
edisonn@google.come50d9a12013-10-10 20:58:22 +00001518 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1519 SkIntToScalar(0), SkIntToScalar(-yStep));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001520 totaly += yStep;
1521 y += SkIntToScalar(yStep);
1522 }
edisonn@google.come50d9a12013-10-10 20:58:22 +00001523 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1524 SkIntToScalar(0), SkIntToScalar(totaly));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001525 }
1526 }
1527
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001528 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001529 } else {
1530 paint.setStyle(SkPaint::kFill_Style);
1531 if (evenOdd) {
1532 path.setFillType(SkPath::kEvenOdd_FillType);
1533 }
1534
1535 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1536
1537 canvas->drawPath(path, paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001538 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001539 }
1540
1541 if (stroke) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001542 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer,
1543 "Pattern", strlen("Pattern")) == 0) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001544 // TODO(edisonn): implement Pattern for strokes
1545 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001546
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001547 paint.setColor(SK_ColorGREEN);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001548
edisonn@google.come50d9a12013-10-10 20:58:22 +00001549 // reset it, just in case it messes up the stroke
1550 path.setFillType(SkPath::kWinding_FillType);
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001551 canvas->drawPath(path, paint);
1552 } else {
1553 paint.setStyle(SkPaint::kStroke_Style);
1554
1555 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1556
edisonn@google.come50d9a12013-10-10 20:58:22 +00001557 // reset it, just in case it messes up the stroke
1558 path.setFillType(SkPath::kWinding_FillType);
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001559 canvas->drawPath(path, paint);
1560 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001561 }
1562 }
1563
1564 pdfContext->fGraphicsState.fPath.reset();
edisonn@google.come50d9a12013-10-10 20:58:22 +00001565 // TODO(edisonn): implement scale/zoom
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001566
1567 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1568#ifndef PDF_DEBUG_NO_CLIPING
1569 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1570#endif
1571 }
1572
1573 //pdfContext->fGraphicsState.fClipPath.reset();
1574 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1575
edisonn@google.com3aa35552013-08-14 18:26:20 +00001576 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001577
1578}
1579
edisonn@google.com3aa35552013-08-14 18:26:20 +00001580static SkPdfResult PdfOp_S(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001581 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1582}
1583
edisonn@google.com3aa35552013-08-14 18:26:20 +00001584static SkPdfResult PdfOp_s(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001585 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1586}
1587
edisonn@google.com3aa35552013-08-14 18:26:20 +00001588static SkPdfResult PdfOp_F(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001589 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1590}
1591
edisonn@google.com3aa35552013-08-14 18:26:20 +00001592static SkPdfResult PdfOp_f(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001593 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1594}
1595
edisonn@google.come50d9a12013-10-10 20:58:22 +00001596static SkPdfResult PdfOp_f_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1597 PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001598 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1599}
1600
edisonn@google.com3aa35552013-08-14 18:26:20 +00001601static SkPdfResult PdfOp_B(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001602 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1603}
1604
edisonn@google.come50d9a12013-10-10 20:58:22 +00001605static SkPdfResult PdfOp_B_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1606 PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001607 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1608}
1609
edisonn@google.com3aa35552013-08-14 18:26:20 +00001610static SkPdfResult PdfOp_b(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001611 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1612}
1613
edisonn@google.come50d9a12013-10-10 20:58:22 +00001614static SkPdfResult PdfOp_b_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1615 PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001616 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1617}
1618
edisonn@google.com3aa35552013-08-14 18:26:20 +00001619static SkPdfResult PdfOp_n(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001620 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001621 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1622#ifndef PDF_DEBUG_NO_CLIPING
1623 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1624#endif
1625 }
1626
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001627 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1628
1629 pdfContext->fGraphicsState.fPathClosed = true;
1630
edisonn@google.com3aa35552013-08-14 18:26:20 +00001631 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001632}
1633
edisonn@google.com3aa35552013-08-14 18:26:20 +00001634static SkPdfResult PdfOp_BT(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001635 pdfContext->fGraphicsState.fTextBlock = true;
edisonn@google.come57c62d2013-08-07 18:04:15 +00001636 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
1637 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
1638 pdfContext->fGraphicsState.fMatrixTm = matrix;
1639 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001640
edisonn@google.com3aa35552013-08-14 18:26:20 +00001641 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001642}
1643
edisonn@google.com3aa35552013-08-14 18:26:20 +00001644static SkPdfResult PdfOp_ET(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001645 if (!pdfContext->fGraphicsState.fTextBlock) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001646 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "ET without BT", NULL,
1647 pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00001648
edisonn@google.com3aa35552013-08-14 18:26:20 +00001649 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001650 }
1651 // 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 +00001652 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001653}
1654
edisonn@google.come50d9a12013-10-10 20:58:22 +00001655static SkPdfResult skpdfGraphicsStateApplyFontCore(SkPdfContext* pdfContext,
1656 const SkPdfNativeObject* fontName, double fontSize) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001657#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001658 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001659#endif
1660
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001661 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1662 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.come50d9a12013-10-10 20:58:22 +00001663 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingFont_SkPdfIssue,
1664 "No font", fontName, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001665 return kIgnoreError_SkPdfResult;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001666 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001667
edisonn@google.come50d9a12013-10-10 20:58:22 +00001668 SkPdfNativeObject* objFont
1669 = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001670 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001671 if (kNone_SkPdfNativeObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001672 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.come50d9a12013-10-10 20:58:22 +00001673 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kInvalidFont_SkPdfIssue,
1674 "Invalid font", objFont, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001675 return kIgnoreError_SkPdfResult;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001676 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001677
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001678 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1679
1680 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1681
1682 if (skfont) {
1683 pdfContext->fGraphicsState.fSkFont = skfont;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001684 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001685 pdfContext->fGraphicsState.fCurFontSize = fontSize;
edisonn@google.com3aa35552013-08-14 18:26:20 +00001686 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001687}
1688
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001689//font size Tf Set the text font, Tf
1690//, to font and the text font size, Tfs, to size. font is the name of a
1691//font resource in the Fontsubdictionary of the current resource dictionary; size is
1692//a number representing a scale factor. There is no initial value for either font or
1693//size; they must be specified explicitly using Tf before any text is shown.
edisonn@google.com3aa35552013-08-14 18:26:20 +00001694static SkPdfResult PdfOp_Tf(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001695 EXPECT_OPERANDS("Tf", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001696 POP_NUMBER(pdfContext, fontSize);
1697 POP_NAME(pdfContext, fontName);
1698 CHECK_PARAMETERS();
1699
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001700 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1701}
1702
edisonn@google.com3aa35552013-08-14 18:26:20 +00001703static SkPdfResult PdfOp_Tj(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001704 EXPECT_OPERANDS("Tj", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001705 POP_STRING(pdfContext, str);
1706 CHECK_PARAMETERS();
1707
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001708 if (!pdfContext->fGraphicsState.fTextBlock) {
1709 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.come50d9a12013-10-10 20:58:22 +00001710 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "Tj without BT", NULL,
1711 pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001712 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001713 }
1714
edisonn@google.combd2f3012013-08-22 14:18:04 +00001715 SkPdfResult ret = DrawText(pdfContext, str, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001716
1717 return ret;
1718}
1719
edisonn@google.come50d9a12013-10-10 20:58:22 +00001720static SkPdfResult PdfOp_quote(SkPdfContext* pdfContext, SkCanvas* canvas,
1721 PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001722 if (!pdfContext->fGraphicsState.fTextBlock) {
1723 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.come50d9a12013-10-10 20:58:22 +00001724 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue,
1725 "' without BT", NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001726 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001727 }
1728
1729 PdfOp_T_star(pdfContext, canvas, looper);
1730 // Do not pop, and push, just transfer the param to Tj
1731 return PdfOp_Tj(pdfContext, canvas, looper);
1732}
1733
edisonn@google.come50d9a12013-10-10 20:58:22 +00001734static SkPdfResult PdfOp_doublequote(SkPdfContext* pdfContext, SkCanvas* canvas,
1735 PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001736 if (!pdfContext->fGraphicsState.fTextBlock) {
1737 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.come50d9a12013-10-10 20:58:22 +00001738 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue,
1739 "\" without BT", NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001740 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001741 }
1742
edisonn@google.comaf54a512013-09-13 19:33:42 +00001743 EXPECT_OPERANDS("\"", pdfContext, 3);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001744 POP_OBJ(pdfContext, str);
1745 POP_OBJ(pdfContext, ac);
1746 POP_OBJ(pdfContext, aw);
1747 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001748
1749 pdfContext->fObjectStack.push(aw);
1750 PdfOp_Tw(pdfContext, canvas, looper);
1751
1752 pdfContext->fObjectStack.push(ac);
1753 PdfOp_Tc(pdfContext, canvas, looper);
1754
1755 pdfContext->fObjectStack.push(str);
1756 PdfOp_quote(pdfContext, canvas, looper);
1757
edisonn@google.com3aa35552013-08-14 18:26:20 +00001758 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001759}
1760
edisonn@google.com3aa35552013-08-14 18:26:20 +00001761static SkPdfResult PdfOp_TJ(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001762 EXPECT_OPERANDS("Tf", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001763 POP_ARRAY(pdfContext, array);
1764 CHECK_PARAMETERS();
1765
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001766 if (!pdfContext->fGraphicsState.fTextBlock) {
1767 // TODO(edisonn): try to recover and draw it any way?
edisonn@google.come50d9a12013-10-10 20:58:22 +00001768 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "TJ without BT", NULL,
1769 pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001770 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001771 }
1772
edisonn@google.com571c70b2013-07-10 17:09:50 +00001773 if (!array->isArray()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001774 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, array,
1775 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001776 return kIgnoreError_SkPdfResult;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001777 }
1778
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001779 for( int i=0; i<static_cast<int>(array->size()); i++ )
1780 {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001781 if (!(*array)[i]) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001782 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
1783 "element [i] is null, no element should be null",
1784 array,
1785 SkPdfNativeObject::_kAnyString_PdfObjectType ||
1786 SkPdfNativeObject::_kNumber_PdfObjectType,
1787 pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00001788 } else if( (*array)[i]->isAnyString()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001789 SkPdfNativeObject* obj = (*array)[i];
edisonn@google.combd2f3012013-08-22 14:18:04 +00001790 DrawText(pdfContext, obj, canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001791 } else if ((*array)[i]->isNumber()) {
1792 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001793 SkMatrix matrix;
1794 matrix.setAll(SkDoubleToScalar(1),
1795 SkDoubleToScalar(0),
1796 // TODO(edisonn): use writing mode, vertical/horizontal.
1797 SkDoubleToScalar(-dx), // amount is substracted!!!
1798 SkDoubleToScalar(0),
1799 SkDoubleToScalar(1),
1800 SkDoubleToScalar(0),
1801 SkDoubleToScalar(0),
1802 SkDoubleToScalar(0),
1803 SkDoubleToScalar(1));
1804
1805 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
edisonn@google.comaf54a512013-09-13 19:33:42 +00001806 } else {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001807 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", (*array)[i],
1808 SkPdfNativeObject::kArray_PdfObjectType ||
1809 SkPdfNativeObject::_kNumber_PdfObjectType,
1810 pdfContext);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001811 }
1812 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001813 return kPartial_SkPdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001814}
1815
edisonn@google.come50d9a12013-10-10 20:58:22 +00001816static SkPdfResult PdfOp_CS_cs(SkPdfContext* pdfContext, SkCanvas* canvas,
1817 SkPdfColorOperator* colorOperator) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001818 EXPECT_OPERANDS("CS/cs", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001819 POP_NAME(pdfContext, name);
1820 CHECK_PARAMETERS();
edisonn@google.com4f898b72013-08-07 21:11:57 +00001821
1822 //Next, get the ColorSpace Dictionary from the Resource Dictionary:
edisonn@google.come50d9a12013-10-10 20:58:22 +00001823 SkPdfDictionary* colorSpaceResource
1824 = pdfContext->fGraphicsState.fResources->ColorSpace(pdfContext->fPdfDoc);
edisonn@google.com4f898b72013-08-07 21:11:57 +00001825
edisonn@google.come50d9a12013-10-10 20:58:22 +00001826 SkPdfNativeObject* colorSpace
1827 = colorSpaceResource ? pdfContext->fPdfDoc
1828 ->resolveReference(colorSpaceResource->get(name)) :
1829 name;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001830
1831 if (colorSpace == NULL) {
1832 colorOperator->fColorSpace = name->strRef();
1833 } else {
1834#ifdef PDF_TRACE
1835 printf("CS = %s\n", colorSpace->toString(0, 0).c_str());
1836#endif // PDF_TRACE
1837 if (colorSpace->isName()) {
1838 colorOperator->fColorSpace = colorSpace->strRef();
1839 } else if (colorSpace->isArray()) {
1840 int cnt = colorSpace->size();
1841 if (cnt == 0) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001842 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
1843 "color space has length 0", colorSpace, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001844 return kIgnoreError_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001845 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001846 SkPdfNativeObject* type = colorSpace->objAtAIndex(0);
edisonn@google.com4f898b72013-08-07 21:11:57 +00001847 type = pdfContext->fPdfDoc->resolveReference(type);
1848
1849 if (type->isName("ICCBased")) {
1850 if (cnt != 2) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00001851 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
1852 "ICCBased color space must have an array with 2 elements",
1853 colorSpace, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001854 return kIgnoreError_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001855 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001856 SkPdfNativeObject* prop = colorSpace->objAtAIndex(1);
edisonn@google.com4f898b72013-08-07 21:11:57 +00001857 prop = pdfContext->fPdfDoc->resolveReference(prop);
1858#ifdef PDF_TRACE
1859 printf("ICCBased prop = %s\n", prop->toString(0, 0).c_str());
1860#endif // PDF_TRACE
1861 // TODO(edisonn): hack
edisonn@google.come50d9a12013-10-10 20:58:22 +00001862 if (prop && prop->isDictionary() && prop->get("N") &&
1863 prop->get("N")->isInteger() && prop->get("N")->intValue() == 3) {
edisonn@google.com4f898b72013-08-07 21:11:57 +00001864 colorOperator->setColorSpace(&strings_DeviceRGB);
edisonn@google.com3aa35552013-08-14 18:26:20 +00001865 return kPartial_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001866 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001867 return kNYI_SkPdfResult;
edisonn@google.com4f898b72013-08-07 21:11:57 +00001868 }
1869 }
1870 }
1871
edisonn@google.com3aa35552013-08-14 18:26:20 +00001872 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001873}
1874
edisonn@google.com3aa35552013-08-14 18:26:20 +00001875static SkPdfResult PdfOp_CS(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001876 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1877}
1878
edisonn@google.com3aa35552013-08-14 18:26:20 +00001879static SkPdfResult PdfOp_cs(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001880 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1881}
1882
edisonn@google.come50d9a12013-10-10 20:58:22 +00001883static SkPdfResult PdfOp_SC_sc(SkPdfContext* pdfContext, SkCanvas* canvas,
1884 SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001885 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001886// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001887
1888 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1889
1890 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001891 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001892 doubles = false;
1893 }
1894
1895#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001896 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001897#endif
1898
edisonn@google.comaf54a512013-09-13 19:33:42 +00001899 EXPECT_OPERANDS("SC/sc", pdfContext, n);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001900
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001901 for (int i = n - 1; i >= 0 ; i--) {
1902 if (doubles) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001903 POP_NUMBER_INTO(pdfContext, c[i]);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001904// } else {
1905// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001906 }
1907 }
edisonn@google.combd2f3012013-08-22 14:18:04 +00001908 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001909
1910 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001911 // TODO(edisonn): do possible field values to enum at parsing time!
edisonn@google.come50d9a12013-10-10 20:58:22 +00001912 // TODO(edisonn): support also abbreviations /DeviceRGB == /RGB
1913 if (colorOperator->fColorSpace.equals("DeviceRGB") ||
1914 colorOperator->fColorSpace.equals("RGB")) {
1915 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]),
1916 (U8CPU)(255*c[1]),
1917 (U8CPU)(255*c[2])));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001918 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00001919 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001920}
1921
edisonn@google.com3aa35552013-08-14 18:26:20 +00001922static SkPdfResult PdfOp_SC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001923 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1924}
1925
edisonn@google.com3aa35552013-08-14 18:26:20 +00001926static SkPdfResult PdfOp_sc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001927 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1928}
1929
edisonn@google.come50d9a12013-10-10 20:58:22 +00001930static SkPdfResult PdfOp_SCN_scn(SkPdfContext* pdfContext, SkCanvas* canvas,
1931 SkPdfColorOperator* colorOperator) {
edisonn@google.combd2f3012013-08-22 14:18:04 +00001932 if (pdfContext->fObjectStack.count() > 0 && pdfContext->fObjectStack.top()->isName()) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00001933 SkPdfNativeObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com276fed92013-08-01 21:20:47 +00001934
edisonn@google.come50d9a12013-10-10 20:58:22 +00001935 SkPdfDictionary* patternResources
1936 = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
edisonn@google.com276fed92013-08-01 21:20:47 +00001937
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001938 if (patternResources == NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001939#ifdef PDF_TRACE
1940 printf("ExtGState is NULL!\n");
1941#endif
edisonn@google.com3aa35552013-08-14 18:26:20 +00001942 return kIgnoreError_SkPdfResult;
edisonn@google.com276fed92013-08-01 21:20:47 +00001943 }
1944
edisonn@google.come50d9a12013-10-10 20:58:22 +00001945 colorOperator->setPatternColorSpace(
1946 pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001947 }
1948
1949 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1950 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1951
edisonn@google.com3aa35552013-08-14 18:26:20 +00001952 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001953}
1954
edisonn@google.com3aa35552013-08-14 18:26:20 +00001955static SkPdfResult PdfOp_SCN(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001956 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1957}
1958
edisonn@google.com3aa35552013-08-14 18:26:20 +00001959static SkPdfResult PdfOp_scn(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001960 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1961}
1962
edisonn@google.come50d9a12013-10-10 20:58:22 +00001963static SkPdfResult PdfOp_G_g(SkPdfContext* pdfContext, SkCanvas* canvas,
1964 SkPdfColorOperator* colorOperator) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001965 EXPECT_OPERANDS("G/g", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001966 POP_NUMBER(pdfContext, gray);
1967 CHECK_PARAMETERS();
1968
edisonn@google.come50d9a12013-10-10 20:58:22 +00001969 // TODO(edisonn): limit gray in [0, 1]
1970
1971 // TODO(edisonn): HACK - it should be device gray, but not suported right now
1972 colorOperator->fColorSpace = strings_DeviceRGB;
1973 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255 * gray),
1974 (U8CPU)(255 * gray),
1975 (U8CPU)(255 * gray)));
edisonn@google.combd2f3012013-08-22 14:18:04 +00001976
1977 return kPartial_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001978}
1979
edisonn@google.com3aa35552013-08-14 18:26:20 +00001980static SkPdfResult PdfOp_G(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001981 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1982}
1983
edisonn@google.com3aa35552013-08-14 18:26:20 +00001984static SkPdfResult PdfOp_g(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001985 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1986}
1987
edisonn@google.come50d9a12013-10-10 20:58:22 +00001988static SkPdfResult PdfOp_RG_rg(SkPdfContext* pdfContext, SkCanvas* canvas,
1989 SkPdfColorOperator* colorOperator) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00001990 EXPECT_OPERANDS("RG/rg", pdfContext, 3);
edisonn@google.combd2f3012013-08-22 14:18:04 +00001991 POP_NUMBER(pdfContext, b);
1992 POP_NUMBER(pdfContext, g);
1993 POP_NUMBER(pdfContext, r);
1994 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001995
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001996 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001997 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
edisonn@google.com3aa35552013-08-14 18:26:20 +00001998 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001999}
2000
edisonn@google.com3aa35552013-08-14 18:26:20 +00002001static SkPdfResult PdfOp_RG(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002002 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2003}
2004
edisonn@google.com3aa35552013-08-14 18:26:20 +00002005static SkPdfResult PdfOp_rg(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002006 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2007}
2008
edisonn@google.come50d9a12013-10-10 20:58:22 +00002009static SkPdfResult PdfOp_K_k(SkPdfContext* pdfContext, SkCanvas* canvas,
2010 SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002011 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.comaf54a512013-09-13 19:33:42 +00002012 EXPECT_OPERANDS("K/k", pdfContext, 4);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002013 POP_NUMBER(pdfContext, k);
2014 POP_NUMBER(pdfContext, y);
2015 POP_NUMBER(pdfContext, m);
2016 POP_NUMBER(pdfContext, c);
2017 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002018
edisonn@google.come50d9a12013-10-10 20:58:22 +00002019 // TODO(edisonn): really silly quick way to remove compiler warning
edisonn@google.combd2f3012013-08-22 14:18:04 +00002020 if (k + y + m + c == 0) {
2021 return kNYI_SkPdfResult;
2022 }
2023
2024 //colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002025 // TODO(edisonn): Set color.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002026 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002027}
2028
edisonn@google.com3aa35552013-08-14 18:26:20 +00002029static SkPdfResult PdfOp_K(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002030 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
2031}
2032
edisonn@google.com3aa35552013-08-14 18:26:20 +00002033static SkPdfResult PdfOp_k(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002034 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
2035}
2036
edisonn@google.com3aa35552013-08-14 18:26:20 +00002037static SkPdfResult PdfOp_W(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002038 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
2039 pdfContext->fGraphicsState.fHasClipPathToApply = true;
2040
edisonn@google.com3aa35552013-08-14 18:26:20 +00002041 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002042}
2043
edisonn@google.come50d9a12013-10-10 20:58:22 +00002044static SkPdfResult PdfOp_W_star(SkPdfContext* pdfContext, SkCanvas* canvas,
2045 PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002046 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
2047
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002048 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
2049 pdfContext->fGraphicsState.fHasClipPathToApply = true;
2050
edisonn@google.com3aa35552013-08-14 18:26:20 +00002051 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002052}
2053
edisonn@google.com3aa35552013-08-14 18:26:20 +00002054static SkPdfResult PdfOp_BX(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002055 *looper = new PdfCompatibilitySectionLooper();
edisonn@google.com3aa35552013-08-14 18:26:20 +00002056 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002057}
2058
edisonn@google.com3aa35552013-08-14 18:26:20 +00002059static SkPdfResult PdfOp_EX(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002060 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue,
2061 "EX operator should not be called, it is handled in a looper, "
2062 "unless the file is corrupted, we should assert",
2063 NULL, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002064
edisonn@google.com3aa35552013-08-14 18:26:20 +00002065 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002066}
2067
edisonn@google.com3aa35552013-08-14 18:26:20 +00002068static SkPdfResult PdfOp_BI(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002069 *looper = new PdfInlineImageLooper();
edisonn@google.com3aa35552013-08-14 18:26:20 +00002070 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002071}
2072
edisonn@google.com3aa35552013-08-14 18:26:20 +00002073static SkPdfResult PdfOp_ID(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002074 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue,
2075 "ID operator should not be called, it is habdled in a looper, "
2076 "unless the file is corrupted, we should assert",
2077 NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002078 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002079}
2080
edisonn@google.com3aa35552013-08-14 18:26:20 +00002081static SkPdfResult PdfOp_EI(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002082 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue,
2083 "EI operator should not be called, it is habdled in a looper, "
2084 "unless the file is corrupted, we should assert",
2085 NULL, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002086 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002087}
2088
edisonn@google.com33f11b62013-08-14 21:35:27 +00002089static SkPdfResult skpdfGraphicsStateApply_ca(SkPdfContext* pdfContext, double ca) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002090 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002091 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002092}
2093
edisonn@google.com33f11b62013-08-14 21:35:27 +00002094static SkPdfResult skpdfGraphicsStateApply_CA(SkPdfContext* pdfContext, double CA) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002095 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002096 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002097}
2098
edisonn@google.com33f11b62013-08-14 21:35:27 +00002099static SkPdfResult skpdfGraphicsStateApplyLW(SkPdfContext* pdfContext, double lineWidth) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002100 pdfContext->fGraphicsState.fLineWidth = lineWidth;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002101 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002102}
2103
edisonn@google.com33f11b62013-08-14 21:35:27 +00002104static SkPdfResult skpdfGraphicsStateApplyLC(SkPdfContext* pdfContext, int64_t lineCap) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002105 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002106 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002107}
2108
edisonn@google.com33f11b62013-08-14 21:35:27 +00002109static SkPdfResult skpdfGraphicsStateApplyLJ(SkPdfContext* pdfContext, int64_t lineJoin) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002110 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002111 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002112}
2113
edisonn@google.com33f11b62013-08-14 21:35:27 +00002114static SkPdfResult skpdfGraphicsStateApplyML(SkPdfContext* pdfContext, double miterLimit) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002115 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
edisonn@google.com3aa35552013-08-14 18:26:20 +00002116 return kOK_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002117}
2118
edisonn@google.come50d9a12013-10-10 20:58:22 +00002119// TODO(edisonn): test all dashing rules, not sure if they work as in skia.
edisonn@google.com24cdf132013-07-30 16:06:12 +00002120/*
21211) [ ] 0 No dash; solid, unbroken lines
21222) [3] 0 3 units on, 3 units off, …
21233) [2] 1 1 on, 2 off, 2 on, 2 off, …
21244) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
21255) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
21266) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
2127 */
2128
edisonn@google.come50d9a12013-10-10 20:58:22 +00002129static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* intervals,
2130 SkPdfNativeObject* phase) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002131 if (intervals == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002132 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, intervals,
2133 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002134 return kIgnoreError_SkPdfResult;
2135 }
2136
2137 if (phase == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002138 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, phase,
2139 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002140 return kIgnoreError_SkPdfResult;
2141 }
2142
edisonn@google.com24cdf132013-07-30 16:06:12 +00002143 int cnt = intervals->size();
2144 if (cnt >= 256) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00002145 // TODO(edisonn): alloc memory
edisonn@google.come50d9a12013-10-10 20:58:22 +00002146 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
2147 "dash array size unssuported, cnt > 256", intervals, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002148 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002149 }
2150 for (int i = 0; i < cnt; i++) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002151 if (!intervals->objAtAIndex(i) || !intervals->objAtAIndex(i)->isNumber()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002152 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2153 intervals->objAtAIndex(i),
2154 SkPdfNativeObject::_kNumber_PdfObjectType, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002155 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002156 }
2157 }
2158
edisonn@google.com24cdf132013-07-30 16:06:12 +00002159 double total = 0;
2160 for (int i = 0 ; i < cnt; i++) {
2161 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
2162 total += pdfContext->fGraphicsState.fDashArray[i];
2163 }
edisonn@google.comf111a4b2013-07-31 18:22:36 +00002164 if (cnt & 1) {
2165 if (cnt == 1) {
2166 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
2167 cnt++;
2168 } else {
2169 // TODO(edisonn): report error/warning
edisonn@google.com3aa35552013-08-14 18:26:20 +00002170 return kNYI_SkPdfResult;
edisonn@google.comf111a4b2013-07-31 18:22:36 +00002171 }
2172 }
2173 pdfContext->fGraphicsState.fDashArrayLength = cnt;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002174 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
2175 if (pdfContext->fGraphicsState.fDashPhase == 0) {
2176 // other rules, changes?
edisonn@google.com063d7072013-08-16 15:05:08 +00002177 pdfContext->fGraphicsState.fDashPhase = SkDoubleToScalar(total);
edisonn@google.com24cdf132013-07-30 16:06:12 +00002178 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002179
edisonn@google.com3aa35552013-08-14 18:26:20 +00002180 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002181}
2182
edisonn@google.com33f11b62013-08-14 21:35:27 +00002183static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* dash) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002184 if (!dash || dash->isArray()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002185 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash,
2186 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002187 return kIgnoreError_SkPdfResult;
edisonn@google.com24cdf132013-07-30 16:06:12 +00002188 }
edisonn@google.comaf54a512013-09-13 19:33:42 +00002189
2190 if (dash->size() != 2) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002191 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
2192 "hash array must have 2 elements", dash, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002193 return kIgnoreError_SkPdfResult;
2194 }
2195
2196 if (!dash->objAtAIndex(0) || !dash->objAtAIndex(0)->isArray()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002197 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash->objAtAIndex(0),
2198 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002199 return kIgnoreError_SkPdfResult;
2200 }
2201
2202 if (!dash->objAtAIndex(1) || !dash->objAtAIndex(1)->isNumber()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002203 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash->objAtAIndex(1),
2204 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002205 return kIgnoreError_SkPdfResult;
2206 }
2207
edisonn@google.come50d9a12013-10-10 20:58:22 +00002208 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0),
2209 dash->objAtAIndex(1));
edisonn@google.com24cdf132013-07-30 16:06:12 +00002210}
2211
edisonn@google.com33f11b62013-08-14 21:35:27 +00002212static void skpdfGraphicsStateApplyFont(SkPdfContext* pdfContext, SkPdfArray* fontAndSize) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002213 if (!fontAndSize || !fontAndSize->isArray()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002214 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, fontAndSize,
2215 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.com24cdf132013-07-30 16:06:12 +00002216 return;
2217 }
edisonn@google.comaf54a512013-09-13 19:33:42 +00002218
2219 if (fontAndSize->size() != 2) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002220 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
2221 "font array must have 2 elements", fontAndSize, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002222 return;
2223 }
2224
2225 if (!fontAndSize->objAtAIndex(0) || !fontAndSize->objAtAIndex(0)->isName()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002226 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2227 fontAndSize->objAtAIndex(0),
2228 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002229 return;
2230 }
2231
2232
2233 if (!fontAndSize->objAtAIndex(1) || !fontAndSize->objAtAIndex(1)->isNumber()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002234 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2235 fontAndSize->objAtAIndex(0),
2236 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002237 return;
2238 }
2239
edisonn@google.come50d9a12013-10-10 20:58:22 +00002240 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0),
2241 fontAndSize->objAtAIndex(1)->numberValue());
edisonn@google.com24cdf132013-07-30 16:06:12 +00002242}
2243
2244
2245//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002246static SkPdfResult PdfOp_w(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002247 EXPECT_OPERANDS("w", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002248 POP_NUMBER(pdfContext, lw);
2249 CHECK_PARAMETERS();
2250
edisonn@google.com24cdf132013-07-30 16:06:12 +00002251 return skpdfGraphicsStateApplyLW(pdfContext, lw);
2252}
2253
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002254//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 +00002255static SkPdfResult PdfOp_J(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com063d7072013-08-16 15:05:08 +00002256 // TODO(edisonn): round/ceil to int?
edisonn@google.comaf54a512013-09-13 19:33:42 +00002257 EXPECT_OPERANDS("J", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002258 POP_NUMBER(pdfContext, lc);
2259 CHECK_PARAMETERS();
2260
2261 return skpdfGraphicsStateApplyLC(pdfContext, (int)lc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002262}
2263
2264//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 +00002265static SkPdfResult PdfOp_j(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com063d7072013-08-16 15:05:08 +00002266 // TODO(edisonn): round/ceil to int?
edisonn@google.comaf54a512013-09-13 19:33:42 +00002267 EXPECT_OPERANDS("j", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002268 POP_NUMBER(pdfContext, lj);
2269 CHECK_PARAMETERS();
2270
2271 return skpdfGraphicsStateApplyLJ(pdfContext, (int)lj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002272}
2273
2274//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002275static SkPdfResult PdfOp_M(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002276 EXPECT_OPERANDS("M", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002277 POP_NUMBER(pdfContext, ml);
2278 CHECK_PARAMETERS();
edisonn@google.com24cdf132013-07-30 16:06:12 +00002279 return skpdfGraphicsStateApplyML(pdfContext, ml);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002280}
2281
2282//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
2283//page 155).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002284static SkPdfResult PdfOp_d(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002285 EXPECT_OPERANDS("d", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002286 POP_OBJ(pdfContext, phase);
2287 POP_ARRAY(pdfContext, array);
2288 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002289
edisonn@google.combd2f3012013-08-22 14:18:04 +00002290 return skpdfGraphicsStateApplyD(pdfContext, array, phase);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002291}
2292
edisonn@google.come50d9a12013-10-10 20:58:22 +00002293//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents”
2294// on page 197).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002295static SkPdfResult PdfOp_ri(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002296 pdfContext->fObjectStack.pop();
2297
edisonn@google.come50d9a12013-10-10 20:58:22 +00002298 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "render intent NYI", NULL,
2299 pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002300
edisonn@google.com3aa35552013-08-14 18:26:20 +00002301 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002302}
2303
2304//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
2305//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
2306//fies the output device’s default flatness tolerance.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002307static SkPdfResult PdfOp_i(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002308 EXPECT_OPERANDS("i", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002309 POP_NUMBER(pdfContext, flatness);
2310 CHECK_PARAMETERS();
2311
2312 if (flatness < 0 || flatness > 100) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002313 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2314 "flatness must be a real in [0, 100] range", flatness_obj, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002315 return kIgnoreError_SkPdfResult;
edisonn@google.combd2f3012013-08-22 14:18:04 +00002316 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002317
edisonn@google.com3aa35552013-08-14 18:26:20 +00002318 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002319}
2320
edisonn@google.come878e722013-07-29 19:10:58 +00002321SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
2322
2323class InitBlendModes {
2324public:
2325 InitBlendModes() {
2326 // TODO(edisonn): use the python code generator?
2327 // TABLE 7.2 Standard separable blend modes
2328 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
2329 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
2330 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
2331 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
2332 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
2333 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
2334 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
2335 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
2336 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
2337 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
2338 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
2339 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
2340
2341 // TABLE 7.3 Standard nonseparable blend modes
2342 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
2343 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
2344 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
2345 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
2346 }
2347};
2348
2349InitBlendModes _gDummyInniter;
2350
edisonn@google.com33f11b62013-08-14 21:35:27 +00002351static SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
edisonn@google.come878e722013-07-29 19:10:58 +00002352 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2353 if (gPdfBlendModes.find(blendMode, len, &mode)) {
2354 return mode;
2355 }
2356
2357 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2358}
2359
edisonn@google.com063d7072013-08-16 15:05:08 +00002360static void skpdfGraphicsStateApplyBM_name(SkPdfContext* pdfContext, const SkString& blendMode) {
2361 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.size());
edisonn@google.come878e722013-07-29 19:10:58 +00002362 if (mode <= SkXfermode::kLastMode) {
2363 pdfContext->fGraphicsState.fBlendModesLength = 1;
2364 pdfContext->fGraphicsState.fBlendModes[0] = mode;
2365 } else {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002366 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue,
2367 blendMode.c_str(), NULL, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002368 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002369}
2370
edisonn@google.com33f11b62013-08-14 21:35:27 +00002371static void skpdfGraphicsStateApplyBM_array(SkPdfContext* pdfContext, SkPdfArray* blendModes) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002372 if (!blendModes || !blendModes->isArray()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002373 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, blendModes,
2374 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002375 return;
2376 }
edisonn@google.comaf54a512013-09-13 19:33:42 +00002377
2378 if (blendModes->size() == 0 || blendModes->size() > 256) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002379 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
2380 "length of blendmodes, 0, is an erro, 256+, is NYI", blendModes, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002381 return;
2382 }
2383
edisonn@google.come878e722013-07-29 19:10:58 +00002384 SkXfermode::Mode modes[256];
2385 int cnt = blendModes->size();
2386 for (int i = 0; i < cnt; i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00002387 SkPdfNativeObject* name = blendModes->objAtAIndex(i);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002388 if (!name || !name->isName()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002389 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name,
2390 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002391 return;
2392 }
2393 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
2394 if (mode > SkXfermode::kLastMode) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002395 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue, NULL, name,
2396 pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002397 return;
2398 }
2399 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002400
edisonn@google.come878e722013-07-29 19:10:58 +00002401 pdfContext->fGraphicsState.fBlendModesLength = cnt;
2402 for (int i = 0; i < cnt; i++) {
2403 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
2404 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002405}
2406
edisonn@google.com33f11b62013-08-14 21:35:27 +00002407static void skpdfGraphicsStateApplySMask_dict(SkPdfContext* pdfContext, SkPdfDictionary* sMask) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002408 if (!sMask || !sMask->isName()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002409 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, sMask,
2410 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002411 return;
2412 }
2413
edisonn@google.come878e722013-07-29 19:10:58 +00002414 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002415 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
edisonn@google.come878e722013-07-29 19:10:58 +00002416 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
2417 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
2418 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
2419 } else {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002420 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
2421 "Dictionary must be SoftMask, or SoftMaskImage",
2422 sMask, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002423 }
2424}
2425
edisonn@google.com063d7072013-08-16 15:05:08 +00002426static void skpdfGraphicsStateApplySMask_name(SkPdfContext* pdfContext, const SkString& sMask) {
2427 if (sMask.equals("None")) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002428 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002429 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002430 return;
2431 }
2432
edisonn@google.come50d9a12013-10-10 20:58:22 +00002433 SkPdfDictionary* extGStateDictionary
2434 = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.come878e722013-07-29 19:10:58 +00002435
2436 if (extGStateDictionary == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002437 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL,
2438 pdfContext->fGraphicsState.fResources, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002439 return;
2440 }
2441
edisonn@google.come50d9a12013-10-10 20:58:22 +00002442 SkPdfNativeObject* obj
2443 = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
edisonn@google.come878e722013-07-29 19:10:58 +00002444 if (!obj || !obj->isDictionary()) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002445 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, obj,
2446 SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
edisonn@google.come878e722013-07-29 19:10:58 +00002447 return;
2448 }
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002449
2450 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002451 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002452
edisonn@google.come878e722013-07-29 19:10:58 +00002453 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002454}
2455
edisonn@google.com33f11b62013-08-14 21:35:27 +00002456static void skpdfGraphicsStateApplyAIS(SkPdfContext* pdfContext, bool alphaSource) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002457 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2458}
2459
2460
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002461//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
edisonn@google.come50d9a12013-10-10 20:58:22 +00002462//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current
2463//resource dictionary (see the next section).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002464static SkPdfResult PdfOp_gs(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002465 EXPECT_OPERANDS("gs", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002466 POP_NAME(pdfContext, name);
2467 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002468
edisonn@google.come50d9a12013-10-10 20:58:22 +00002469 SkPdfDictionary* extGStateDictionary
2470 = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002471
2472 if (extGStateDictionary == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002473 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL,
2474 pdfContext->fGraphicsState.fResources, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002475 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002476 }
2477
edisonn@google.come50d9a12013-10-10 20:58:22 +00002478 SkPdfNativeObject* value
2479 = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002480
edisonn@google.come50d9a12013-10-10 20:58:22 +00002481 if (kNone_SkPdfNativeObjectType ==
2482 pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
edisonn@google.com3aa35552013-08-14 18:26:20 +00002483 return kIgnoreError_SkPdfResult;
edisonn@google.com571c70b2013-07-10 17:09:50 +00002484 }
2485 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002486
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002487 if (gs == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002488 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2489 gs, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002490 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002491 }
2492
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002493 if (gs->has_LW()) {
2494 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2495 }
2496
2497 if (gs->has_LC()) {
2498 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2499 }
2500
2501 if (gs->has_LJ()) {
2502 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2503 }
2504
2505 if (gs->has_ML()) {
2506 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2507 }
2508
2509 if (gs->has_D()) {
2510 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2511 }
2512
2513 if (gs->has_Font()) {
2514 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2515 }
2516
2517 if (gs->has_BM()) {
2518 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2519 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2520 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2521 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2522 } else {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002523 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", gs->get("BM"),
2524 SkPdfNativeObject::kArray_PdfObjectType ||
2525 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002526 }
2527 }
2528
2529 if (gs->has_SMask()) {
2530 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2531 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2532 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002533 skpdfGraphicsStateApplySMask_dict(pdfContext,
2534 gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002535 } else {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002536 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
2537 "wrong type",
2538 gs->get("BM"),
2539 SkPdfNativeObject::kDictionary_PdfObjectType ||
2540 SkPdfNativeObject::kName_PdfObjectType,
2541 pdfContext);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002542 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002543 }
2544
2545 if (gs->has_ca()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002546 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002547 }
2548
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002549 if (gs->has_CA()) {
2550 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2551 }
2552
2553 if (gs->has_AIS()) {
2554 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002555 }
2556
edisonn@google.come50d9a12013-10-10 20:58:22 +00002557 // TODO(edisonn): make sure we loaded all those properties in graphic state.
2558
edisonn@google.com3aa35552013-08-14 18:26:20 +00002559 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002560}
2561
2562//charSpace Tc Set the character spacing, Tc
edisonn@google.come50d9a12013-10-10 20:58:22 +00002563//, to charSpace, which is a number expressed in unscaled text space units.
2564// Character spacing is used by the Tj, TJ, and ' operators.
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002565//Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002566SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002567 EXPECT_OPERANDS("Tc", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002568 POP_NUMBER(pdfContext, charSpace);
2569 CHECK_PARAMETERS();
2570
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002571 pdfContext->fGraphicsState.fCharSpace = charSpace;
2572
edisonn@google.com3aa35552013-08-14 18:26:20 +00002573 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002574}
2575
2576//wordSpace Tw Set the word spacing, T
2577//w
2578//, to wordSpace, which is a number expressed in unscaled
2579//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2580//value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002581SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002582 EXPECT_OPERANDS("Tw", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002583 POP_NUMBER(pdfContext, wordSpace);
2584 CHECK_PARAMETERS();
2585
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002586 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2587
edisonn@google.com3aa35552013-08-14 18:26:20 +00002588 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002589}
2590
2591//scale Tz Set the horizontal scaling, Th
2592//, to (scale ˜ 100). scale is a number specifying the
2593//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002594static SkPdfResult PdfOp_Tz(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002595 EXPECT_OPERANDS("Tz", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002596 POP_NUMBER(pdfContext, scale);
2597 CHECK_PARAMETERS();
2598
2599 if (scale < 0) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002600 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2601 "scale must a positive real number", scale_obj, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002602 return kError_SkPdfResult;
2603 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002604
edisonn@google.com3aa35552013-08-14 18:26:20 +00002605 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002606}
2607
2608//render Tr Set the text rendering mode, T
2609//mode, to render, which is an integer. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002610static SkPdfResult PdfOp_Tr(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002611 EXPECT_OPERANDS("Tr", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002612 POP_INTEGER(pdfContext, mode);
2613 CHECK_PARAMETERS();
2614
2615 if (mode < 0) { // TODO(edisonn): function/enums with supported modes
edisonn@google.come50d9a12013-10-10 20:58:22 +00002616 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2617 "mode must a positive integer or 0", mode_obj, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002618 return kError_SkPdfResult;
2619 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002620
edisonn@google.com3aa35552013-08-14 18:26:20 +00002621 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002622}
2623//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2624//units. Initial value: 0.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002625static SkPdfResult PdfOp_Ts(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002626 EXPECT_OPERANDS("Ts", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002627 POP_NUMBER(pdfContext, rise);
2628 CHECK_PARAMETERS();
2629
2630 if (rise < 0) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002631 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2632 "rise must a positive real number", rise_obj, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002633 return kNYI_SkPdfResult;
2634 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002635
edisonn@google.com3aa35552013-08-14 18:26:20 +00002636 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002637}
2638
2639//wx wy d0
edisonn@google.com3aa35552013-08-14 18:26:20 +00002640static SkPdfResult PdfOp_d0(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002641 EXPECT_OPERANDS("d0", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002642 POP_NUMBER(pdfContext, wy);
2643 POP_NUMBER(pdfContext, wx);
2644 CHECK_PARAMETERS();
2645
edisonn@google.comaf54a512013-09-13 19:33:42 +00002646 if (wx < 0) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002647 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2648 "wx must a positive real number", wx_obj, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002649 return kError_SkPdfResult;
2650 }
2651
2652 if (wy < 0) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002653 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2654 "wy must a positive real number", wy_obj, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002655 return kError_SkPdfResult;
2656 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002657
edisonn@google.com3aa35552013-08-14 18:26:20 +00002658 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002659}
2660
2661//wx wy llx lly urx ury d1
edisonn@google.com3aa35552013-08-14 18:26:20 +00002662static SkPdfResult PdfOp_d1(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002663 EXPECT_OPERANDS("d1", pdfContext, 6);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002664 POP_NUMBER(pdfContext, ury);
2665 POP_NUMBER(pdfContext, urx);
2666 POP_NUMBER(pdfContext, lly);
2667 POP_NUMBER(pdfContext, llx);
2668 POP_NUMBER(pdfContext, wy);
2669 POP_NUMBER(pdfContext, wx);
2670 CHECK_PARAMETERS();
2671
edisonn@google.come50d9a12013-10-10 20:58:22 +00002672 // TODO(edisonn): really silly quick way to remove warning
edisonn@google.combd2f3012013-08-22 14:18:04 +00002673 if (wx + wy + llx + lly + urx + ury) {
2674 return kNYI_SkPdfResult;
2675 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002676
edisonn@google.com3aa35552013-08-14 18:26:20 +00002677 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002678}
2679
2680//name sh
edisonn@google.com3aa35552013-08-14 18:26:20 +00002681static SkPdfResult PdfOp_sh(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002682 EXPECT_OPERANDS("sh", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002683 POP_NAME(pdfContext, name);
2684 CHECK_PARAMETERS();
2685
2686 if (name == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002687 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name,
2688 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002689 return kError_SkPdfResult;
2690 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002691
edisonn@google.com3aa35552013-08-14 18:26:20 +00002692 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002693}
2694
2695//name Do
edisonn@google.com3aa35552013-08-14 18:26:20 +00002696static SkPdfResult PdfOp_Do(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002697 EXPECT_OPERANDS("Do", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002698 POP_NAME(pdfContext, name);
2699 CHECK_PARAMETERS();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002700
edisonn@google.com571c70b2013-07-10 17:09:50 +00002701 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002702
2703 if (xObject == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002704 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingXObject_SkPdfIssue, NULL,
2705 pdfContext->fGraphicsState.fResources, pdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002706 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002707 }
2708
edisonn@google.com3aa35552013-08-14 18:26:20 +00002709 SkPdfNativeObject* value = xObject->get(name);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002710 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002711
edisonn@google.com571c70b2013-07-10 17:09:50 +00002712 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002713}
2714
2715//tag MP Designate a marked-content point. tag is a name object indicating the role or
2716//significance of the point.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002717static SkPdfResult PdfOp_MP(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002718 EXPECT_OPERANDS("MP", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002719 POP_OBJ(pdfContext, tag);
2720 CHECK_PARAMETERS();
2721
2722 if (tag == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002723 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2724 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002725 return kNYI_SkPdfResult;
2726 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002727
edisonn@google.comaf54a512013-09-13 19:33:42 +00002728 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MP NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002729 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002730}
2731
2732//tag properties DP Designate a marked-content point with an associated property list. tag is a
2733//name object indicating the role or significance of the point; properties is
2734//either an inline dictionary containing the property list or a name object
2735//associated with it in the Properties subdictionary of the current resource
2736//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002737static SkPdfResult PdfOp_DP(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002738 EXPECT_OPERANDS("DP", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002739 POP_OBJ(pdfContext, properties);
2740 POP_OBJ(pdfContext, tag);
2741 CHECK_PARAMETERS();
2742
edisonn@google.comaf54a512013-09-13 19:33:42 +00002743 if (tag == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002744 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2745 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002746 return kNYI_SkPdfResult;
2747 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002748
edisonn@google.comaf54a512013-09-13 19:33:42 +00002749 if (properties == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002750 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties,
2751 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002752 return kNYI_SkPdfResult;
2753 }
2754
2755 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "DP NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002756 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002757}
2758
2759//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2760//tag is a name object indicating the role or significance of the sequence.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002761static SkPdfResult PdfOp_BMC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002762 EXPECT_OPERANDS("BMC", pdfContext, 1);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002763 POP_OBJ(pdfContext, tag);
2764 CHECK_PARAMETERS();
2765
2766 if (tag == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002767 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2768 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002769 return kNYI_SkPdfResult;
2770 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002771
edisonn@google.comaf54a512013-09-13 19:33:42 +00002772 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BMC NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002773 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002774}
2775
2776//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
edisonn@google.come50d9a12013-10-10 20:58:22 +00002777//by a balancing EMCoperator. tag is a name object indicating the role or significance of the
2778// sequence; propertiesis either an inline dictionary containing the
2779//property list or a name object associated with it in the Properties subdictionary of the current
2780//resource dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.com3aa35552013-08-14 18:26:20 +00002781static SkPdfResult PdfOp_BDC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002782 EXPECT_OPERANDS("BDC", pdfContext, 2);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002783 POP_OBJ(pdfContext, properties);
2784 POP_OBJ(pdfContext, tag);
2785 CHECK_PARAMETERS();
2786
edisonn@google.comaf54a512013-09-13 19:33:42 +00002787 if (tag == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002788 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2789 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.combd2f3012013-08-22 14:18:04 +00002790 return kNYI_SkPdfResult;
2791 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002792
edisonn@google.comaf54a512013-09-13 19:33:42 +00002793 if (properties == NULL) {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002794 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties,
2795 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
edisonn@google.comaf54a512013-09-13 19:33:42 +00002796 return kNYI_SkPdfResult;
2797 }
2798
2799 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BDC NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002800 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002801}
2802
2803//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.com3aa35552013-08-14 18:26:20 +00002804static SkPdfResult PdfOp_EMC(SkPdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.comaf54a512013-09-13 19:33:42 +00002805 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "EMC NYI", NULL, NULL);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002806 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002807}
2808
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002809static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002810 static bool gInitialized = false;
2811 if (gInitialized) {
2812 return;
2813 }
2814
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002815 gPdfOps.set("q", PdfOp_q);
2816 gPdfOps.set("Q", PdfOp_Q);
2817 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002818
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002819 gPdfOps.set("TD", PdfOp_TD);
2820 gPdfOps.set("Td", PdfOp_Td);
2821 gPdfOps.set("Tm", PdfOp_Tm);
2822 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002823
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002824 gPdfOps.set("m", PdfOp_m);
2825 gPdfOps.set("l", PdfOp_l);
2826 gPdfOps.set("c", PdfOp_c);
2827 gPdfOps.set("v", PdfOp_v);
2828 gPdfOps.set("y", PdfOp_y);
2829 gPdfOps.set("h", PdfOp_h);
2830 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002831
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002832 gPdfOps.set("S", PdfOp_S);
2833 gPdfOps.set("s", PdfOp_s);
2834 gPdfOps.set("f", PdfOp_f);
2835 gPdfOps.set("F", PdfOp_F);
2836 gPdfOps.set("f*", PdfOp_f_star);
2837 gPdfOps.set("B", PdfOp_B);
2838 gPdfOps.set("B*", PdfOp_B_star);
2839 gPdfOps.set("b", PdfOp_b);
2840 gPdfOps.set("b*", PdfOp_b_star);
2841 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002842
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002843 gPdfOps.set("BT", PdfOp_BT);
2844 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002845
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002846 gPdfOps.set("Tj", PdfOp_Tj);
2847 gPdfOps.set("'", PdfOp_quote);
2848 gPdfOps.set("\"", PdfOp_doublequote);
2849 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002850
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002851 gPdfOps.set("CS", PdfOp_CS);
2852 gPdfOps.set("cs", PdfOp_cs);
2853 gPdfOps.set("SC", PdfOp_SC);
2854 gPdfOps.set("SCN", PdfOp_SCN);
2855 gPdfOps.set("sc", PdfOp_sc);
2856 gPdfOps.set("scn", PdfOp_scn);
2857 gPdfOps.set("G", PdfOp_G);
2858 gPdfOps.set("g", PdfOp_g);
2859 gPdfOps.set("RG", PdfOp_RG);
2860 gPdfOps.set("rg", PdfOp_rg);
2861 gPdfOps.set("K", PdfOp_K);
2862 gPdfOps.set("k", PdfOp_k);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002863
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002864 gPdfOps.set("W", PdfOp_W);
2865 gPdfOps.set("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002866
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002867 gPdfOps.set("BX", PdfOp_BX);
2868 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002869
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002870 gPdfOps.set("BI", PdfOp_BI);
2871 gPdfOps.set("ID", PdfOp_ID);
2872 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002873
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002874 gPdfOps.set("w", PdfOp_w);
2875 gPdfOps.set("J", PdfOp_J);
2876 gPdfOps.set("j", PdfOp_j);
2877 gPdfOps.set("M", PdfOp_M);
2878 gPdfOps.set("d", PdfOp_d);
2879 gPdfOps.set("ri", PdfOp_ri);
2880 gPdfOps.set("i", PdfOp_i);
2881 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002882
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002883 gPdfOps.set("Tc", PdfOp_Tc);
2884 gPdfOps.set("Tw", PdfOp_Tw);
2885 gPdfOps.set("Tz", PdfOp_Tz);
2886 gPdfOps.set("TL", PdfOp_TL);
2887 gPdfOps.set("Tf", PdfOp_Tf);
2888 gPdfOps.set("Tr", PdfOp_Tr);
2889 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002890
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002891 gPdfOps.set("d0", PdfOp_d0);
2892 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002893
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002894 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002895
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002896 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002897
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002898 gPdfOps.set("MP", PdfOp_MP);
2899 gPdfOps.set("DP", PdfOp_DP);
2900 gPdfOps.set("BMC", PdfOp_BMC);
2901 gPdfOps.set("BDC", PdfOp_BDC);
2902 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002903
2904 gInitialized = true;
2905}
2906
2907class InitPdfOps {
2908public:
2909 InitPdfOps() {
2910 initPdfOperatorRenderes();
2911 }
2912};
2913
2914InitPdfOps gInitPdfOps;
2915
2916void reportPdfRenderStats() {
edisonn@google.com3aa35552013-08-14 18:26:20 +00002917 for (int i = 0 ; i < kCount_SkPdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002918 SkTDict<int>::Iter iter(gRenderStats[i]);
2919 const char* key;
2920 int value = 0;
2921 while ((key = iter.next(&value)) != NULL) {
2922 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002923 }
2924 }
2925}
2926
edisonn@google.com3aa35552013-08-14 18:26:20 +00002927SkPdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002928 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002929 {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002930 PdfOperatorRenderer pdfOperatorRenderer = NULL;
edisonn@google.come50d9a12013-10-10 20:58:22 +00002931 if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) &&
2932 pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002933 PdfTokenLooper* childLooper = NULL;
edisonn@google.come50d9a12013-10-10 20:58:22 +00002934 // Main work is done by pdfOperatorRenderer(...)
edisonn@google.com3aa35552013-08-14 18:26:20 +00002935 SkPdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002936
2937 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002938 gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
2939 gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002940
2941 if (childLooper) {
2942 childLooper->setUp(this);
2943 childLooper->loop();
2944 delete childLooper;
2945 }
2946 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002947 int cnt = 0;
edisonn@google.come50d9a12013-10-10 20:58:22 +00002948 gRenderStats[kUnsupported_SkPdfResult].find(token.fKeyword,
2949 token.fKeywordLength,
2950 &cnt);
2951 gRenderStats[kUnsupported_SkPdfResult].set(token.fKeyword,
2952 token.fKeywordLength,
2953 cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002954 }
2955 }
2956 else if (token.fType == kObject_TokenType)
2957 {
2958 fPdfContext->fObjectStack.push( token.fObject );
2959 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002960 else {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002961 // TODO(edisonn): store the keyword as a object, so we can track the location in file,
2962 // and report where the error was triggered
2963 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, token.fKeyword, NULL,
2964 fPdfContext);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002965 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002966 }
edisonn@google.com3aa35552013-08-14 18:26:20 +00002967 return kOK_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002968}
2969
2970void PdfMainLooper::loop() {
2971 PdfToken token;
2972 while (readToken(fTokenizer, &token)) {
2973 consumeToken(token);
2974 }
2975}
2976
edisonn@google.com3aa35552013-08-14 18:26:20 +00002977SkPdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002978 SkASSERT(false);
edisonn@google.com3aa35552013-08-14 18:26:20 +00002979 return kIgnoreError_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002980}
2981
2982void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002983 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002984}
2985
edisonn@google.com3aa35552013-08-14 18:26:20 +00002986SkPdfResult PdfInlineImageLooper::done() {
2987 return kNYI_SkPdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002988}
2989
edisonn@google.com3aa35552013-08-14 18:26:20 +00002990SkPdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002991 return fParent->consumeToken(token);
2992}
2993
2994void PdfCompatibilitySectionLooper::loop() {
edisonn@google.come50d9a12013-10-10 20:58:22 +00002995 PdfOp_q(fPdfContext, fCanvas, NULL);
2996
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002997 PdfToken token;
2998 while (readToken(fTokenizer, &token)) {
2999 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
3000 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
3001 looper->setUp(this);
3002 looper->loop();
3003 delete looper;
3004 } else {
3005 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
3006 fParent->consumeToken(token);
3007 }
3008 }
edisonn@google.come50d9a12013-10-10 20:58:22 +00003009
3010 PdfOp_Q(fPdfContext, fCanvas, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00003011}
3012
edisonn@google.come50d9a12013-10-10 20:58:22 +00003013// TODO(edisonn): for debugging - remove or put it in a #ifdef
edisonn@google.com3aa35552013-08-14 18:26:20 +00003014SkPdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00003015
edisonn@google.com444e25a2013-07-11 15:20:50 +00003016bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00003017 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00003018 return false;
3019 }
3020
edisonn@google.com222382b2013-07-10 22:33:10 +00003021 if (page < 0 || page >= pages()) {
3022 return false;
3023 }
3024
edisonn@google.com3aa35552013-08-14 18:26:20 +00003025 SkPdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00003026
edisonn@google.com222382b2013-07-10 22:33:10 +00003027 pdfContext.fOriginalMatrix = SkMatrix::I();
3028 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
3029
3030 gPdfContext = &pdfContext;
3031
edisonn@google.com222382b2013-07-10 22:33:10 +00003032 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00003033 SkScalar w = dst.width();
3034 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00003035
edisonn@google.comf68aed32013-08-22 15:37:21 +00003036 if (SkScalarTruncToInt(w) <= 0 || SkScalarTruncToInt(h) <= 0) {
3037 return true;
3038 }
3039
edisonn@google.com444e25a2013-07-11 15:20:50 +00003040 SkScalar wp = fPdfDoc->MediaBox(page).width();
3041 SkScalar hp = fPdfDoc->MediaBox(page).height();
3042
edisonn@google.come50d9a12013-10-10 20:58:22 +00003043 SkPoint pdfSpace[4] = {SkPoint::Make(z, z),
3044 SkPoint::Make(wp, z),
3045 SkPoint::Make(wp, hp),
3046 SkPoint::Make(z, hp)};
edisonn@google.com222382b2013-07-10 22:33:10 +00003047
3048#ifdef PDF_DEBUG_3X
edisonn@google.come50d9a12013-10-10 20:58:22 +00003049 // Use larger image to make sure we do not draw anything outside of page
3050 // could be used in tests.
3051 SkPoint skiaSpace[4] = {SkPoint::Make(w+z, h+h),
3052 SkPoint::Make(w+w, h+h),
3053 SkPoint::Make(w+w, h+z),
3054 SkPoint::Make(w+z, h+z)};
edisonn@google.com222382b2013-07-10 22:33:10 +00003055#else
edisonn@google.come50d9a12013-10-10 20:58:22 +00003056 SkPoint skiaSpace[4] = {SkPoint::Make(z, h),
3057 SkPoint::Make(w, h),
3058 SkPoint::Make(w, z),
3059 SkPoint::Make(z, z)};
edisonn@google.com222382b2013-07-10 22:33:10 +00003060#endif
edisonn@google.com222382b2013-07-10 22:33:10 +00003061
3062 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
3063 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
3064
edisonn@google.coma0cefa12013-07-28 18:34:14 +00003065 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
edisonn@google.com0f901902013-08-07 11:56:16 +00003066 pdfContext.fGraphicsState.fContentStreamMatrix = pdfContext.fOriginalMatrix;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00003067 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
3068 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
edisonn@google.com222382b2013-07-10 22:33:10 +00003069
edisonn@google.com222382b2013-07-10 22:33:10 +00003070#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00003071 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00003072#endif
3073
edisonn@google.com444e25a2013-07-11 15:20:50 +00003074 canvas->setMatrix(pdfContext.fOriginalMatrix);
3075
edisonn@google.com88fc03d2013-07-30 13:34:10 +00003076 doPage(&pdfContext, canvas, fPdfDoc->page(page));
3077
edisonn@google.come50d9a12013-10-10 20:58:22 +00003078 // TODO(edisonn:) erase with white before draw? Right now the caller is responsible.
edisonn@google.com222382b2013-07-10 22:33:10 +00003079// SkPaint paint;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00003080// paint.setColor(SK_ColorWHITE);
edisonn@google.com222382b2013-07-10 22:33:10 +00003081// canvas->drawRect(rect, paint);
3082
edisonn@google.com222382b2013-07-10 22:33:10 +00003083
3084 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00003085 return true;
3086}
edisonn@google.com222382b2013-07-10 22:33:10 +00003087
3088bool SkPdfRenderer::load(const SkString inputFileName) {
3089 unload();
3090
edisonn@google.com3aa35552013-08-14 18:26:20 +00003091 fPdfDoc = new SkPdfNativeDoc(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00003092 if (fPdfDoc->pages() == 0) {
3093 delete fPdfDoc;
3094 fPdfDoc = NULL;
3095 }
edisonn@google.com222382b2013-07-10 22:33:10 +00003096
3097 return fPdfDoc != NULL;
3098}
3099
edisonn@google.com147adb12013-07-24 15:56:19 +00003100bool SkPdfRenderer::load(SkStream* stream) {
3101 unload();
3102
3103 // TODO(edisonn): create static function that could return NULL if there are errors
edisonn@google.com3aa35552013-08-14 18:26:20 +00003104 fPdfDoc = new SkPdfNativeDoc(stream);
edisonn@google.com147adb12013-07-24 15:56:19 +00003105 if (fPdfDoc->pages() == 0) {
3106 delete fPdfDoc;
3107 fPdfDoc = NULL;
3108 }
3109
3110 return fPdfDoc != NULL;
3111}
3112
3113
edisonn@google.com222382b2013-07-10 22:33:10 +00003114int SkPdfRenderer::pages() const {
3115 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
3116}
3117
3118void SkPdfRenderer::unload() {
3119 delete fPdfDoc;
3120 fPdfDoc = NULL;
3121}
3122
3123SkRect SkPdfRenderer::MediaBox(int page) const {
3124 SkASSERT(fPdfDoc);
3125 return fPdfDoc->MediaBox(page);
3126}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00003127
edisonn@google.com7b328fd2013-07-11 12:53:06 +00003128size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00003129 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
3130}
edisonn@google.com147adb12013-07-24 15:56:19 +00003131
3132bool SkPDFNativeRenderToBitmap(SkStream* stream,
3133 SkBitmap* output,
3134 int page,
3135 SkPdfContent content,
3136 double dpi) {
3137 SkASSERT(page >= 0);
3138 SkPdfRenderer renderer;
3139 renderer.load(stream);
3140 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
3141 return false;
3142 }
3143
3144 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
3145
3146 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
3147 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
3148
3149 rect = SkRect::MakeWH(width, height);
3150
3151 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
3152
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003153 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (*output)));
edisonn@google.com147adb12013-07-24 15:56:19 +00003154 SkCanvas canvas(device);
3155
3156 return renderer.renderPage(page, &canvas, rect);
3157}