blob: 5b8edc3ee3473afd893fd0bc22dd363d397341e0 [file] [log] [blame]
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkCanvas.h"
9#include "SkDevice.h"
10#include "SkForceLinking.h"
11#include "SkGraphics.h"
12#include "SkImageDecoder.h"
13#include "SkImageEncoder.h"
14#include "SkOSFile.h"
15#include "SkPicture.h"
16#include "SkStream.h"
17#include "SkTypeface.h"
18#include "SkTArray.h"
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000019#include "SkTDict.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000020
edisonn@google.com15b11182013-07-11 14:43:15 +000021#include "SkPdfBasics.h"
22#include "SkPdfNativeTokenizer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000023#include <cstdio>
24#include <stack>
edisonn@google.com571c70b2013-07-10 17:09:50 +000025#include <set>
edisonn@google.com131d4ee2013-06-26 17:48:12 +000026
edisonn@google.com15b11182013-07-11 14:43:15 +000027extern "C" PdfContext* gPdfContext;
28extern "C" SkBitmap* gDumpBitmap;
29extern "C" SkCanvas* gDumpCanvas;
30
edisonn@google.com131d4ee2013-06-26 17:48:12 +000031__SK_FORCE_IMAGE_DECODER_LINKING;
32
33// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
34// keep for each object pos in file
35// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
36
37// TODO(edisonn): security - validate all the user input, all pdf!
38
edisonn@google.com6e49c342013-06-27 20:03:43 +000039// TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor changes in text positioning and font
40// this way, we look more at other features and layout in diffs
edisonn@google.com131d4ee2013-06-26 17:48:12 +000041
edisonn@google.com3aac1f92013-07-02 22:42:53 +000042// TODO(edisonn): move trace dump in the get functions, and mapper ones too so it ghappens automatically
43/*
44#ifdef PDF_TRACE
45 std::string str;
edisonn@google.com571c70b2013-07-10 17:09:50 +000046 pdfContext->fGraphicsState.fResources->native()->ToString(str);
edisonn@google.com3aac1f92013-07-02 22:42:53 +000047 printf("Print Tf Resources: %s\n", str.c_str());
48#endif
49 */
50
edisonn@google.com131d4ee2013-06-26 17:48:12 +000051#include "SkPdfHeaders_autogen.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +000052#include "SkPdfMapper_autogen.h"
edisonn@google.com222382b2013-07-10 22:33:10 +000053#include "SkPdfRenderer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000054
55#include "SkPdfBasics.h"
56#include "SkPdfUtils.h"
57
58#include "SkPdfFont.h"
59
edisonn@google.com131d4ee2013-06-26 17:48:12 +000060/*
61 * TODO(edisonn):
62 * - all font types and all ppdf font features
63 * - word spacing
64 * - load font for baidu.pdf
65 * - load font for youtube.pdf
66 * - parser for pdf from the definition already available in pdfspec_autogen.py
67 * - all docs from ~/work
edisonn@google.com571c70b2013-07-10 17:09:50 +000068 * - encapsulate native in the pdf api so the skpdf does not know anything about native ... in progress
edisonn@google.com131d4ee2013-06-26 17:48:12 +000069 * - load gs/ especially smask and already known prop (skp) ... in progress
70 * - wrapper on classes for customizations? e.g.
71 * SkPdfPageObjectVanila - has only the basic loaders/getters
72 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
73 * need to find a nice object model for all this with constructors and factories
74 * - deal with inheritable automatically ?
75 * - deal with specific type in spec directly, add all dictionary types to known types
76*/
77
78using namespace std;
edisonn@google.com131d4ee2013-06-26 17:48:12 +000079
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000080NotOwnedString strings_DeviceRGB;
81NotOwnedString strings_DeviceCMYK;
edisonn@google.com222382b2013-07-10 22:33:10 +000082
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000083class StringsInit {
84public:
85 StringsInit() {
86 NotOwnedString::init(&strings_DeviceRGB, "DeviceRGB");
87 NotOwnedString::init(&strings_DeviceCMYK, "DeviceCMYK");
88 }
89};
90
91StringsInit gStringsInit;
edisonn@google.com222382b2013-07-10 22:33:10 +000092
93// TODO(edisonn): Document PdfTokenLooper and subclasses.
94class PdfTokenLooper {
95protected:
96 PdfTokenLooper* fParent;
97 SkPdfNativeTokenizer* fTokenizer;
98 PdfContext* fPdfContext;
99 SkCanvas* fCanvas;
100
101public:
102 PdfTokenLooper(PdfTokenLooper* parent,
103 SkPdfNativeTokenizer* tokenizer,
104 PdfContext* pdfContext,
105 SkCanvas* canvas)
106 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
107
108 virtual ~PdfTokenLooper() {}
109
110 virtual PdfResult consumeToken(PdfToken& token) = 0;
111 virtual void loop() = 0;
112
113 void setUp(PdfTokenLooper* parent) {
114 fParent = parent;
115 fTokenizer = parent->fTokenizer;
116 fPdfContext = parent->fPdfContext;
117 fCanvas = parent->fCanvas;
118 }
edisonn@google.com78b38b12013-07-15 18:20:58 +0000119
120 SkPdfNativeTokenizer* tokenizer() { return fTokenizer; }
edisonn@google.com222382b2013-07-10 22:33:10 +0000121};
122
123class PdfMainLooper : public PdfTokenLooper {
124public:
125 PdfMainLooper(PdfTokenLooper* parent,
126 SkPdfNativeTokenizer* tokenizer,
127 PdfContext* pdfContext,
128 SkCanvas* canvas)
129 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
130
131 virtual PdfResult consumeToken(PdfToken& token);
132 virtual void loop();
133};
134
135class PdfInlineImageLooper : public PdfTokenLooper {
136public:
137 PdfInlineImageLooper()
138 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
139
140 virtual PdfResult consumeToken(PdfToken& token);
141 virtual void loop();
142 PdfResult done();
143};
144
145class PdfCompatibilitySectionLooper : public PdfTokenLooper {
146public:
147 PdfCompatibilitySectionLooper()
148 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
149
150 virtual PdfResult consumeToken(PdfToken& token);
151 virtual void loop();
152};
153
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000154// Utilities
155static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
156 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
157
158 bitmap->allocPixels();
159 bitmap->eraseColor(color);
160}
161
162// TODO(edisonn): synonyms? DeviceRGB and RGB ...
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000163static int GetColorSpaceComponents(NotOwnedString& colorSpace) {
164 if (colorSpace.equals("DeviceCMYK")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000165 return 4;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000166 } else if (colorSpace.equals("DeviceGray") ||
167 colorSpace.equals("CalGray") ||
168 colorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000169 return 1;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000170 } else if (colorSpace.equals("DeviceRGB") ||
171 colorSpace.equals("CalRGB") ||
172 colorSpace.equals("Lab")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000173 return 3;
174 } else {
175 return 0;
176 }
177}
178
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000179SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000180 SkMatrix matrix;
181 matrix.setAll(SkDoubleToScalar(array[0]),
182 SkDoubleToScalar(array[2]),
183 SkDoubleToScalar(array[4]),
184 SkDoubleToScalar(array[1]),
185 SkDoubleToScalar(array[3]),
186 SkDoubleToScalar(array[5]),
187 SkDoubleToScalar(0),
188 SkDoubleToScalar(0),
189 SkDoubleToScalar(1));
190
191 return matrix;
192}
193
194SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
195 double array[6];
196
197 // TODO(edisonn): security issue, ret if size() != 6
198 for (int i = 0; i < 6; i++) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000199 const SkPdfObject* elem = pdfArray->operator [](i);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000200 if (elem == NULL || !elem->isNumber()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000201 return SkMatrix::I(); // TODO(edisonn): report issue
202 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000203 array[i] = elem->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000204 }
205
206 return SkMatrixFromPdfMatrix(array);
207}
208
edisonn@google.com222382b2013-07-10 22:33:10 +0000209
210extern "C" SkNativeParsedPDF* gDoc;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000211SkBitmap* gDumpBitmap = NULL;
212SkCanvas* gDumpCanvas = NULL;
213char gLastKeyword[100] = "";
214int gLastOpKeyword = -1;
215char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
216int gReadOp = 0;
217
218
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000219#ifdef PDF_TRACE_DIFF_IN_PNG
220static bool hasVisualEffect(const char* pdfOp) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000221 return true;
222 if (*pdfOp == '\0') return false;
223
224 char markedPdfOp[100] = ",";
225 strcat(markedPdfOp, pdfOp);
226 strcat(markedPdfOp, ",");
227
228 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
229}
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000230#endif // PDF_TRACE_DIFF_IN_PNG
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000231
edisonn@google.com222382b2013-07-10 22:33:10 +0000232
233
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000234// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000235static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000236 bool ret = fTokenizer->readToken(token);
237
238 gReadOp++;
239
240#ifdef PDF_TRACE_DIFF_IN_PNG
241 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
242 // the numbar and name of last operation, so the file name will reflect op that changed.
243 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
244 gDumpCanvas->flush();
245
246 SkBitmap bitmap;
247 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
248
249 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
250
251 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
252 SkCanvas canvas(device);
253
254 // draw context stuff here
255 SkPaint blueBorder;
256 blueBorder.setColor(SK_ColorBLUE);
257 blueBorder.setStyle(SkPaint::kStroke_Style);
258 blueBorder.setTextSize(SkDoubleToScalar(20));
259
260 SkString str;
261
262 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
263 if (clipStack) {
264 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
265 const SkClipStack::Element* elem;
266 double y = 0;
267 int total = 0;
268 while (elem = iter.next()) {
269 total++;
270 y += 30;
271
272 switch (elem->getType()) {
273 case SkClipStack::Element::kRect_Type:
274 canvas.drawRect(elem->getRect(), blueBorder);
275 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
276 break;
277 case SkClipStack::Element::kPath_Type:
278 canvas.drawPath(elem->getPath(), blueBorder);
279 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
280 break;
281 case SkClipStack::Element::kEmpty_Type:
282 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
283 break;
284 default:
285 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
286 break;
287 }
288 }
289
290 y += 30;
291 str.printf("Number of clips in stack: %i", total);
292 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
293 }
294
295 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
296 SkPath clipPath;
297 if (clipRegion.getBoundaryPath(&clipPath)) {
298 SkPaint redBorder;
299 redBorder.setColor(SK_ColorRED);
300 redBorder.setStyle(SkPaint::kStroke_Style);
301 canvas.drawPath(clipPath, redBorder);
302 }
303
304 canvas.flush();
305
306 SkString out;
307
308 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
309 // ... and other properties, to be able to debug th code easily
310
311 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
312 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
313 }
314
315 if (token->fType == kKeyword_TokenType) {
316 strcpy(gLastKeyword, token->fKeyword);
317 gLastOpKeyword = gReadOp;
318 } else {
319 strcpy(gLastKeyword, "");
320 }
321#endif
322
323 return ret;
324}
325
326
327
328typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
329
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000330SkTDict<PdfOperatorRenderer> gPdfOps(100);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000331
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000332
333template <typename T> class SkTDictWithDefaultConstructor : public SkTDict<T> {
334public:
335 SkTDictWithDefaultConstructor() : SkTDict<T>(10) {}
336};
337
338SkTDictWithDefaultConstructor<int> gRenderStats[kCount_PdfResult];
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000339
edisonn@google.com571c70b2013-07-10 17:09:50 +0000340const char* gRenderStatsNames[kCount_PdfResult] = {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000341 "Success",
342 "Partially implemented",
343 "Not yet implemented",
344 "Ignore Error",
345 "Error",
346 "Unsupported/Unknown"
347};
348
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000349static PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000350 const SkPdfObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000351 SkCanvas* canvas)
352{
353
354 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
355 if (skfont == NULL) {
356 skfont = SkPdfFont::Default();
357 }
358
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000359
edisonn@google.com571c70b2013-07-10 17:09:50 +0000360 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000361 // TODO(edisonn): report warning
362 return kIgnoreError_PdfResult;
363 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000364 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000365
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000366 SkUnencodedText binary(str);
367
368 SkDecodedText decoded;
369
370 if (skfont->encoding() == NULL) {
371 // TODO(edisonn): report warning
372 return kNYI_PdfResult;
373 }
374
375 skfont->encoding()->decodeText(binary, &decoded);
376
377 SkPaint paint;
378 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
379 // Or maybe just not call setTextSize at all?
380 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
381 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
382 }
383
384// if (fCurFont && fCurFont->GetFontScale() != 0) {
385// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
386// }
387
388 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
389
390 canvas->save();
391
392#if 1
393 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
394
395 SkPoint point1;
396 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
397
398 SkMatrix mirror;
399 mirror.setTranslate(0, -point1.y());
400 // TODO(edisonn): fix rotated text, and skewed too
401 mirror.postScale(SK_Scalar1, -SK_Scalar1);
402 // TODO(edisonn): post rotate, skew
403 mirror.postTranslate(0, point1.y());
404
405 matrix.postConcat(mirror);
406
407 canvas->setMatrix(matrix);
408
409 SkTraceMatrix(matrix, "mirrored");
410#endif
411
edisonn@google.com6e49c342013-06-27 20:03:43 +0000412 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000413 canvas->restore();
414
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000415 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000416}
417
418// TODO(edisonn): create header files with declarations!
419PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
420PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
421PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
422PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
423
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000424// TODO(edisonn): perf!!!
425
426static SkColorTable* getGrayColortable() {
427 static SkColorTable* grayColortable = NULL;
428 if (grayColortable == NULL) {
429 SkPMColor* colors = new SkPMColor[256];
430 for (int i = 0 ; i < 256; i++) {
431 colors[i] = SkPreMultiplyARGB(255, i, i, i);
432 }
433 grayColortable = new SkColorTable(colors, 256);
434 }
435 return grayColortable;
436}
437
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000438static SkBitmap transferImageStreamToBitmap(const unsigned char* uncompressedStream, size_t uncompressedStreamLength,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000439 int width, int height, int bytesPerLine,
440 int bpc, const std::string& colorSpace,
441 bool transparencyMask) {
442 SkBitmap bitmap;
443
edisonn@google.com571c70b2013-07-10 17:09:50 +0000444 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000445//#define MAX_COMPONENTS 10
446
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000447 // TODO(edisonn): assume start of lines are aligned at 32 bits?
448 // Is there a faster way to load the uncompressed stream into a bitmap?
449
450 // minimal support for now
451 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
452 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
453
454 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000455 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000456 for (int w = 0 ; w < width; w++) {
457 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
458 uncompressedStream[3 * w + 1],
459 uncompressedStream[3 * w + 2]);
460 i++;
461 }
462 uncompressedStream += bytesPerLine;
463 }
464
465 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
466 bitmap.setPixels(uncompressedStreamArgb);
467 }
468 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
469 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
470
471 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000472 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000473 for (int w = 0 ; w < width; w++) {
474 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
475 uncompressedStream[w];
476 i++;
477 }
478 uncompressedStream += bytesPerLine;
479 }
480
481 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
482 width, height);
483 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
484 }
485
486 // TODO(edisonn): Report Warning, NYI, or error
487 return bitmap;
488}
489
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000490// utils
491
492// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
493// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
494// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
495// skia format, through a table
496
497// this functions returns the image, it does not look at the smask.
498
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000499static SkBitmap getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000500 if (image == NULL || !image->hasStream()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000501 // TODO(edisonn): report warning to be used in testing.
502 return SkBitmap();
503 }
504
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000505 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc);
506 int64_t width = image->Width(pdfContext->fPdfDoc);
507 int64_t height = image->Height(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000508 std::string colorSpace = "DeviceRGB";
509
510 // TODO(edisonn): color space can be an array too!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000511 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
512 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000513 }
514
515/*
516 bool imageMask = image->imageMask();
517
518 if (imageMask) {
519 if (bpc != 0 && bpc != 1) {
520 // TODO(edisonn): report warning to be used in testing.
521 return SkBitmap();
522 }
523 bpc = 1;
524 }
525*/
526
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000527 const unsigned char* uncompressedStream = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000528 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000529
edisonn@google.com571c70b2013-07-10 17:09:50 +0000530 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000531
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000532 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000533 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000534 // TODO(edisonn): report warning to be used in testing.
535 return SkBitmap();
536 }
537
edisonn@google.com571c70b2013-07-10 17:09:50 +0000538 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
539
540 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
541 streamDict->getFilterAsName(NULL) == "DCTDecode") ||
542 (streamDict->isFilterAArray(NULL) &&
543 streamDict->getFilterAsArray(NULL)->size() > 0 &&
544 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
545 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() == "DCTDecode"))) {
546 SkBitmap bitmap;
547 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, &bitmap);
548 return bitmap;
549 }
550
551
552
553 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
554// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
555// obj.GetDictionary().GetKey(PdfName("Filter")));
556// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
557// value = resolveReferenceObject(pdfContext->fPdfDoc,
558// &value->GetArray()[0]);
559// }
560// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
561// SkStream stream = SkStream::
562// SkImageDecoder::Factory()
563// }
564
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000565 int bytesPerLine = (int)(uncompressedStreamLength / height);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000566#ifdef PDF_TRACE
567 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000568 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000569 }
570#endif
571
572 SkBitmap bitmap = transferImageStreamToBitmap(
573 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000574 (int)width, (int)height, bytesPerLine,
575 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000576 transparencyMask);
577
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000578 return bitmap;
579}
580
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000581static SkBitmap getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000582 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000583
584 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000585 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000586 }
587
588 // TODO(edisonn): implement GS SMask. Default to empty right now.
589 return pdfContext->fGraphicsState.fSMask;
590}
591
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000592static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000593 if (skpdfimage == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000594 return kIgnoreError_PdfResult;
595 }
596
597 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
598 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
599
600 canvas->save();
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000601 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000602
603#if 1
604 SkScalar z = SkIntToScalar(0);
605 SkScalar one = SkIntToScalar(1);
606
607 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
608 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
609 SkMatrix flip;
610 SkAssertResult(flip.setPolyToPoly(from, to, 4));
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000611 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000612 solveImageFlip.preConcat(flip);
613 canvas->setMatrix(solveImageFlip);
614#endif
615
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000616 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
617
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000618 // TODO(edisonn): soft mask type? alpha/luminosity.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000619 if (sMask.empty()) {
620 canvas->drawBitmapRect(image, dst, NULL);
621 } else {
622 canvas->saveLayer(&dst, NULL);
623 canvas->drawBitmapRect(image, dst, NULL);
624 SkPaint xfer;
625 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000626 // TODO(edisonn): is the blend mode specified already implicitly/explicitly in pdf?
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000627 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
628 canvas->drawBitmapRect(sMask, dst, &xfer);
629 canvas->restore();
630 }
631
632 canvas->restore();
633
634 return kPartial_PdfResult;
635}
636
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000637//TODO(edisonn): options for implementing isolation and knockout
638// 1) emulate them (current solution)
639// PRO: simple
640// CON: will need to use readPixels, which means serious perf issues
641// 2) Compile a plan for an array of matrixes, compose the result at the end
642// PRO: might be faster then 1, no need to readPixels
643// CON: multiple drawings (but on smaller areas), pay a price at loading pdf to compute a pdf draw plan
644// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
645// 3) support them natively in SkCanvas
646// PRO: simple
647// CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1)
648// 4) compile a plan using pathops, and render once without any fancy rules with backdrop
649// PRO: simple, fast
650// CON: pathops must be bug free first + time to compute new paths
651// pay a price at loading pdf to compute a pdf draw plan
652// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
edisonn@google.com251176e2013-08-01 13:24:00 +0000653// 5) for knockout, render the objects in reverse order, and add every object to the clip, and any new draw will be cliped
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000654
655
656// TODO(edisonn): draw plan from point! - list of draw ops of a point, like a tree!
657// TODO(edisonn): Minimal PDF to draw some points - remove everything that it is not needed, save pdf uncompressed
658
659
660
661static void doGroup_before(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup, bool page) {
662 SkRect bboxOrig = bbox;
663 SkBitmap backdrop;
664 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
665// bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000666 SkPaint paint;
667 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
668 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000669}
670
edisonn@google.com251176e2013-08-01 13:24:00 +0000671// TODO(edisonn): non isolation implemented in skia
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000672//static void doGroup_after(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup) {
edisonn@google.com251176e2013-08-01 13:24:00 +0000673// if not isolated
674// canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000675//}
676
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000677static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000678 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000679 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000680 }
681
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000682 if (!skobj->has_BBox()) {
683 return kIgnoreError_PdfResult;
684 }
685
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000686 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000687
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000688 canvas->save();
689
690
edisonn@google.com571c70b2013-07-10 17:09:50 +0000691 if (skobj->Resources(pdfContext->fPdfDoc)) {
692 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000693 }
694
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000695 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000696
edisonn@google.com571c70b2013-07-10 17:09:50 +0000697 if (skobj->has_Matrix()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000698 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
699 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
700 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000701 // TODO(edisonn) reset matrixTm and matricTlm also?
702 }
703
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000704 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000705
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000706 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000707
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000708 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
709 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000710
711 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
712 // For this PdfContentsTokenizer needs to be extended.
713
edisonn@google.come878e722013-07-29 19:10:58 +0000714 // This is a group?
715 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000716 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
717 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
edisonn@google.come878e722013-07-29 19:10:58 +0000718 }
719
edisonn@google.com571c70b2013-07-10 17:09:50 +0000720 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000721
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000722 SkPdfNativeTokenizer* tokenizer =
723 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000724 if (tokenizer != NULL) {
725 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
726 looper.loop();
727 delete tokenizer;
728 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000729
730 // TODO(edisonn): should we restore the variable stack at the same state?
731 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000732
733 if (skobj->has_Group()) {
734 canvas->restore();
735 }
736
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000737 canvas->restore();
738 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000739 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000740}
741
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000742
743// TODO(edisonn): Extract a class like ObjWithStream
744static PdfResult doXObject_Pattern(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1PatternDictionary* skobj) {
745 if (!skobj || !skobj->hasStream()) {
746 return kIgnoreError_PdfResult;
747 }
748
749 if (!skobj->has_BBox()) {
750 return kIgnoreError_PdfResult;
751 }
752
753 PdfOp_q(pdfContext, canvas, NULL);
754
755 canvas->save();
756
757
758 if (skobj->Resources(pdfContext->fPdfDoc)) {
759 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
760 }
761
762 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
763
764 if (skobj->has_Matrix()) {
765 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
766 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
767 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
768 // TODO(edisonn) reset matrixTm and matricTlm also?
769 }
770
771 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
772
773 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
774
775 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
776 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
777
778 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
779 // For this PdfContentsTokenizer needs to be extended.
780
781 SkPdfStream* stream = (SkPdfStream*)skobj;
782
783 SkPdfNativeTokenizer* tokenizer =
784 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
785 if (tokenizer != NULL) {
786 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
787 looper.loop();
788 delete tokenizer;
789 }
790
791 // TODO(edisonn): should we restore the variable stack at the same state?
792 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
793
794 canvas->restore();
795 PdfOp_Q(pdfContext, canvas, NULL);
796 return kPartial_PdfResult;
797}
798
799
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000800//static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
801// return kNYI_PdfResult;
802//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000803
edisonn@google.com571c70b2013-07-10 17:09:50 +0000804PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
805 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000806 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000807 }
808
809 PdfOp_q(pdfContext, canvas, NULL);
810 canvas->save();
811
812 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
813 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
814
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000815 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
816 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000817
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000818 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000819
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000820 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000821
822 SkRect rm = bBox;
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000823 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000824
825 SkTraceRect(rm, "bbox mapped");
826
827 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
828
829 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
830 // For this PdfContentsTokenizer needs to be extended.
831
edisonn@google.com571c70b2013-07-10 17:09:50 +0000832 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000833
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000834 SkPdfNativeTokenizer* tokenizer =
835 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000836 if (tokenizer != NULL) {
837 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
838 looper.loop();
839 delete tokenizer;
840 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000841
842 // TODO(edisonn): should we restore the variable stack at the same state?
843 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
844 canvas->restore();
845 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000846
847 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000848}
849
850
edisonn@google.com571c70b2013-07-10 17:09:50 +0000851// TODO(edisonn): make sure the pointer is unique
852std::set<const SkPdfObject*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000853
854class CheckRecursiveRendering {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000855 const SkPdfObject* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000856public:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000857 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000858 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000859 }
860
861 ~CheckRecursiveRendering() {
862 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000863 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000864 }
865
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000866 static bool IsInRendering(const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000867 return gInRendering.find(obj) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000868 }
869};
870
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000871static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000872 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000873 // Oops, corrupt PDF!
874 return kIgnoreError_PdfResult;
875 }
876
edisonn@google.com571c70b2013-07-10 17:09:50 +0000877 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000878
edisonn@google.com571c70b2013-07-10 17:09:50 +0000879 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000880 {
881 case kImageDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000882 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000883 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000884 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000885 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
886 //return doXObject_PS(skxobj.asPS());
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000887 default: {
888 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != kNone_SkPdfObjectType) {
889 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
890 return doXObject_Pattern(pdfContext, canvas, pattern);
891 }
892 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000893 }
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000894 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000895}
896
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000897static PdfResult doPage(PdfContext* pdfContext, SkCanvas* canvas, SkPdfPageObjectDictionary* skobj) {
898 if (!skobj) {
899 return kIgnoreError_PdfResult;
900 }
901
902 if (!skobj->isContentsAStream(pdfContext->fPdfDoc)) {
903 return kNYI_PdfResult;
904 }
905
906 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
907
908 if (!stream) {
909 return kIgnoreError_PdfResult;
910 }
911
912 if (CheckRecursiveRendering::IsInRendering(skobj)) {
913 // Oops, corrupt PDF!
914 return kIgnoreError_PdfResult;
915 }
916 CheckRecursiveRendering checkRecursion(skobj);
917
918
919 PdfOp_q(pdfContext, canvas, NULL);
920
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000921
922 if (skobj->Resources(pdfContext->fPdfDoc)) {
923 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
924 }
925
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000926 // TODO(edisonn): MediaBox can be inherited!!!!
927 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000928 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000929 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
930 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
931 } else {
932 canvas->save();
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000933 }
934
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000935
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000936 SkPdfNativeTokenizer* tokenizer =
937 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
938 if (tokenizer != NULL) {
939 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
940 looper.loop();
941 delete tokenizer;
942 }
943
944 // TODO(edisonn): should we restore the variable stack at the same state?
945 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
946 canvas->restore();
947 PdfOp_Q(pdfContext, canvas, NULL);
948 return kPartial_PdfResult;
949}
950
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000951PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
952 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
953 canvas->save();
954 return kOK_PdfResult;
955}
956
957PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
958 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
959 pdfContext->fStateStack.pop();
960 canvas->restore();
961 return kOK_PdfResult;
962}
963
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000964static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000965 double array[6];
966 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000967 array[5 - i] = pdfContext->fObjectStack.top()->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000968 pdfContext->fObjectStack.pop();
969 }
970
971 // a b
972 // c d
973 // e f
974
975 // 0 1
976 // 2 3
977 // 4 5
978
979 // sx ky
980 // kx sy
981 // tx ty
982 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
983
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000984 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000985
986#ifdef PDF_TRACE
987 printf("cm ");
988 for (int i = 0 ; i < 6 ; i++) {
989 printf("%f ", array[i]);
990 }
991 printf("\n");
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000992 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000993#endif
994
995 return kOK_PdfResult;
996}
997
998//leading TL Set the text leading, Tl
999//, to leading, which is a number expressed in unscaled text
1000//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001001static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001002 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001003
1004 pdfContext->fGraphicsState.fTextLeading = ty;
1005
1006 return kOK_PdfResult;
1007}
1008
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001009static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001010#ifdef PDF_TRACE
1011 printf("stack size = %i\n", (int)pdfContext->fObjectStack.size());
1012#endif
edisonn@google.com571c70b2013-07-10 17:09:50 +00001013 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001014 SkPdfObject* obj = pdfContext->fObjectStack.top();
1015 obj = obj;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001016 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001017
1018 double array[6] = {1, 0, 0, 1, tx, ty};
1019 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1020
1021 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1022 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1023
1024 return kPartial_PdfResult;
1025}
1026
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001027static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001028 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1029 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001030
edisonn@google.com571c70b2013-07-10 17:09:50 +00001031 // TODO(edisonn): Create factory methods or constructors so native is hidden
1032 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001033 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001034
1035 PdfOp_TL(pdfContext, canvas, looper);
1036
edisonn@google.com571c70b2013-07-10 17:09:50 +00001037 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001038 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001039
edisonn@google.com571c70b2013-07-10 17:09:50 +00001040 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001041 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001042
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001043 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1044
1045 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
1046 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001047
1048 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001049}
1050
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001051static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001052 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1053 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1054 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1055 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1056 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1057 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001058
1059 double array[6];
1060 array[0] = a;
1061 array[1] = b;
1062 array[2] = c;
1063 array[3] = d;
1064 array[4] = e;
1065 array[5] = f;
1066
1067 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001068 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001069
1070 // TODO(edisonn): Text positioning.
1071 pdfContext->fGraphicsState.fMatrixTm = matrix;
1072 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1073
1074 return kPartial_PdfResult;
1075}
1076
1077//— T* Move to the start of the next line. This operator has the same effect as the code
1078//0 Tl Td
1079//where Tl is the current leading parameter in the text state
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001080static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001081 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
1082 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001083
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001084 pdfContext->fObjectStack.push(zero);
1085 pdfContext->fObjectStack.push(tl);
1086
1087 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1088
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001089 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001090}
1091
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001092static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001093 if (pdfContext->fGraphicsState.fPathClosed) {
1094 pdfContext->fGraphicsState.fPath.reset();
1095 pdfContext->fGraphicsState.fPathClosed = false;
1096 }
1097
edisonn@google.com571c70b2013-07-10 17:09:50 +00001098 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1099 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001100
1101 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1102 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1103
1104 return kOK_PdfResult;
1105}
1106
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001107static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001108 if (pdfContext->fGraphicsState.fPathClosed) {
1109 pdfContext->fGraphicsState.fPath.reset();
1110 pdfContext->fGraphicsState.fPathClosed = false;
1111 }
1112
edisonn@google.com571c70b2013-07-10 17:09:50 +00001113 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1114 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001115
1116 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1117 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1118
1119 return kOK_PdfResult;
1120}
1121
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001122static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001123 if (pdfContext->fGraphicsState.fPathClosed) {
1124 pdfContext->fGraphicsState.fPath.reset();
1125 pdfContext->fGraphicsState.fPathClosed = false;
1126 }
1127
edisonn@google.com571c70b2013-07-10 17:09:50 +00001128 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1129 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1130 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1131 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1132 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1133 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001134
1135 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1136 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1137 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1138
1139 pdfContext->fGraphicsState.fCurPosX = x3;
1140 pdfContext->fGraphicsState.fCurPosY = y3;
1141
1142 return kOK_PdfResult;
1143}
1144
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001145static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001146 if (pdfContext->fGraphicsState.fPathClosed) {
1147 pdfContext->fGraphicsState.fPath.reset();
1148 pdfContext->fGraphicsState.fPathClosed = false;
1149 }
1150
edisonn@google.com571c70b2013-07-10 17:09:50 +00001151 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1152 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1153 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1154 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001155 double y1 = pdfContext->fGraphicsState.fCurPosY;
1156 double x1 = pdfContext->fGraphicsState.fCurPosX;
1157
1158 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1159 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1160 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1161
1162 pdfContext->fGraphicsState.fCurPosX = x3;
1163 pdfContext->fGraphicsState.fCurPosY = y3;
1164
1165 return kOK_PdfResult;
1166}
1167
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001168static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001169 if (pdfContext->fGraphicsState.fPathClosed) {
1170 pdfContext->fGraphicsState.fPath.reset();
1171 pdfContext->fGraphicsState.fPathClosed = false;
1172 }
1173
edisonn@google.com571c70b2013-07-10 17:09:50 +00001174 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1175 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001176 double y2 = pdfContext->fGraphicsState.fCurPosY;
1177 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001178 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1179 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001180
1181 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1182 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1183 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1184
1185 pdfContext->fGraphicsState.fCurPosX = x3;
1186 pdfContext->fGraphicsState.fCurPosY = y3;
1187
1188 return kOK_PdfResult;
1189}
1190
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001191static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001192 if (pdfContext->fGraphicsState.fPathClosed) {
1193 pdfContext->fGraphicsState.fPath.reset();
1194 pdfContext->fGraphicsState.fPathClosed = false;
1195 }
1196
edisonn@google.com571c70b2013-07-10 17:09:50 +00001197 double height = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1198 double width = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1199 double y = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1200 double x = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001201
1202 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1203 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1204
1205 pdfContext->fGraphicsState.fCurPosX = x;
1206 pdfContext->fGraphicsState.fCurPosY = y + height;
1207
1208 return kOK_PdfResult;
1209}
1210
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001211static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001212 pdfContext->fGraphicsState.fPath.close();
1213 return kOK_PdfResult;
1214}
1215
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001216static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001217 SkPath path = pdfContext->fGraphicsState.fPath;
1218
1219 if (close) {
1220 path.close();
1221 }
1222
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001223 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001224
1225 SkPaint paint;
1226
1227 SkPoint line[2];
1228 if (fill && !stroke && path.isLine(line)) {
1229 paint.setStyle(SkPaint::kStroke_Style);
1230
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001231 // TODO(edisonn): implement this with patterns
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001232 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1233 paint.setStrokeWidth(SkDoubleToScalar(0));
1234
1235 canvas->drawPath(path, paint);
1236 } else {
1237 if (fill) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001238 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0 &&
1239 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1240
1241 // TODO(edisonn): we can use a shader here, like imageshader to draw fast. ultimately,
1242 // if this is not possible, and we are in rasper mode, and the cells don't intersect, we could even have multiple cpus.
1243
1244 canvas->save();
1245 PdfOp_q(pdfContext, canvas, NULL);
1246
1247 if (evenOdd) {
1248 path.setFillType(SkPath::kEvenOdd_FillType);
1249 }
1250 canvas->clipPath(path);
1251
1252 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(pdfContext->fGraphicsState.fNonStroking.fPattern) != kNone_SkPdfObjectType) {
1253 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState.fNonStroking.fPattern;
1254
1255 // TODO(edisonn): constants
1256 // TODO(edisonn): colored
1257 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
1258 int xStep = (int)pattern->XStep(pdfContext->fPdfDoc);
1259 int yStep = (int)pattern->YStep(pdfContext->fPdfDoc);
1260
1261 SkRect bounds = path.getBounds();
1262 SkScalar x;
1263 SkScalar y;
1264
1265 // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse
1266
1267 y = bounds.top();
1268 int totalx = 0;
1269 int totaly = 0;
1270 while (y < bounds.bottom()) {
1271 x = bounds.left();
1272 totalx = 0;
1273
1274 while (x < bounds.right()) {
1275 doXObject(pdfContext, canvas, pattern);
1276
1277 pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(xStep), SkIntToScalar(0));
1278 totalx += xStep;
1279 x += SkIntToScalar(xStep);
1280 }
1281 pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(-totalx), SkIntToScalar(0));
1282
1283 pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(0), SkIntToScalar(-yStep));
1284 totaly += yStep;
1285 y += SkIntToScalar(yStep);
1286 }
1287 pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(0), SkIntToScalar(totaly));
1288 }
1289 }
1290
1291 // apply matrix
1292 // get xstep, y step, bbox ... for cliping, and bos of the path
1293
1294 PdfOp_Q(pdfContext, canvas, NULL);
1295 canvas->restore();
1296 } else {
1297 paint.setStyle(SkPaint::kFill_Style);
1298 if (evenOdd) {
1299 path.setFillType(SkPath::kEvenOdd_FillType);
1300 }
1301
1302 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1303
1304 canvas->drawPath(path, paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001305 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001306 }
1307
1308 if (stroke) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001309 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0) {
1310 // TODO(edisonn): implement Pattern for strokes
1311 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001312
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001313 paint.setColor(SK_ColorGREEN);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001314
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001315 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1316 canvas->drawPath(path, paint);
1317 } else {
1318 paint.setStyle(SkPaint::kStroke_Style);
1319
1320 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1321
1322 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1323 canvas->drawPath(path, paint);
1324 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001325 }
1326 }
1327
1328 pdfContext->fGraphicsState.fPath.reset();
1329 // todo zoom ... other stuff ?
1330
1331 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1332#ifndef PDF_DEBUG_NO_CLIPING
1333 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1334#endif
1335 }
1336
1337 //pdfContext->fGraphicsState.fClipPath.reset();
1338 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1339
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001340 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001341
1342}
1343
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001344static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001345 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1346}
1347
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001348static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001349 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1350}
1351
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001352static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001353 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1354}
1355
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001356static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001357 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1358}
1359
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001360static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001361 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1362}
1363
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001364static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001365 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1366}
1367
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001368static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001369 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1370}
1371
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001372static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001373 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1374}
1375
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001376static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001377 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1378}
1379
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001380static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001381 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001382 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1383#ifndef PDF_DEBUG_NO_CLIPING
1384 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1385#endif
1386 }
1387
1388 //pdfContext->fGraphicsState.fClipPath.reset();
1389 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1390
1391 pdfContext->fGraphicsState.fPathClosed = true;
1392
1393 return kOK_PdfResult;
1394}
1395
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001396static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001397 pdfContext->fGraphicsState.fTextBlock = true;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001398 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
1399 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001400
1401 return kPartial_PdfResult;
1402}
1403
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001404static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001405 if (!pdfContext->fGraphicsState.fTextBlock) {
1406 return kIgnoreError_PdfResult;
1407 }
1408 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001409 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001410}
1411
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001412PdfResult skpdfGraphicsStateApplyFontCore(PdfContext* pdfContext, const SkPdfObject* fontName, double fontSize) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001413#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001414 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001415#endif
1416
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001417 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1418 // TODO(edisonn): try to recover and draw it any way?
1419 return kIgnoreError_PdfResult;
1420 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001421
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001422 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1423 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1424 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1425 // TODO(edisonn): try to recover and draw it any way?
1426 return kIgnoreError_PdfResult;
1427 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001428
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001429 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1430
1431 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1432
1433 if (skfont) {
1434 pdfContext->fGraphicsState.fSkFont = skfont;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001435 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001436 pdfContext->fGraphicsState.fCurFontSize = fontSize;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001437 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001438}
1439
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001440//font size Tf Set the text font, Tf
1441//, to font and the text font size, Tfs, to size. font is the name of a
1442//font resource in the Fontsubdictionary of the current resource dictionary; size is
1443//a number representing a scale factor. There is no initial value for either font or
1444//size; they must be specified explicitly using Tf before any text is shown.
1445static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1446 double fontSize = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1447 SkPdfObject* fontName = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1448 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1449}
1450
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001451static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001452 if (!pdfContext->fGraphicsState.fTextBlock) {
1453 // TODO(edisonn): try to recover and draw it any way?
1454 return kIgnoreError_PdfResult;
1455 }
1456
1457 PdfResult ret = DrawText(pdfContext,
1458 pdfContext->fObjectStack.top(),
1459 canvas);
1460 pdfContext->fObjectStack.pop();
1461
1462 return ret;
1463}
1464
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001465static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001466 if (!pdfContext->fGraphicsState.fTextBlock) {
1467 // TODO(edisonn): try to recover and draw it any way?
1468 return kIgnoreError_PdfResult;
1469 }
1470
1471 PdfOp_T_star(pdfContext, canvas, looper);
1472 // Do not pop, and push, just transfer the param to Tj
1473 return PdfOp_Tj(pdfContext, canvas, looper);
1474}
1475
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001476static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001477 if (!pdfContext->fGraphicsState.fTextBlock) {
1478 // TODO(edisonn): try to recover and draw it any way?
1479 return kIgnoreError_PdfResult;
1480 }
1481
1482 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1483 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1484 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1485
1486 pdfContext->fObjectStack.push(aw);
1487 PdfOp_Tw(pdfContext, canvas, looper);
1488
1489 pdfContext->fObjectStack.push(ac);
1490 PdfOp_Tc(pdfContext, canvas, looper);
1491
1492 pdfContext->fObjectStack.push(str);
1493 PdfOp_quote(pdfContext, canvas, looper);
1494
1495 return kPartial_PdfResult;
1496}
1497
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001498static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001499 if (!pdfContext->fGraphicsState.fTextBlock) {
1500 // TODO(edisonn): try to recover and draw it any way?
1501 return kIgnoreError_PdfResult;
1502 }
1503
edisonn@google.com571c70b2013-07-10 17:09:50 +00001504 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001505 pdfContext->fObjectStack.pop();
1506
edisonn@google.com571c70b2013-07-10 17:09:50 +00001507 if (!array->isArray()) {
1508 return kIgnoreError_PdfResult;
1509 }
1510
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001511 for( int i=0; i<static_cast<int>(array->size()); i++ )
1512 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001513 if( (*array)[i]->isAnyString()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001514 SkPdfObject* obj = (*array)[i];
1515 DrawText(pdfContext,
1516 obj,
1517 canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001518 } else if ((*array)[i]->isNumber()) {
1519 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001520 SkMatrix matrix;
1521 matrix.setAll(SkDoubleToScalar(1),
1522 SkDoubleToScalar(0),
1523 // TODO(edisonn): use writing mode, vertical/horizontal.
1524 SkDoubleToScalar(-dx), // amount is substracted!!!
1525 SkDoubleToScalar(0),
1526 SkDoubleToScalar(1),
1527 SkDoubleToScalar(0),
1528 SkDoubleToScalar(0),
1529 SkDoubleToScalar(0),
1530 SkDoubleToScalar(1));
1531
1532 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1533 }
1534 }
1535 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1536}
1537
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001538static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001539 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->strRef(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001540 return kOK_PdfResult;
1541}
1542
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001543static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001544 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1545}
1546
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001547static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001548 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1549}
1550
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001551static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001552 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001553// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001554
1555 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1556
1557 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001558 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001559 doubles = false;
1560 }
1561
1562#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001563 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001564#endif
1565
1566 for (int i = n - 1; i >= 0 ; i--) {
1567 if (doubles) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001568 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1569// } else {
1570// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001571 }
1572 }
1573
1574 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001575 // TODO(edisonn): do possible field values to enum at parsing time!
1576 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001577 if (colorOperator->fColorSpace.equals("DeviceRGB") || colorOperator->fColorSpace.equals("RGB")) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001578 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]), (U8CPU)(255*c[1]), (U8CPU)(255*c[2])));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001579 }
1580 return kPartial_PdfResult;
1581}
1582
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001583static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001584 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1585}
1586
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001587static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001588 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1589}
1590
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001591static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001592 if (pdfContext->fObjectStack.top()->isName()) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001593 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1594
1595 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001596 SkPdfDictionary* patternResources = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
edisonn@google.com276fed92013-08-01 21:20:47 +00001597
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001598 if (patternResources == NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001599#ifdef PDF_TRACE
1600 printf("ExtGState is NULL!\n");
1601#endif
1602 return kIgnoreError_PdfResult;
1603 }
1604
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001605 colorOperator->setPatternColorSpace(pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001606 }
1607
1608 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1609 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1610
1611 return kPartial_PdfResult;
1612}
1613
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001614static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001615 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1616}
1617
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001618static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001619 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1620}
1621
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001622static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001623 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001624 return kNYI_PdfResult;
1625}
1626
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001627static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001628 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1629}
1630
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001631static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001632 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1633}
1634
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001635static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001636 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1637 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1638 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001639
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001640 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001641 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001642 return kOK_PdfResult;
1643}
1644
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001645static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001646 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1647}
1648
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001649static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001650 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1651}
1652
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001653static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001654 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001655 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1656 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1657 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1658 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001659
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001660 colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001661 // TODO(edisonn): Set color.
1662 return kNYI_PdfResult;
1663}
1664
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001665static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001666 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1667}
1668
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001669static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001670 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1671}
1672
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001673static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001674 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1675 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1676
1677 return kOK_PdfResult;
1678}
1679
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001680static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001681 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1682
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001683 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1684 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1685
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001686 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001687}
1688
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001689static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001690 *looper = new PdfCompatibilitySectionLooper();
1691 return kOK_PdfResult;
1692}
1693
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001694static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001695#ifdef ASSERT_BAD_PDF_OPS
1696 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1697 // have the assert when testing good pdfs.
1698#endif
1699 return kIgnoreError_PdfResult;
1700}
1701
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001702static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001703 *looper = new PdfInlineImageLooper();
1704 return kOK_PdfResult;
1705}
1706
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001707static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001708#ifdef ASSERT_BAD_PDF_OPS
1709 SkASSERT(false); // must be processed in inline image looper, but let's
1710 // have the assert when testing good pdfs.
1711#endif
1712 return kIgnoreError_PdfResult;
1713}
1714
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001715static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001716#ifdef ASSERT_BAD_PDF_OPS
1717 SkASSERT(false); // must be processed in inline image looper, but let's
1718 // have the assert when testing good pdfs.
1719#endif
1720 return kIgnoreError_PdfResult;
1721}
1722
edisonn@google.com24cdf132013-07-30 16:06:12 +00001723
1724// TODO(edisonn): security review here, make sure all parameters are valid, and safe.
1725PdfResult skpdfGraphicsStateApply_ca(PdfContext* pdfContext, double ca) {
1726 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
1727 return kOK_PdfResult;
1728}
1729
1730PdfResult skpdfGraphicsStateApply_CA(PdfContext* pdfContext, double CA) {
1731 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
1732 return kOK_PdfResult;
1733}
1734
1735PdfResult skpdfGraphicsStateApplyLW(PdfContext* pdfContext, double lineWidth) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001736 pdfContext->fGraphicsState.fLineWidth = lineWidth;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001737 return kOK_PdfResult;
1738}
1739
1740PdfResult skpdfGraphicsStateApplyLC(PdfContext* pdfContext, int64_t lineCap) {
1741 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
1742 return kOK_PdfResult;
1743}
1744
1745PdfResult skpdfGraphicsStateApplyLJ(PdfContext* pdfContext, int64_t lineJoin) {
1746 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
1747 return kOK_PdfResult;
1748}
1749
1750PdfResult skpdfGraphicsStateApplyML(PdfContext* pdfContext, double miterLimit) {
1751 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
1752 return kOK_PdfResult;
1753}
1754
1755// TODO(edisonn): implement all rules, as of now 3) and 5) and 6) do not seem suported by skia, but I am not sure
1756/*
17571) [ ] 0 No dash; solid, unbroken lines
17582) [3] 0 3 units on, 3 units off, …
17593) [2] 1 1 on, 2 off, 2 on, 2 off, …
17604) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
17615) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
17626) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
1763 */
1764
1765PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* intervals, SkPdfObject* phase) {
1766 int cnt = intervals->size();
1767 if (cnt >= 256) {
1768 // TODO(edisonn): report error/warning, unsuported;
1769 // TODO(edisonn): alloc memory
1770 return kIgnoreError_PdfResult;
1771 }
1772 for (int i = 0; i < cnt; i++) {
1773 if (!intervals->objAtAIndex(i)->isNumber()) {
1774 // TODO(edisonn): report error/warning
1775 return kIgnoreError_PdfResult;
1776 }
1777 }
1778
edisonn@google.com24cdf132013-07-30 16:06:12 +00001779 double total = 0;
1780 for (int i = 0 ; i < cnt; i++) {
1781 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
1782 total += pdfContext->fGraphicsState.fDashArray[i];
1783 }
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001784 if (cnt & 1) {
1785 if (cnt == 1) {
1786 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
1787 cnt++;
1788 } else {
1789 // TODO(edisonn): report error/warning
1790 return kNYI_PdfResult;
1791 }
1792 }
1793 pdfContext->fGraphicsState.fDashArrayLength = cnt;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001794 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
1795 if (pdfContext->fGraphicsState.fDashPhase == 0) {
1796 // other rules, changes?
1797 pdfContext->fGraphicsState.fDashPhase = total;
1798 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001799
1800 return kOK_PdfResult;
1801}
1802
edisonn@google.com24cdf132013-07-30 16:06:12 +00001803PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* dash) {
1804 // TODO(edisonn): verify input
1805 if (!dash || dash->isArray() || dash->size() != 2 || !dash->objAtAIndex(0)->isArray() || !dash->objAtAIndex(1)->isNumber()) {
1806 // TODO(edisonn): report error/warning
1807 return kIgnoreError_PdfResult;
1808 }
1809 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0), dash->objAtAIndex(1));
1810}
1811
1812void skpdfGraphicsStateApplyFont(PdfContext* pdfContext, SkPdfArray* fontAndSize) {
1813 if (!fontAndSize || fontAndSize->isArray() || fontAndSize->size() != 2 || !fontAndSize->objAtAIndex(0)->isName() || !fontAndSize->objAtAIndex(1)->isNumber()) {
1814 // TODO(edisonn): report error/warning
1815 return;
1816 }
1817 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), fontAndSize->objAtAIndex(1)->numberValue());
1818}
1819
1820
1821//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1822static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1823 double lw = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1824 return skpdfGraphicsStateApplyLW(pdfContext, lw);
1825}
1826
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001827//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001828static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001829 int64_t lc = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1830 return skpdfGraphicsStateApplyLC(pdfContext, lc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001831}
1832
1833//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001834static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001835 double lj = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1836 return skpdfGraphicsStateApplyLJ(pdfContext, lj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001837}
1838
1839//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001840static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001841 double ml = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1842 return skpdfGraphicsStateApplyML(pdfContext, ml);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001843}
1844
1845//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1846//page 155).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001847static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001848 SkPdfObject* phase = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1849 SkPdfObject* array = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001850
edisonn@google.com24cdf132013-07-30 16:06:12 +00001851 if (!array->isArray()) {
1852 return kIgnoreError_PdfResult;
1853 }
1854
1855 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)array, phase);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001856}
1857
1858//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001859static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001860 pdfContext->fObjectStack.pop();
1861
1862 return kNYI_PdfResult;
1863}
1864
1865//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1866//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1867//fies the output device’s default flatness tolerance.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001868static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001869 pdfContext->fObjectStack.pop();
1870
1871 return kNYI_PdfResult;
1872}
1873
edisonn@google.come878e722013-07-29 19:10:58 +00001874SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
1875
1876class InitBlendModes {
1877public:
1878 InitBlendModes() {
1879 // TODO(edisonn): use the python code generator?
1880 // TABLE 7.2 Standard separable blend modes
1881 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
1882 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
1883 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
1884 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
1885 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
1886 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
1887 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
1888 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
1889 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
1890 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
1891 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
1892 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
1893
1894 // TABLE 7.3 Standard nonseparable blend modes
1895 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
1896 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
1897 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
1898 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
1899 }
1900};
1901
1902InitBlendModes _gDummyInniter;
1903
1904SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
1905 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1906 if (gPdfBlendModes.find(blendMode, len, &mode)) {
1907 return mode;
1908 }
1909
1910 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1911}
1912
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001913void skpdfGraphicsStateApplyBM_name(PdfContext* pdfContext, const std::string& blendMode) {
edisonn@google.come878e722013-07-29 19:10:58 +00001914 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.length());
1915 if (mode <= SkXfermode::kLastMode) {
1916 pdfContext->fGraphicsState.fBlendModesLength = 1;
1917 pdfContext->fGraphicsState.fBlendModes[0] = mode;
1918 } else {
1919 // TODO(edisonn): report unknown blend mode
1920 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001921}
1922
1923void skpdfGraphicsStateApplyBM_array(PdfContext* pdfContext, SkPdfArray* blendModes) {
edisonn@google.come878e722013-07-29 19:10:58 +00001924 if (!blendModes || blendModes->isArray() || blendModes->size() == 0 || blendModes->size() > 256) {
1925 // TODO(edisonn): report error/warning
1926 return;
1927 }
1928 SkXfermode::Mode modes[256];
1929 int cnt = blendModes->size();
1930 for (int i = 0; i < cnt; i++) {
1931 SkPdfObject* name = blendModes->objAtAIndex(i);
1932 if (!name->isName()) {
1933 // TODO(edisonn): report error/warning
1934 return;
1935 }
1936 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
1937 if (mode > SkXfermode::kLastMode) {
1938 // TODO(edisonn): report error/warning
1939 return;
1940 }
1941 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001942
edisonn@google.come878e722013-07-29 19:10:58 +00001943 pdfContext->fGraphicsState.fBlendModesLength = cnt;
1944 for (int i = 0; i < cnt; i++) {
1945 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
1946 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001947}
1948
1949void skpdfGraphicsStateApplySMask_dict(PdfContext* pdfContext, SkPdfDictionary* sMask) {
1950 // TODO(edisonn): verify input
edisonn@google.come878e722013-07-29 19:10:58 +00001951 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001952 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
edisonn@google.come878e722013-07-29 19:10:58 +00001953 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
1954 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
1955 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
1956 } else {
1957 // TODO (edisonn): report error/warning
1958 }
1959}
1960
1961void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string& sMask) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001962 if (sMask == "None") {
1963 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
1964 pdfContext->fGraphicsState.fSMask = SkBitmap();
1965 return;
1966 }
1967
edisonn@google.come878e722013-07-29 19:10:58 +00001968 //Next, get the ExtGState Dictionary from the Resource Dictionary:
1969 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
1970
1971 if (extGStateDictionary == NULL) {
1972#ifdef PDF_TRACE
1973 printf("ExtGState is NULL!\n");
1974#endif
1975 // TODO (edisonn): report error/warning
1976 return;
1977 }
1978
1979 SkPdfObject* obj = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
1980 if (!obj || !obj->isDictionary()) {
1981 // TODO (edisonn): report error/warning
1982 return;
1983 }
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001984
1985 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
1986 pdfContext->fGraphicsState.fSMask = SkBitmap();
1987
edisonn@google.come878e722013-07-29 19:10:58 +00001988 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001989}
1990
1991void skpdfGraphicsStateApplyAIS(PdfContext* pdfContext, bool alphaSource) {
1992 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
1993}
1994
1995
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001996//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
1997//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001998static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001999 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002000
2001#ifdef PDF_TRACE
2002 std::string str;
2003#endif
2004
2005 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00002006 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002007
2008 if (extGStateDictionary == NULL) {
2009#ifdef PDF_TRACE
2010 printf("ExtGState is NULL!\n");
2011#endif
2012 return kIgnoreError_PdfResult;
2013 }
2014
edisonn@google.com571c70b2013-07-10 17:09:50 +00002015 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002016
edisonn@google.com571c70b2013-07-10 17:09:50 +00002017 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2018 return kIgnoreError_PdfResult;
2019 }
2020 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002021
2022 // TODO(edisonn): now load all those properties in graphic state.
2023 if (gs == NULL) {
2024 return kIgnoreError_PdfResult;
2025 }
2026
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002027 if (gs->has_LW()) {
2028 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2029 }
2030
2031 if (gs->has_LC()) {
2032 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2033 }
2034
2035 if (gs->has_LJ()) {
2036 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2037 }
2038
2039 if (gs->has_ML()) {
2040 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2041 }
2042
2043 if (gs->has_D()) {
2044 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2045 }
2046
2047 if (gs->has_Font()) {
2048 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2049 }
2050
2051 if (gs->has_BM()) {
2052 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2053 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2054 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2055 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2056 } else {
2057 // TODO(edisonn): report/warn
2058 }
2059 }
2060
2061 if (gs->has_SMask()) {
2062 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2063 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2064 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
2065 skpdfGraphicsStateApplySMask_dict(pdfContext, gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
2066 } else {
2067 // TODO(edisonn): report/warn
2068 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002069 }
2070
2071 if (gs->has_ca()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002072 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002073 }
2074
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002075 if (gs->has_CA()) {
2076 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2077 }
2078
2079 if (gs->has_AIS()) {
2080 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002081 }
2082
edisonn@google.com9a43c182013-08-01 20:06:42 +00002083 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002084}
2085
2086//charSpace Tc Set the character spacing, Tc
2087//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2088//Initial value: 0.
2089PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002090 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002091 pdfContext->fGraphicsState.fCharSpace = charSpace;
2092
2093 return kOK_PdfResult;
2094}
2095
2096//wordSpace Tw Set the word spacing, T
2097//w
2098//, to wordSpace, which is a number expressed in unscaled
2099//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2100//value: 0.
2101PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002102 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002103 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2104
2105 return kOK_PdfResult;
2106}
2107
2108//scale Tz Set the horizontal scaling, Th
2109//, to (scale ˜ 100). scale is a number specifying the
2110//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002111static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002112 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002113
2114 return kNYI_PdfResult;
2115}
2116
2117//render Tr Set the text rendering mode, T
2118//mode, to render, which is an integer. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002119static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002120 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002121
2122 return kNYI_PdfResult;
2123}
2124//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2125//units. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002126static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002127 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002128
2129 return kNYI_PdfResult;
2130}
2131
2132//wx wy d0
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002133static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002134 pdfContext->fObjectStack.pop();
2135 pdfContext->fObjectStack.pop();
2136
2137 return kNYI_PdfResult;
2138}
2139
2140//wx wy llx lly urx ury d1
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002141static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002142 pdfContext->fObjectStack.pop();
2143 pdfContext->fObjectStack.pop();
2144 pdfContext->fObjectStack.pop();
2145 pdfContext->fObjectStack.pop();
2146 pdfContext->fObjectStack.pop();
2147 pdfContext->fObjectStack.pop();
2148
2149 return kNYI_PdfResult;
2150}
2151
2152//name sh
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002153static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002154 pdfContext->fObjectStack.pop();
2155
2156 return kNYI_PdfResult;
2157}
2158
2159//name Do
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002160static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002161 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002162
edisonn@google.com571c70b2013-07-10 17:09:50 +00002163 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002164
2165 if (xObject == NULL) {
2166#ifdef PDF_TRACE
2167 printf("XObject is NULL!\n");
2168#endif
2169 return kIgnoreError_PdfResult;
2170 }
2171
edisonn@google.com571c70b2013-07-10 17:09:50 +00002172 SkPdfObject* value = xObject->get(name);
2173 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002174
2175#ifdef PDF_TRACE
2176// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002177// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002178#endif
2179
edisonn@google.com571c70b2013-07-10 17:09:50 +00002180 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002181}
2182
2183//tag MP Designate a marked-content point. tag is a name object indicating the role or
2184//significance of the point.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002185static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002186 pdfContext->fObjectStack.pop();
2187
2188 return kNYI_PdfResult;
2189}
2190
2191//tag properties DP Designate a marked-content point with an associated property list. tag is a
2192//name object indicating the role or significance of the point; properties is
2193//either an inline dictionary containing the property list or a name object
2194//associated with it in the Properties subdictionary of the current resource
2195//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002196static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002197 pdfContext->fObjectStack.pop();
2198 pdfContext->fObjectStack.pop();
2199
2200 return kNYI_PdfResult;
2201}
2202
2203//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2204//tag is a name object indicating the role or significance of the sequence.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002205static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002206 pdfContext->fObjectStack.pop();
2207
2208 return kNYI_PdfResult;
2209}
2210
2211//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2212//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2213//property list or a name object associated with it in the Properties subdictionary of the current resource dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002214static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002215 pdfContext->fObjectStack.pop();
2216 pdfContext->fObjectStack.pop();
2217
2218 return kNYI_PdfResult;
2219}
2220
2221//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002222static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002223 return kNYI_PdfResult;
2224}
2225
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002226static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002227 static bool gInitialized = false;
2228 if (gInitialized) {
2229 return;
2230 }
2231
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002232 gPdfOps.set("q", PdfOp_q);
2233 gPdfOps.set("Q", PdfOp_Q);
2234 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002235
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002236 gPdfOps.set("TD", PdfOp_TD);
2237 gPdfOps.set("Td", PdfOp_Td);
2238 gPdfOps.set("Tm", PdfOp_Tm);
2239 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002240
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002241 gPdfOps.set("m", PdfOp_m);
2242 gPdfOps.set("l", PdfOp_l);
2243 gPdfOps.set("c", PdfOp_c);
2244 gPdfOps.set("v", PdfOp_v);
2245 gPdfOps.set("y", PdfOp_y);
2246 gPdfOps.set("h", PdfOp_h);
2247 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002248
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002249 gPdfOps.set("S", PdfOp_S);
2250 gPdfOps.set("s", PdfOp_s);
2251 gPdfOps.set("f", PdfOp_f);
2252 gPdfOps.set("F", PdfOp_F);
2253 gPdfOps.set("f*", PdfOp_f_star);
2254 gPdfOps.set("B", PdfOp_B);
2255 gPdfOps.set("B*", PdfOp_B_star);
2256 gPdfOps.set("b", PdfOp_b);
2257 gPdfOps.set("b*", PdfOp_b_star);
2258 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002259
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002260 gPdfOps.set("BT", PdfOp_BT);
2261 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002262
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002263 gPdfOps.set("Tj", PdfOp_Tj);
2264 gPdfOps.set("'", PdfOp_quote);
2265 gPdfOps.set("\"", PdfOp_doublequote);
2266 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002267
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002268 gPdfOps.set("CS", PdfOp_CS);
2269 gPdfOps.set("cs", PdfOp_cs);
2270 gPdfOps.set("SC", PdfOp_SC);
2271 gPdfOps.set("SCN", PdfOp_SCN);
2272 gPdfOps.set("sc", PdfOp_sc);
2273 gPdfOps.set("scn", PdfOp_scn);
2274 gPdfOps.set("G", PdfOp_G);
2275 gPdfOps.set("g", PdfOp_g);
2276 gPdfOps.set("RG", PdfOp_RG);
2277 gPdfOps.set("rg", PdfOp_rg);
2278 gPdfOps.set("K", PdfOp_K);
2279 gPdfOps.set("k", PdfOp_k);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002280
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002281 gPdfOps.set("W", PdfOp_W);
2282 gPdfOps.set("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002283
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002284 gPdfOps.set("BX", PdfOp_BX);
2285 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002286
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002287 gPdfOps.set("BI", PdfOp_BI);
2288 gPdfOps.set("ID", PdfOp_ID);
2289 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002290
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002291 gPdfOps.set("w", PdfOp_w);
2292 gPdfOps.set("J", PdfOp_J);
2293 gPdfOps.set("j", PdfOp_j);
2294 gPdfOps.set("M", PdfOp_M);
2295 gPdfOps.set("d", PdfOp_d);
2296 gPdfOps.set("ri", PdfOp_ri);
2297 gPdfOps.set("i", PdfOp_i);
2298 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002299
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002300 gPdfOps.set("Tc", PdfOp_Tc);
2301 gPdfOps.set("Tw", PdfOp_Tw);
2302 gPdfOps.set("Tz", PdfOp_Tz);
2303 gPdfOps.set("TL", PdfOp_TL);
2304 gPdfOps.set("Tf", PdfOp_Tf);
2305 gPdfOps.set("Tr", PdfOp_Tr);
2306 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002307
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002308 gPdfOps.set("d0", PdfOp_d0);
2309 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002310
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002311 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002312
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002313 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002314
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002315 gPdfOps.set("MP", PdfOp_MP);
2316 gPdfOps.set("DP", PdfOp_DP);
2317 gPdfOps.set("BMC", PdfOp_BMC);
2318 gPdfOps.set("BDC", PdfOp_BDC);
2319 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002320
2321 gInitialized = true;
2322}
2323
2324class InitPdfOps {
2325public:
2326 InitPdfOps() {
2327 initPdfOperatorRenderes();
2328 }
2329};
2330
2331InitPdfOps gInitPdfOps;
2332
2333void reportPdfRenderStats() {
2334 std::map<std::string, int>::iterator iter;
2335
2336 for (int i = 0 ; i < kCount_PdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002337 SkTDict<int>::Iter iter(gRenderStats[i]);
2338 const char* key;
2339 int value = 0;
2340 while ((key = iter.next(&value)) != NULL) {
2341 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002342 }
2343 }
2344}
2345
2346PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002347 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002348 {
2349 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002350 PdfOperatorRenderer pdfOperatorRenderer = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002351 if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) && pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002352 // caller, main work is done by pdfOperatorRenderer(...)
2353 PdfTokenLooper* childLooper = NULL;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002354 PdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
2355
2356 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002357 gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
2358 gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002359
2360 if (childLooper) {
2361 childLooper->setUp(this);
2362 childLooper->loop();
2363 delete childLooper;
2364 }
2365 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002366 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002367 gRenderStats[kUnsupported_PdfResult].find(token.fKeyword, token.fKeywordLength, &cnt);
2368 gRenderStats[kUnsupported_PdfResult].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002369 }
2370 }
2371 else if (token.fType == kObject_TokenType)
2372 {
2373 fPdfContext->fObjectStack.push( token.fObject );
2374 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002375 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002376 // TODO(edisonn): deine or use assert not reached
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002377 return kIgnoreError_PdfResult;
2378 }
2379 return kOK_PdfResult;
2380}
2381
2382void PdfMainLooper::loop() {
2383 PdfToken token;
2384 while (readToken(fTokenizer, &token)) {
2385 consumeToken(token);
2386 }
2387}
2388
2389PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002390 SkASSERT(false);
2391 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002392}
2393
2394void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002395 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002396}
2397
2398PdfResult PdfInlineImageLooper::done() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002399 return kNYI_PdfResult;
2400}
2401
2402PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2403 return fParent->consumeToken(token);
2404}
2405
2406void PdfCompatibilitySectionLooper::loop() {
2407 // TODO(edisonn): save stacks position, or create a new stack?
2408 // TODO(edisonn): what happens if we pop out more variables then when we started?
2409 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2410 // pop-ing too much will not affect outside the section.
2411 PdfToken token;
2412 while (readToken(fTokenizer, &token)) {
2413 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2414 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2415 looper->setUp(this);
2416 looper->loop();
2417 delete looper;
2418 } else {
2419 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
2420 fParent->consumeToken(token);
2421 }
2422 }
2423 // TODO(edisonn): restore stack.
2424}
2425
2426// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2427// TODO(edisonn): Add API for Forms viewing and editing
2428// e.g. SkBitmap getPage(int page);
2429// int formsCount();
2430// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2431// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2432// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2433// resources needed, so the preview is fast.
2434// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2435// references automatically.
2436
edisonn@google.com222382b2013-07-10 22:33:10 +00002437PdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00002438
edisonn@google.com444e25a2013-07-11 15:20:50 +00002439bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00002440 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002441 return false;
2442 }
2443
edisonn@google.com222382b2013-07-10 22:33:10 +00002444 if (page < 0 || page >= pages()) {
2445 return false;
2446 }
2447
edisonn@google.com222382b2013-07-10 22:33:10 +00002448 PdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002449
edisonn@google.com222382b2013-07-10 22:33:10 +00002450 pdfContext.fOriginalMatrix = SkMatrix::I();
2451 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2452
2453 gPdfContext = &pdfContext;
2454
2455 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00002456 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00002457 SkScalar w = dst.width();
2458 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00002459
edisonn@google.com444e25a2013-07-11 15:20:50 +00002460 SkScalar wp = fPdfDoc->MediaBox(page).width();
2461 SkScalar hp = fPdfDoc->MediaBox(page).height();
2462
2463 SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(wp, z), SkPoint::Make(wp, hp), SkPoint::Make(z, hp)};
edisonn@google.com222382b2013-07-10 22:33:10 +00002464// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2465
2466 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2467 // TODO(edisonn): add flagg for no clipping.
2468 // Use larger image to make sure we do not draw anything outside of page
2469 // could be used in tests.
2470
2471#ifdef PDF_DEBUG_3X
2472 SkPoint skiaSpace[4] = {SkPoint::Make(w+z, h+h), SkPoint::Make(w+w, h+h), SkPoint::Make(w+w, h+z), SkPoint::Make(w+z, h+z)};
2473#else
2474 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2475#endif
2476 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2477 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2478
2479 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2480 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2481
2482 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2483 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2484
2485 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2486 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2487
2488
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002489 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
2490 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
2491 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
edisonn@google.com222382b2013-07-10 22:33:10 +00002492
edisonn@google.com222382b2013-07-10 22:33:10 +00002493#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00002494 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00002495#endif
2496
edisonn@google.com444e25a2013-07-11 15:20:50 +00002497 canvas->setMatrix(pdfContext.fOriginalMatrix);
2498
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002499 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2500
2501 // TODO(edisonn:) erase with white before draw?
edisonn@google.com222382b2013-07-10 22:33:10 +00002502// SkPaint paint;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002503// paint.setColor(SK_ColorWHITE);
edisonn@google.com222382b2013-07-10 22:33:10 +00002504// canvas->drawRect(rect, paint);
2505
edisonn@google.com222382b2013-07-10 22:33:10 +00002506
2507 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002508 return true;
2509}
edisonn@google.com222382b2013-07-10 22:33:10 +00002510
2511bool SkPdfRenderer::load(const SkString inputFileName) {
2512 unload();
2513
2514 // TODO(edisonn): create static function that could return NULL if there are errors
2515 fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00002516 if (fPdfDoc->pages() == 0) {
2517 delete fPdfDoc;
2518 fPdfDoc = NULL;
2519 }
edisonn@google.com222382b2013-07-10 22:33:10 +00002520
2521 return fPdfDoc != NULL;
2522}
2523
edisonn@google.com147adb12013-07-24 15:56:19 +00002524bool SkPdfRenderer::load(SkStream* stream) {
2525 unload();
2526
2527 // TODO(edisonn): create static function that could return NULL if there are errors
2528 fPdfDoc = new SkNativeParsedPDF(stream);
2529 if (fPdfDoc->pages() == 0) {
2530 delete fPdfDoc;
2531 fPdfDoc = NULL;
2532 }
2533
2534 return fPdfDoc != NULL;
2535}
2536
2537
edisonn@google.com222382b2013-07-10 22:33:10 +00002538int SkPdfRenderer::pages() const {
2539 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2540}
2541
2542void SkPdfRenderer::unload() {
2543 delete fPdfDoc;
2544 fPdfDoc = NULL;
2545}
2546
2547SkRect SkPdfRenderer::MediaBox(int page) const {
2548 SkASSERT(fPdfDoc);
2549 return fPdfDoc->MediaBox(page);
2550}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002551
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002552size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002553 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2554}
edisonn@google.com147adb12013-07-24 15:56:19 +00002555
2556bool SkPDFNativeRenderToBitmap(SkStream* stream,
2557 SkBitmap* output,
2558 int page,
2559 SkPdfContent content,
2560 double dpi) {
2561 SkASSERT(page >= 0);
2562 SkPdfRenderer renderer;
2563 renderer.load(stream);
2564 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
2565 return false;
2566 }
2567
2568 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
2569
2570 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2571 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2572
2573 rect = SkRect::MakeWH(width, height);
2574
2575 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
2576
2577 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*output)));
2578 SkCanvas canvas(device);
2579
2580 return renderer.renderPage(page, &canvas, rect);
2581}