blob: 4d9b9647482c5a954e9c54b8d00bc22ce4458660 [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;
edisonn@google.com91ce6982013-08-05 20:45:40 +0000268 while ((elem = iter.next()) != NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000269 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 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000314#endif
315
316 return ret;
317}
318
319
320
321typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
322
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000323SkTDict<PdfOperatorRenderer> gPdfOps(100);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000324
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000325
326template <typename T> class SkTDictWithDefaultConstructor : public SkTDict<T> {
327public:
328 SkTDictWithDefaultConstructor() : SkTDict<T>(10) {}
329};
330
331SkTDictWithDefaultConstructor<int> gRenderStats[kCount_PdfResult];
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000332
edisonn@google.com571c70b2013-07-10 17:09:50 +0000333const char* gRenderStatsNames[kCount_PdfResult] = {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000334 "Success",
335 "Partially implemented",
336 "Not yet implemented",
337 "Ignore Error",
338 "Error",
339 "Unsupported/Unknown"
340};
341
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000342static PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000343 const SkPdfObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000344 SkCanvas* canvas)
345{
346
347 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
348 if (skfont == NULL) {
349 skfont = SkPdfFont::Default();
350 }
351
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000352
edisonn@google.com571c70b2013-07-10 17:09:50 +0000353 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000354 // TODO(edisonn): report warning
355 return kIgnoreError_PdfResult;
356 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000357 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000358
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000359 SkUnencodedText binary(str);
360
361 SkDecodedText decoded;
362
363 if (skfont->encoding() == NULL) {
364 // TODO(edisonn): report warning
365 return kNYI_PdfResult;
366 }
367
368 skfont->encoding()->decodeText(binary, &decoded);
369
370 SkPaint paint;
371 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
372 // Or maybe just not call setTextSize at all?
373 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
374 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
375 }
376
377// if (fCurFont && fCurFont->GetFontScale() != 0) {
378// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
379// }
380
381 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
382
383 canvas->save();
384
385#if 1
386 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
387
388 SkPoint point1;
389 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
390
391 SkMatrix mirror;
392 mirror.setTranslate(0, -point1.y());
393 // TODO(edisonn): fix rotated text, and skewed too
394 mirror.postScale(SK_Scalar1, -SK_Scalar1);
395 // TODO(edisonn): post rotate, skew
396 mirror.postTranslate(0, point1.y());
397
398 matrix.postConcat(mirror);
399
400 canvas->setMatrix(matrix);
401
402 SkTraceMatrix(matrix, "mirrored");
403#endif
404
edisonn@google.com6e49c342013-06-27 20:03:43 +0000405 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000406 canvas->restore();
407
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000408 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000409}
410
411// TODO(edisonn): create header files with declarations!
412PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
413PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
414PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
415PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
416
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000417// TODO(edisonn): perf!!!
418
419static SkColorTable* getGrayColortable() {
420 static SkColorTable* grayColortable = NULL;
421 if (grayColortable == NULL) {
422 SkPMColor* colors = new SkPMColor[256];
423 for (int i = 0 ; i < 256; i++) {
424 colors[i] = SkPreMultiplyARGB(255, i, i, i);
425 }
426 grayColortable = new SkColorTable(colors, 256);
427 }
428 return grayColortable;
429}
430
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000431static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream, size_t uncompressedStreamLength,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000432 int width, int height, int bytesPerLine,
433 int bpc, const std::string& colorSpace,
434 bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000435 SkBitmap* bitmap = new SkBitmap();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000436
edisonn@google.com571c70b2013-07-10 17:09:50 +0000437 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000438//#define MAX_COMPONENTS 10
439
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000440 // TODO(edisonn): assume start of lines are aligned at 32 bits?
441 // Is there a faster way to load the uncompressed stream into a bitmap?
442
443 // minimal support for now
444 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
445 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
446
447 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000448 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000449 for (int w = 0 ; w < width; w++) {
450 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
451 uncompressedStream[3 * w + 1],
452 uncompressedStream[3 * w + 2]);
453 i++;
454 }
455 uncompressedStream += bytesPerLine;
456 }
457
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000458 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
459 bitmap->setPixels(uncompressedStreamArgb);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000460 }
461 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
462 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
463
464 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000465 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000466 for (int w = 0 ; w < width; w++) {
467 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
468 uncompressedStream[w];
469 i++;
470 }
471 uncompressedStream += bytesPerLine;
472 }
473
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000474 bitmap->setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000475 width, height);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000476 bitmap->setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000477 }
478
479 // TODO(edisonn): Report Warning, NYI, or error
480 return bitmap;
481}
482
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000483// utils
484
485// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
486// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
487// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
488// skia format, through a table
489
490// this functions returns the image, it does not look at the smask.
491
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000492static SkBitmap* getImageFromObjectCore(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000493 if (image == NULL || !image->hasStream()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000494 // TODO(edisonn): report warning to be used in testing.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000495 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000496 }
497
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000498 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc);
499 int64_t width = image->Width(pdfContext->fPdfDoc);
500 int64_t height = image->Height(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000501 std::string colorSpace = "DeviceRGB";
502
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000503 bool indexed = false;
504 SkPMColor colors[256];
505 int cnt = 0;
506
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000507 // TODO(edisonn): color space can be an array too!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000508 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
509 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000510 } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) {
511 SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc);
512 if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") &&
513 (array->objAtAIndex(1)->isName("DeviceRGB") || array->objAtAIndex(1)->isName("RGB")) &&
514 array->objAtAIndex(2)->isInteger() &&
515 array->objAtAIndex(3)->isHexString()
516 ) {
517 // TODO(edisonn): suport only DeviceRGB for now.
518 indexed = true;
519 cnt = array->objAtAIndex(2)->intValue() + 1;
520 if (cnt > 256) {
521 // TODO(edionn): report NYIs
522 return NULL;
523 }
524 SkColorTable colorTable(cnt);
525 NotOwnedString data = array->objAtAIndex(3)->strRef();
526 if (data.fBytes != (unsigned int)cnt * 3) {
527 // TODO(edionn): report error/warning
528 return NULL;
529 }
530 for (int i = 0 ; i < cnt; i++) {
531 colors[i] = SkPreMultiplyARGB(0xff, data.fBuffer[3 * i], data.fBuffer[3 * i + 1], data.fBuffer[3 * i + 2]);
532 }
533 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000534 }
535
536/*
537 bool imageMask = image->imageMask();
538
539 if (imageMask) {
540 if (bpc != 0 && bpc != 1) {
541 // TODO(edisonn): report warning to be used in testing.
542 return SkBitmap();
543 }
544 bpc = 1;
545 }
546*/
547
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000548 const unsigned char* uncompressedStream = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000549 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000550
edisonn@google.com571c70b2013-07-10 17:09:50 +0000551 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000552
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000553 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000554 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000555 // TODO(edisonn): report warning to be used in testing.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000556 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000557 }
558
edisonn@google.com571c70b2013-07-10 17:09:50 +0000559 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
560
561 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
562 streamDict->getFilterAsName(NULL) == "DCTDecode") ||
563 (streamDict->isFilterAArray(NULL) &&
564 streamDict->getFilterAsArray(NULL)->size() > 0 &&
565 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
566 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() == "DCTDecode"))) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000567 SkBitmap* bitmap = new SkBitmap();
568 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000569 return bitmap;
570 }
571
572
573
574 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
575// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
576// obj.GetDictionary().GetKey(PdfName("Filter")));
577// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
578// value = resolveReferenceObject(pdfContext->fPdfDoc,
579// &value->GetArray()[0]);
580// }
581// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
582// SkStream stream = SkStream::
583// SkImageDecoder::Factory()
584// }
585
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000586 // TODO(edisonn): assumes RGB for now, since it is the only onwe implemented
587 if (indexed) {
588 SkBitmap* bitmap = new SkBitmap();
589 bitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
590 SkColorTable* colorTable = new SkColorTable(colors, cnt);
591 bitmap->setPixels((void*)uncompressedStream, colorTable);
592 return bitmap;
593 }
594
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000595 int bytesPerLine = (int)(uncompressedStreamLength / height);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000596#ifdef PDF_TRACE
597 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000598 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000599 }
600#endif
601
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000602 SkBitmap* bitmap = transferImageStreamToBitmap(
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000603 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000604 (int)width, (int)height, bytesPerLine,
605 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000606 transparencyMask);
607
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000608 return bitmap;
609}
610
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000611static SkBitmap* getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
612 if (!transparencyMask) {
613 if (!image->hasData(SkPdfObject::kBitmap_Data)) {
614 SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask);
615 image->setData(bitmap, SkPdfObject::kBitmap_Data);
616 }
617 return (SkBitmap*) image->data(SkPdfObject::kBitmap_Data);
618 } else {
619 return getImageFromObjectCore(pdfContext, image, transparencyMask);
620 }
621}
622
623static SkBitmap* getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000624 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000625
626 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000627 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000628 }
629
630 // TODO(edisonn): implement GS SMask. Default to empty right now.
631 return pdfContext->fGraphicsState.fSMask;
632}
633
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000634static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000635 if (skpdfimage == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000636 return kIgnoreError_PdfResult;
637 }
638
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000639 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
640 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000641
642 canvas->save();
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000643 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000644
645#if 1
646 SkScalar z = SkIntToScalar(0);
647 SkScalar one = SkIntToScalar(1);
648
649 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
650 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
651 SkMatrix flip;
652 SkAssertResult(flip.setPolyToPoly(from, to, 4));
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000653 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000654 solveImageFlip.preConcat(flip);
655 canvas->setMatrix(solveImageFlip);
656#endif
657
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000658 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
659
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000660 // TODO(edisonn): soft mask type? alpha/luminosity.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000661 if (!sMask || sMask->empty()) {
662 canvas->drawBitmapRect(*image, dst, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000663 } else {
664 canvas->saveLayer(&dst, NULL);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000665 canvas->drawBitmapRect(*image, dst, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000666 SkPaint xfer;
667 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000668 // TODO(edisonn): is the blend mode specified already implicitly/explicitly in pdf?
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000669 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000670 canvas->drawBitmapRect(*sMask, dst, &xfer);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000671 canvas->restore();
672 }
673
674 canvas->restore();
675
676 return kPartial_PdfResult;
677}
678
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000679//TODO(edisonn): options for implementing isolation and knockout
680// 1) emulate them (current solution)
681// PRO: simple
682// CON: will need to use readPixels, which means serious perf issues
683// 2) Compile a plan for an array of matrixes, compose the result at the end
684// PRO: might be faster then 1, no need to readPixels
685// CON: multiple drawings (but on smaller areas), pay a price at loading pdf to compute a pdf draw plan
686// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
687// 3) support them natively in SkCanvas
688// PRO: simple
689// CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1)
690// 4) compile a plan using pathops, and render once without any fancy rules with backdrop
691// PRO: simple, fast
692// CON: pathops must be bug free first + time to compute new paths
693// pay a price at loading pdf to compute a pdf draw plan
694// 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 +0000695// 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 +0000696
697
698// TODO(edisonn): draw plan from point! - list of draw ops of a point, like a tree!
699// TODO(edisonn): Minimal PDF to draw some points - remove everything that it is not needed, save pdf uncompressed
700
701
702
703static void doGroup_before(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup, bool page) {
704 SkRect bboxOrig = bbox;
705 SkBitmap backdrop;
706 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
707// bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000708 SkPaint paint;
709 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
710 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000711}
712
edisonn@google.com251176e2013-08-01 13:24:00 +0000713// TODO(edisonn): non isolation implemented in skia
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000714//static void doGroup_after(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup) {
edisonn@google.com251176e2013-08-01 13:24:00 +0000715// if not isolated
716// canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000717//}
718
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000719static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000720 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000721 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000722 }
723
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000724 if (!skobj->has_BBox()) {
725 return kIgnoreError_PdfResult;
726 }
727
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000728 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000729
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000730 canvas->save();
731
732
edisonn@google.com571c70b2013-07-10 17:09:50 +0000733 if (skobj->Resources(pdfContext->fPdfDoc)) {
734 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000735 }
736
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000737 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000738
edisonn@google.com571c70b2013-07-10 17:09:50 +0000739 if (skobj->has_Matrix()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000740 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
741 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
742 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000743 // TODO(edisonn) reset matrixTm and matricTlm also?
744 }
745
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000746 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000747
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000748 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000749
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000750 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
751 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000752
753 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
754 // For this PdfContentsTokenizer needs to be extended.
755
edisonn@google.come878e722013-07-29 19:10:58 +0000756 // This is a group?
757 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000758 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
759 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
edisonn@google.come878e722013-07-29 19:10:58 +0000760 }
761
edisonn@google.com571c70b2013-07-10 17:09:50 +0000762 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000763
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000764 SkPdfNativeTokenizer* tokenizer =
765 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000766 if (tokenizer != NULL) {
767 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
768 looper.loop();
769 delete tokenizer;
770 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000771
772 // TODO(edisonn): should we restore the variable stack at the same state?
773 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000774
775 if (skobj->has_Group()) {
776 canvas->restore();
777 }
778
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000779 canvas->restore();
780 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000781 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000782}
783
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000784
785// TODO(edisonn): Extract a class like ObjWithStream
786static PdfResult doXObject_Pattern(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1PatternDictionary* skobj) {
787 if (!skobj || !skobj->hasStream()) {
788 return kIgnoreError_PdfResult;
789 }
790
791 if (!skobj->has_BBox()) {
792 return kIgnoreError_PdfResult;
793 }
794
795 PdfOp_q(pdfContext, canvas, NULL);
796
797 canvas->save();
798
799
800 if (skobj->Resources(pdfContext->fPdfDoc)) {
801 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
802 }
803
804 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
805
806 if (skobj->has_Matrix()) {
807 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
808 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
809 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
810 // TODO(edisonn) reset matrixTm and matricTlm also?
811 }
812
813 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
814
815 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
816
817 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
818 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
819
820 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
821 // For this PdfContentsTokenizer needs to be extended.
822
823 SkPdfStream* stream = (SkPdfStream*)skobj;
824
825 SkPdfNativeTokenizer* tokenizer =
826 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
827 if (tokenizer != NULL) {
828 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
829 looper.loop();
830 delete tokenizer;
831 }
832
833 // TODO(edisonn): should we restore the variable stack at the same state?
834 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
835
836 canvas->restore();
837 PdfOp_Q(pdfContext, canvas, NULL);
838 return kPartial_PdfResult;
839}
840
841
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000842//static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
843// return kNYI_PdfResult;
844//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000845
edisonn@google.com571c70b2013-07-10 17:09:50 +0000846PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
847 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000848 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000849 }
850
851 PdfOp_q(pdfContext, canvas, NULL);
852 canvas->save();
853
854 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
855 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
856
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000857 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
858 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000859
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000860 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000861
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000862 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000863
864 SkRect rm = bBox;
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000865 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000866
867 SkTraceRect(rm, "bbox mapped");
868
869 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
870
871 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
872 // For this PdfContentsTokenizer needs to be extended.
873
edisonn@google.com571c70b2013-07-10 17:09:50 +0000874 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000875
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000876 SkPdfNativeTokenizer* tokenizer =
877 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000878 if (tokenizer != NULL) {
879 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
880 looper.loop();
881 delete tokenizer;
882 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000883
884 // TODO(edisonn): should we restore the variable stack at the same state?
885 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
886 canvas->restore();
887 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000888
889 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000890}
891
892
edisonn@google.com571c70b2013-07-10 17:09:50 +0000893// TODO(edisonn): make sure the pointer is unique
894std::set<const SkPdfObject*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000895
896class CheckRecursiveRendering {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000897 const SkPdfObject* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000898public:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000899 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000900 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000901 }
902
903 ~CheckRecursiveRendering() {
904 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000905 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000906 }
907
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000908 static bool IsInRendering(const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000909 return gInRendering.find(obj) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000910 }
911};
912
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000913static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000914 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000915 // Oops, corrupt PDF!
916 return kIgnoreError_PdfResult;
917 }
918
edisonn@google.com571c70b2013-07-10 17:09:50 +0000919 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000920
edisonn@google.com571c70b2013-07-10 17:09:50 +0000921 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000922 {
923 case kImageDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000924 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000925 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000926 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000927 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
928 //return doXObject_PS(skxobj.asPS());
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000929 default: {
930 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != kNone_SkPdfObjectType) {
931 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
932 return doXObject_Pattern(pdfContext, canvas, pattern);
933 }
934 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000935 }
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000936 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000937}
938
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000939static PdfResult doPage(PdfContext* pdfContext, SkCanvas* canvas, SkPdfPageObjectDictionary* skobj) {
940 if (!skobj) {
941 return kIgnoreError_PdfResult;
942 }
943
944 if (!skobj->isContentsAStream(pdfContext->fPdfDoc)) {
945 return kNYI_PdfResult;
946 }
947
948 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
949
950 if (!stream) {
951 return kIgnoreError_PdfResult;
952 }
953
954 if (CheckRecursiveRendering::IsInRendering(skobj)) {
955 // Oops, corrupt PDF!
956 return kIgnoreError_PdfResult;
957 }
958 CheckRecursiveRendering checkRecursion(skobj);
959
960
961 PdfOp_q(pdfContext, canvas, NULL);
962
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000963
964 if (skobj->Resources(pdfContext->fPdfDoc)) {
965 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
966 }
967
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000968 // TODO(edisonn): MediaBox can be inherited!!!!
969 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000970 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000971 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
972 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
973 } else {
974 canvas->save();
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000975 }
976
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000977
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000978 SkPdfNativeTokenizer* tokenizer =
979 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
980 if (tokenizer != NULL) {
981 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
982 looper.loop();
983 delete tokenizer;
984 }
985
986 // TODO(edisonn): should we restore the variable stack at the same state?
987 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
988 canvas->restore();
989 PdfOp_Q(pdfContext, canvas, NULL);
990 return kPartial_PdfResult;
991}
992
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000993PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
994 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
995 canvas->save();
996 return kOK_PdfResult;
997}
998
999PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1000 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1001 pdfContext->fStateStack.pop();
1002 canvas->restore();
1003 return kOK_PdfResult;
1004}
1005
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001006static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001007 double array[6];
1008 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001009 array[5 - i] = pdfContext->fObjectStack.top()->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001010 pdfContext->fObjectStack.pop();
1011 }
1012
1013 // a b
1014 // c d
1015 // e f
1016
1017 // 0 1
1018 // 2 3
1019 // 4 5
1020
1021 // sx ky
1022 // kx sy
1023 // tx ty
1024 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1025
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001026 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001027
1028#ifdef PDF_TRACE
1029 printf("cm ");
1030 for (int i = 0 ; i < 6 ; i++) {
1031 printf("%f ", array[i]);
1032 }
1033 printf("\n");
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001034 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001035#endif
1036
1037 return kOK_PdfResult;
1038}
1039
1040//leading TL Set the text leading, Tl
1041//, to leading, which is a number expressed in unscaled text
1042//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001043static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001044 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001045
1046 pdfContext->fGraphicsState.fTextLeading = ty;
1047
1048 return kOK_PdfResult;
1049}
1050
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001051static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001052#ifdef PDF_TRACE
1053 printf("stack size = %i\n", (int)pdfContext->fObjectStack.size());
1054#endif
edisonn@google.com571c70b2013-07-10 17:09:50 +00001055 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001056 SkPdfObject* obj = pdfContext->fObjectStack.top();
1057 obj = obj;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001058 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001059
1060 double array[6] = {1, 0, 0, 1, tx, ty};
1061 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1062
1063 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1064 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1065
1066 return kPartial_PdfResult;
1067}
1068
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001069static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001070 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1071 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001072
edisonn@google.com571c70b2013-07-10 17:09:50 +00001073 // TODO(edisonn): Create factory methods or constructors so native is hidden
1074 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001075 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001076
1077 PdfOp_TL(pdfContext, canvas, looper);
1078
edisonn@google.com571c70b2013-07-10 17:09:50 +00001079 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001080 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001081
edisonn@google.com571c70b2013-07-10 17:09:50 +00001082 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001083 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001084
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001085 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1086
1087 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
1088 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001089
1090 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001091}
1092
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001093static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001094 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1095 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1096 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1097 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1098 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1099 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001100
1101 double array[6];
1102 array[0] = a;
1103 array[1] = b;
1104 array[2] = c;
1105 array[3] = d;
1106 array[4] = e;
1107 array[5] = f;
1108
1109 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001110 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001111
1112 // TODO(edisonn): Text positioning.
1113 pdfContext->fGraphicsState.fMatrixTm = matrix;
1114 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1115
1116 return kPartial_PdfResult;
1117}
1118
1119//— T* Move to the start of the next line. This operator has the same effect as the code
1120//0 Tl Td
1121//where Tl is the current leading parameter in the text state
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001122static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001123 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
1124 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001125
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001126 pdfContext->fObjectStack.push(zero);
1127 pdfContext->fObjectStack.push(tl);
1128
1129 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1130
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001131 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001132}
1133
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001134static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001135 if (pdfContext->fGraphicsState.fPathClosed) {
1136 pdfContext->fGraphicsState.fPath.reset();
1137 pdfContext->fGraphicsState.fPathClosed = false;
1138 }
1139
edisonn@google.com571c70b2013-07-10 17:09:50 +00001140 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1141 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001142
1143 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1144 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1145
1146 return kOK_PdfResult;
1147}
1148
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001149static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001150 if (pdfContext->fGraphicsState.fPathClosed) {
1151 pdfContext->fGraphicsState.fPath.reset();
1152 pdfContext->fGraphicsState.fPathClosed = false;
1153 }
1154
edisonn@google.com571c70b2013-07-10 17:09:50 +00001155 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1156 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001157
1158 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1159 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1160
1161 return kOK_PdfResult;
1162}
1163
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001164static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001165 if (pdfContext->fGraphicsState.fPathClosed) {
1166 pdfContext->fGraphicsState.fPath.reset();
1167 pdfContext->fGraphicsState.fPathClosed = false;
1168 }
1169
edisonn@google.com571c70b2013-07-10 17:09:50 +00001170 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1171 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1172 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1173 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1174 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1175 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001176
1177 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1178 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1179 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1180
1181 pdfContext->fGraphicsState.fCurPosX = x3;
1182 pdfContext->fGraphicsState.fCurPosY = y3;
1183
1184 return kOK_PdfResult;
1185}
1186
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001187static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001188 if (pdfContext->fGraphicsState.fPathClosed) {
1189 pdfContext->fGraphicsState.fPath.reset();
1190 pdfContext->fGraphicsState.fPathClosed = false;
1191 }
1192
edisonn@google.com571c70b2013-07-10 17:09:50 +00001193 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1194 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1195 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1196 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001197 double y1 = pdfContext->fGraphicsState.fCurPosY;
1198 double x1 = pdfContext->fGraphicsState.fCurPosX;
1199
1200 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1201 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1202 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1203
1204 pdfContext->fGraphicsState.fCurPosX = x3;
1205 pdfContext->fGraphicsState.fCurPosY = y3;
1206
1207 return kOK_PdfResult;
1208}
1209
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001210static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001211 if (pdfContext->fGraphicsState.fPathClosed) {
1212 pdfContext->fGraphicsState.fPath.reset();
1213 pdfContext->fGraphicsState.fPathClosed = false;
1214 }
1215
edisonn@google.com571c70b2013-07-10 17:09:50 +00001216 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1217 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001218 double y2 = pdfContext->fGraphicsState.fCurPosY;
1219 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001220 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1221 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001222
1223 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1224 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1225 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1226
1227 pdfContext->fGraphicsState.fCurPosX = x3;
1228 pdfContext->fGraphicsState.fCurPosY = y3;
1229
1230 return kOK_PdfResult;
1231}
1232
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001233static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001234 if (pdfContext->fGraphicsState.fPathClosed) {
1235 pdfContext->fGraphicsState.fPath.reset();
1236 pdfContext->fGraphicsState.fPathClosed = false;
1237 }
1238
edisonn@google.com571c70b2013-07-10 17:09:50 +00001239 double height = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1240 double width = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1241 double y = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1242 double x = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001243
1244 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1245 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1246
1247 pdfContext->fGraphicsState.fCurPosX = x;
1248 pdfContext->fGraphicsState.fCurPosY = y + height;
1249
1250 return kOK_PdfResult;
1251}
1252
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001253static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001254 pdfContext->fGraphicsState.fPath.close();
1255 return kOK_PdfResult;
1256}
1257
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001258static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001259 SkPath path = pdfContext->fGraphicsState.fPath;
1260
1261 if (close) {
1262 path.close();
1263 }
1264
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001265 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001266
1267 SkPaint paint;
1268
1269 SkPoint line[2];
1270 if (fill && !stroke && path.isLine(line)) {
1271 paint.setStyle(SkPaint::kStroke_Style);
1272
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001273 // TODO(edisonn): implement this with patterns
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001274 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1275 paint.setStrokeWidth(SkDoubleToScalar(0));
1276
1277 canvas->drawPath(path, paint);
1278 } else {
1279 if (fill) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001280 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0 &&
1281 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1282
1283 // TODO(edisonn): we can use a shader here, like imageshader to draw fast. ultimately,
1284 // if this is not possible, and we are in rasper mode, and the cells don't intersect, we could even have multiple cpus.
1285
1286 canvas->save();
1287 PdfOp_q(pdfContext, canvas, NULL);
1288
1289 if (evenOdd) {
1290 path.setFillType(SkPath::kEvenOdd_FillType);
1291 }
1292 canvas->clipPath(path);
1293
1294 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(pdfContext->fGraphicsState.fNonStroking.fPattern) != kNone_SkPdfObjectType) {
1295 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState.fNonStroking.fPattern;
1296
1297 // TODO(edisonn): constants
1298 // TODO(edisonn): colored
1299 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001300 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
1301 // it will change the result iterating in reverse
1302 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
1303 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001304
1305 SkRect bounds = path.getBounds();
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001306
1307 // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001308 // TODO(edisonn): don't do that!
1309 bounds.sort();
1310
1311 SkScalar x;
1312 SkScalar y;
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001313
1314 y = bounds.top();
1315 int totalx = 0;
1316 int totaly = 0;
1317 while (y < bounds.bottom()) {
1318 x = bounds.left();
1319 totalx = 0;
1320
1321 while (x < bounds.right()) {
1322 doXObject(pdfContext, canvas, pattern);
1323
1324 pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(xStep), SkIntToScalar(0));
1325 totalx += xStep;
1326 x += SkIntToScalar(xStep);
1327 }
1328 pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(-totalx), SkIntToScalar(0));
1329
1330 pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(0), SkIntToScalar(-yStep));
1331 totaly += yStep;
1332 y += SkIntToScalar(yStep);
1333 }
1334 pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(0), SkIntToScalar(totaly));
1335 }
1336 }
1337
1338 // apply matrix
1339 // get xstep, y step, bbox ... for cliping, and bos of the path
1340
1341 PdfOp_Q(pdfContext, canvas, NULL);
1342 canvas->restore();
1343 } else {
1344 paint.setStyle(SkPaint::kFill_Style);
1345 if (evenOdd) {
1346 path.setFillType(SkPath::kEvenOdd_FillType);
1347 }
1348
1349 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1350
1351 canvas->drawPath(path, paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001352 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001353 }
1354
1355 if (stroke) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001356 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0) {
1357 // TODO(edisonn): implement Pattern for strokes
1358 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001359
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001360 paint.setColor(SK_ColorGREEN);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001361
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001362 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1363 canvas->drawPath(path, paint);
1364 } else {
1365 paint.setStyle(SkPaint::kStroke_Style);
1366
1367 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1368
1369 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1370 canvas->drawPath(path, paint);
1371 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001372 }
1373 }
1374
1375 pdfContext->fGraphicsState.fPath.reset();
1376 // todo zoom ... other stuff ?
1377
1378 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1379#ifndef PDF_DEBUG_NO_CLIPING
1380 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1381#endif
1382 }
1383
1384 //pdfContext->fGraphicsState.fClipPath.reset();
1385 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1386
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001387 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001388
1389}
1390
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001391static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001392 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1393}
1394
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001395static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001396 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1397}
1398
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001399static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001400 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1401}
1402
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001403static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001404 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1405}
1406
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001407static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001408 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1409}
1410
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001411static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001412 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1413}
1414
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001415static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001416 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1417}
1418
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001419static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001420 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1421}
1422
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001423static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001424 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1425}
1426
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001427static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001428 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001429 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1430#ifndef PDF_DEBUG_NO_CLIPING
1431 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1432#endif
1433 }
1434
1435 //pdfContext->fGraphicsState.fClipPath.reset();
1436 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1437
1438 pdfContext->fGraphicsState.fPathClosed = true;
1439
1440 return kOK_PdfResult;
1441}
1442
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001443static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001444 pdfContext->fGraphicsState.fTextBlock = true;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001445 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
1446 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001447
1448 return kPartial_PdfResult;
1449}
1450
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001451static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001452 if (!pdfContext->fGraphicsState.fTextBlock) {
1453 return kIgnoreError_PdfResult;
1454 }
1455 // 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 +00001456 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001457}
1458
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001459PdfResult skpdfGraphicsStateApplyFontCore(PdfContext* pdfContext, const SkPdfObject* fontName, double fontSize) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001460#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001461 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001462#endif
1463
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001464 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1465 // TODO(edisonn): try to recover and draw it any way?
1466 return kIgnoreError_PdfResult;
1467 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001468
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001469 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1470 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1471 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1472 // TODO(edisonn): try to recover and draw it any way?
1473 return kIgnoreError_PdfResult;
1474 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001475
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001476 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1477
1478 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1479
1480 if (skfont) {
1481 pdfContext->fGraphicsState.fSkFont = skfont;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001482 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001483 pdfContext->fGraphicsState.fCurFontSize = fontSize;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001484 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001485}
1486
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001487//font size Tf Set the text font, Tf
1488//, to font and the text font size, Tfs, to size. font is the name of a
1489//font resource in the Fontsubdictionary of the current resource dictionary; size is
1490//a number representing a scale factor. There is no initial value for either font or
1491//size; they must be specified explicitly using Tf before any text is shown.
1492static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1493 double fontSize = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1494 SkPdfObject* fontName = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1495 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
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
1504 PdfResult ret = DrawText(pdfContext,
1505 pdfContext->fObjectStack.top(),
1506 canvas);
1507 pdfContext->fObjectStack.pop();
1508
1509 return ret;
1510}
1511
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001512static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001513 if (!pdfContext->fGraphicsState.fTextBlock) {
1514 // TODO(edisonn): try to recover and draw it any way?
1515 return kIgnoreError_PdfResult;
1516 }
1517
1518 PdfOp_T_star(pdfContext, canvas, looper);
1519 // Do not pop, and push, just transfer the param to Tj
1520 return PdfOp_Tj(pdfContext, canvas, looper);
1521}
1522
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001523static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001524 if (!pdfContext->fGraphicsState.fTextBlock) {
1525 // TODO(edisonn): try to recover and draw it any way?
1526 return kIgnoreError_PdfResult;
1527 }
1528
1529 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1530 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1531 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1532
1533 pdfContext->fObjectStack.push(aw);
1534 PdfOp_Tw(pdfContext, canvas, looper);
1535
1536 pdfContext->fObjectStack.push(ac);
1537 PdfOp_Tc(pdfContext, canvas, looper);
1538
1539 pdfContext->fObjectStack.push(str);
1540 PdfOp_quote(pdfContext, canvas, looper);
1541
1542 return kPartial_PdfResult;
1543}
1544
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001545static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001546 if (!pdfContext->fGraphicsState.fTextBlock) {
1547 // TODO(edisonn): try to recover and draw it any way?
1548 return kIgnoreError_PdfResult;
1549 }
1550
edisonn@google.com571c70b2013-07-10 17:09:50 +00001551 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001552 pdfContext->fObjectStack.pop();
1553
edisonn@google.com571c70b2013-07-10 17:09:50 +00001554 if (!array->isArray()) {
1555 return kIgnoreError_PdfResult;
1556 }
1557
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001558 for( int i=0; i<static_cast<int>(array->size()); i++ )
1559 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001560 if( (*array)[i]->isAnyString()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001561 SkPdfObject* obj = (*array)[i];
1562 DrawText(pdfContext,
1563 obj,
1564 canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001565 } else if ((*array)[i]->isNumber()) {
1566 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001567 SkMatrix matrix;
1568 matrix.setAll(SkDoubleToScalar(1),
1569 SkDoubleToScalar(0),
1570 // TODO(edisonn): use writing mode, vertical/horizontal.
1571 SkDoubleToScalar(-dx), // amount is substracted!!!
1572 SkDoubleToScalar(0),
1573 SkDoubleToScalar(1),
1574 SkDoubleToScalar(0),
1575 SkDoubleToScalar(0),
1576 SkDoubleToScalar(0),
1577 SkDoubleToScalar(1));
1578
1579 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1580 }
1581 }
1582 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1583}
1584
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001585static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001586 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->strRef(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001587 return kOK_PdfResult;
1588}
1589
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001590static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001591 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1592}
1593
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001594static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001595 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1596}
1597
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001598static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001599 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001600// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001601
1602 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1603
1604 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001605 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001606 doubles = false;
1607 }
1608
1609#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001610 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001611#endif
1612
1613 for (int i = n - 1; i >= 0 ; i--) {
1614 if (doubles) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001615 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1616// } else {
1617// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001618 }
1619 }
1620
1621 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001622 // TODO(edisonn): do possible field values to enum at parsing time!
1623 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001624 if (colorOperator->fColorSpace.equals("DeviceRGB") || colorOperator->fColorSpace.equals("RGB")) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001625 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]), (U8CPU)(255*c[1]), (U8CPU)(255*c[2])));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001626 }
1627 return kPartial_PdfResult;
1628}
1629
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001630static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001631 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1632}
1633
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001634static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001635 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1636}
1637
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001638static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001639 if (pdfContext->fObjectStack.top()->isName()) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001640 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1641
1642 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001643 SkPdfDictionary* patternResources = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
edisonn@google.com276fed92013-08-01 21:20:47 +00001644
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001645 if (patternResources == NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001646#ifdef PDF_TRACE
1647 printf("ExtGState is NULL!\n");
1648#endif
1649 return kIgnoreError_PdfResult;
1650 }
1651
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001652 colorOperator->setPatternColorSpace(pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001653 }
1654
1655 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1656 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1657
1658 return kPartial_PdfResult;
1659}
1660
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001661static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001662 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1663}
1664
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001665static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001666 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1667}
1668
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001669static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001670 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001671 return kNYI_PdfResult;
1672}
1673
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001674static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001675 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1676}
1677
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001678static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001679 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1680}
1681
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001682static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001683 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1684 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1685 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001686
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001687 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001688 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001689 return kOK_PdfResult;
1690}
1691
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001692static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001693 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1694}
1695
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001696static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001697 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1698}
1699
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001700static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001701 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001702 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1703 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1704 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1705 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001706
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001707 colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001708 // TODO(edisonn): Set color.
1709 return kNYI_PdfResult;
1710}
1711
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001712static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001713 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1714}
1715
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001716static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001717 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1718}
1719
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001720static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001721 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1722 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1723
1724 return kOK_PdfResult;
1725}
1726
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001727static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001728 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1729
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001730 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1731 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1732
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001733 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001734}
1735
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001736static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001737 *looper = new PdfCompatibilitySectionLooper();
1738 return kOK_PdfResult;
1739}
1740
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001741static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001742#ifdef ASSERT_BAD_PDF_OPS
1743 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1744 // have the assert when testing good pdfs.
1745#endif
1746 return kIgnoreError_PdfResult;
1747}
1748
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001749static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001750 *looper = new PdfInlineImageLooper();
1751 return kOK_PdfResult;
1752}
1753
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001754static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001755#ifdef ASSERT_BAD_PDF_OPS
1756 SkASSERT(false); // must be processed in inline image looper, but let's
1757 // have the assert when testing good pdfs.
1758#endif
1759 return kIgnoreError_PdfResult;
1760}
1761
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001762static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001763#ifdef ASSERT_BAD_PDF_OPS
1764 SkASSERT(false); // must be processed in inline image looper, but let's
1765 // have the assert when testing good pdfs.
1766#endif
1767 return kIgnoreError_PdfResult;
1768}
1769
edisonn@google.com24cdf132013-07-30 16:06:12 +00001770
1771// TODO(edisonn): security review here, make sure all parameters are valid, and safe.
1772PdfResult skpdfGraphicsStateApply_ca(PdfContext* pdfContext, double ca) {
1773 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
1774 return kOK_PdfResult;
1775}
1776
1777PdfResult skpdfGraphicsStateApply_CA(PdfContext* pdfContext, double CA) {
1778 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
1779 return kOK_PdfResult;
1780}
1781
1782PdfResult skpdfGraphicsStateApplyLW(PdfContext* pdfContext, double lineWidth) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001783 pdfContext->fGraphicsState.fLineWidth = lineWidth;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001784 return kOK_PdfResult;
1785}
1786
1787PdfResult skpdfGraphicsStateApplyLC(PdfContext* pdfContext, int64_t lineCap) {
1788 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
1789 return kOK_PdfResult;
1790}
1791
1792PdfResult skpdfGraphicsStateApplyLJ(PdfContext* pdfContext, int64_t lineJoin) {
1793 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
1794 return kOK_PdfResult;
1795}
1796
1797PdfResult skpdfGraphicsStateApplyML(PdfContext* pdfContext, double miterLimit) {
1798 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
1799 return kOK_PdfResult;
1800}
1801
1802// TODO(edisonn): implement all rules, as of now 3) and 5) and 6) do not seem suported by skia, but I am not sure
1803/*
18041) [ ] 0 No dash; solid, unbroken lines
18052) [3] 0 3 units on, 3 units off, …
18063) [2] 1 1 on, 2 off, 2 on, 2 off, …
18074) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
18085) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
18096) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
1810 */
1811
1812PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* intervals, SkPdfObject* phase) {
1813 int cnt = intervals->size();
1814 if (cnt >= 256) {
1815 // TODO(edisonn): report error/warning, unsuported;
1816 // TODO(edisonn): alloc memory
1817 return kIgnoreError_PdfResult;
1818 }
1819 for (int i = 0; i < cnt; i++) {
1820 if (!intervals->objAtAIndex(i)->isNumber()) {
1821 // TODO(edisonn): report error/warning
1822 return kIgnoreError_PdfResult;
1823 }
1824 }
1825
edisonn@google.com24cdf132013-07-30 16:06:12 +00001826 double total = 0;
1827 for (int i = 0 ; i < cnt; i++) {
1828 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
1829 total += pdfContext->fGraphicsState.fDashArray[i];
1830 }
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001831 if (cnt & 1) {
1832 if (cnt == 1) {
1833 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
1834 cnt++;
1835 } else {
1836 // TODO(edisonn): report error/warning
1837 return kNYI_PdfResult;
1838 }
1839 }
1840 pdfContext->fGraphicsState.fDashArrayLength = cnt;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001841 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
1842 if (pdfContext->fGraphicsState.fDashPhase == 0) {
1843 // other rules, changes?
1844 pdfContext->fGraphicsState.fDashPhase = total;
1845 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001846
1847 return kOK_PdfResult;
1848}
1849
edisonn@google.com24cdf132013-07-30 16:06:12 +00001850PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* dash) {
1851 // TODO(edisonn): verify input
1852 if (!dash || dash->isArray() || dash->size() != 2 || !dash->objAtAIndex(0)->isArray() || !dash->objAtAIndex(1)->isNumber()) {
1853 // TODO(edisonn): report error/warning
1854 return kIgnoreError_PdfResult;
1855 }
1856 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0), dash->objAtAIndex(1));
1857}
1858
1859void skpdfGraphicsStateApplyFont(PdfContext* pdfContext, SkPdfArray* fontAndSize) {
1860 if (!fontAndSize || fontAndSize->isArray() || fontAndSize->size() != 2 || !fontAndSize->objAtAIndex(0)->isName() || !fontAndSize->objAtAIndex(1)->isNumber()) {
1861 // TODO(edisonn): report error/warning
1862 return;
1863 }
1864 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), fontAndSize->objAtAIndex(1)->numberValue());
1865}
1866
1867
1868//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1869static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1870 double lw = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1871 return skpdfGraphicsStateApplyLW(pdfContext, lw);
1872}
1873
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001874//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 +00001875static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001876 int64_t lc = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1877 return skpdfGraphicsStateApplyLC(pdfContext, lc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001878}
1879
1880//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 +00001881static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001882 double lj = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1883 return skpdfGraphicsStateApplyLJ(pdfContext, lj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001884}
1885
1886//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001887static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001888 double ml = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1889 return skpdfGraphicsStateApplyML(pdfContext, ml);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001890}
1891
1892//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1893//page 155).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001894static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001895 SkPdfObject* phase = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1896 SkPdfObject* array = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001897
edisonn@google.com24cdf132013-07-30 16:06:12 +00001898 if (!array->isArray()) {
1899 return kIgnoreError_PdfResult;
1900 }
1901
1902 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)array, phase);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001903}
1904
1905//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 +00001906static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001907 pdfContext->fObjectStack.pop();
1908
1909 return kNYI_PdfResult;
1910}
1911
1912//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1913//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1914//fies the output device’s default flatness tolerance.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001915static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001916 pdfContext->fObjectStack.pop();
1917
1918 return kNYI_PdfResult;
1919}
1920
edisonn@google.come878e722013-07-29 19:10:58 +00001921SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
1922
1923class InitBlendModes {
1924public:
1925 InitBlendModes() {
1926 // TODO(edisonn): use the python code generator?
1927 // TABLE 7.2 Standard separable blend modes
1928 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
1929 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
1930 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
1931 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
1932 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
1933 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
1934 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
1935 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
1936 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
1937 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
1938 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
1939 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
1940
1941 // TABLE 7.3 Standard nonseparable blend modes
1942 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
1943 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
1944 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
1945 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
1946 }
1947};
1948
1949InitBlendModes _gDummyInniter;
1950
1951SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
1952 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1953 if (gPdfBlendModes.find(blendMode, len, &mode)) {
1954 return mode;
1955 }
1956
1957 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1958}
1959
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001960void skpdfGraphicsStateApplyBM_name(PdfContext* pdfContext, const std::string& blendMode) {
edisonn@google.come878e722013-07-29 19:10:58 +00001961 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.length());
1962 if (mode <= SkXfermode::kLastMode) {
1963 pdfContext->fGraphicsState.fBlendModesLength = 1;
1964 pdfContext->fGraphicsState.fBlendModes[0] = mode;
1965 } else {
1966 // TODO(edisonn): report unknown blend mode
1967 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001968}
1969
1970void skpdfGraphicsStateApplyBM_array(PdfContext* pdfContext, SkPdfArray* blendModes) {
edisonn@google.come878e722013-07-29 19:10:58 +00001971 if (!blendModes || blendModes->isArray() || blendModes->size() == 0 || blendModes->size() > 256) {
1972 // TODO(edisonn): report error/warning
1973 return;
1974 }
1975 SkXfermode::Mode modes[256];
1976 int cnt = blendModes->size();
1977 for (int i = 0; i < cnt; i++) {
1978 SkPdfObject* name = blendModes->objAtAIndex(i);
1979 if (!name->isName()) {
1980 // TODO(edisonn): report error/warning
1981 return;
1982 }
1983 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
1984 if (mode > SkXfermode::kLastMode) {
1985 // TODO(edisonn): report error/warning
1986 return;
1987 }
1988 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001989
edisonn@google.come878e722013-07-29 19:10:58 +00001990 pdfContext->fGraphicsState.fBlendModesLength = cnt;
1991 for (int i = 0; i < cnt; i++) {
1992 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
1993 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001994}
1995
1996void skpdfGraphicsStateApplySMask_dict(PdfContext* pdfContext, SkPdfDictionary* sMask) {
1997 // TODO(edisonn): verify input
edisonn@google.come878e722013-07-29 19:10:58 +00001998 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001999 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
edisonn@google.come878e722013-07-29 19:10:58 +00002000 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
2001 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
2002 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
2003 } else {
2004 // TODO (edisonn): report error/warning
2005 }
2006}
2007
2008void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string& sMask) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002009 if (sMask == "None") {
2010 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002011 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002012 return;
2013 }
2014
edisonn@google.come878e722013-07-29 19:10:58 +00002015 //Next, get the ExtGState Dictionary from the Resource Dictionary:
2016 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2017
2018 if (extGStateDictionary == NULL) {
2019#ifdef PDF_TRACE
2020 printf("ExtGState is NULL!\n");
2021#endif
2022 // TODO (edisonn): report error/warning
2023 return;
2024 }
2025
2026 SkPdfObject* obj = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
2027 if (!obj || !obj->isDictionary()) {
2028 // TODO (edisonn): report error/warning
2029 return;
2030 }
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002031
2032 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002033 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002034
edisonn@google.come878e722013-07-29 19:10:58 +00002035 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002036}
2037
2038void skpdfGraphicsStateApplyAIS(PdfContext* pdfContext, bool alphaSource) {
2039 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2040}
2041
2042
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002043//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2044//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 +00002045static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002046 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002047
2048#ifdef PDF_TRACE
2049 std::string str;
2050#endif
2051
2052 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00002053 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002054
2055 if (extGStateDictionary == NULL) {
2056#ifdef PDF_TRACE
2057 printf("ExtGState is NULL!\n");
2058#endif
2059 return kIgnoreError_PdfResult;
2060 }
2061
edisonn@google.com571c70b2013-07-10 17:09:50 +00002062 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002063
edisonn@google.com571c70b2013-07-10 17:09:50 +00002064 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2065 return kIgnoreError_PdfResult;
2066 }
2067 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002068
2069 // TODO(edisonn): now load all those properties in graphic state.
2070 if (gs == NULL) {
2071 return kIgnoreError_PdfResult;
2072 }
2073
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002074 if (gs->has_LW()) {
2075 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2076 }
2077
2078 if (gs->has_LC()) {
2079 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2080 }
2081
2082 if (gs->has_LJ()) {
2083 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2084 }
2085
2086 if (gs->has_ML()) {
2087 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2088 }
2089
2090 if (gs->has_D()) {
2091 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2092 }
2093
2094 if (gs->has_Font()) {
2095 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2096 }
2097
2098 if (gs->has_BM()) {
2099 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2100 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2101 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2102 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2103 } else {
2104 // TODO(edisonn): report/warn
2105 }
2106 }
2107
2108 if (gs->has_SMask()) {
2109 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2110 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2111 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
2112 skpdfGraphicsStateApplySMask_dict(pdfContext, gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
2113 } else {
2114 // TODO(edisonn): report/warn
2115 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002116 }
2117
2118 if (gs->has_ca()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002119 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002120 }
2121
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002122 if (gs->has_CA()) {
2123 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2124 }
2125
2126 if (gs->has_AIS()) {
2127 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002128 }
2129
edisonn@google.com9a43c182013-08-01 20:06:42 +00002130 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002131}
2132
2133//charSpace Tc Set the character spacing, Tc
2134//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2135//Initial value: 0.
2136PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002137 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002138 pdfContext->fGraphicsState.fCharSpace = charSpace;
2139
2140 return kOK_PdfResult;
2141}
2142
2143//wordSpace Tw Set the word spacing, T
2144//w
2145//, to wordSpace, which is a number expressed in unscaled
2146//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2147//value: 0.
2148PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002149 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002150 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2151
2152 return kOK_PdfResult;
2153}
2154
2155//scale Tz Set the horizontal scaling, Th
2156//, to (scale ˜ 100). scale is a number specifying the
2157//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002158static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002159 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002160
2161 return kNYI_PdfResult;
2162}
2163
2164//render Tr Set the text rendering mode, T
2165//mode, to render, which is an integer. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002166static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002167 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002168
2169 return kNYI_PdfResult;
2170}
2171//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2172//units. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002173static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002174 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002175
2176 return kNYI_PdfResult;
2177}
2178
2179//wx wy d0
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002180static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002181 pdfContext->fObjectStack.pop();
2182 pdfContext->fObjectStack.pop();
2183
2184 return kNYI_PdfResult;
2185}
2186
2187//wx wy llx lly urx ury d1
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002188static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002189 pdfContext->fObjectStack.pop();
2190 pdfContext->fObjectStack.pop();
2191 pdfContext->fObjectStack.pop();
2192 pdfContext->fObjectStack.pop();
2193 pdfContext->fObjectStack.pop();
2194 pdfContext->fObjectStack.pop();
2195
2196 return kNYI_PdfResult;
2197}
2198
2199//name sh
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002200static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002201 pdfContext->fObjectStack.pop();
2202
2203 return kNYI_PdfResult;
2204}
2205
2206//name Do
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002207static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002208 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002209
edisonn@google.com571c70b2013-07-10 17:09:50 +00002210 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002211
2212 if (xObject == NULL) {
2213#ifdef PDF_TRACE
2214 printf("XObject is NULL!\n");
2215#endif
2216 return kIgnoreError_PdfResult;
2217 }
2218
edisonn@google.com571c70b2013-07-10 17:09:50 +00002219 SkPdfObject* value = xObject->get(name);
2220 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002221
2222#ifdef PDF_TRACE
2223// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002224// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002225#endif
2226
edisonn@google.com571c70b2013-07-10 17:09:50 +00002227 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002228}
2229
2230//tag MP Designate a marked-content point. tag is a name object indicating the role or
2231//significance of the point.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002232static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002233 pdfContext->fObjectStack.pop();
2234
2235 return kNYI_PdfResult;
2236}
2237
2238//tag properties DP Designate a marked-content point with an associated property list. tag is a
2239//name object indicating the role or significance of the point; properties is
2240//either an inline dictionary containing the property list or a name object
2241//associated with it in the Properties subdictionary of the current resource
2242//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002243static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002244 pdfContext->fObjectStack.pop();
2245 pdfContext->fObjectStack.pop();
2246
2247 return kNYI_PdfResult;
2248}
2249
2250//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2251//tag is a name object indicating the role or significance of the sequence.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002252static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002253 pdfContext->fObjectStack.pop();
2254
2255 return kNYI_PdfResult;
2256}
2257
2258//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2259//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2260//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 +00002261static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002262 pdfContext->fObjectStack.pop();
2263 pdfContext->fObjectStack.pop();
2264
2265 return kNYI_PdfResult;
2266}
2267
2268//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002269static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002270 return kNYI_PdfResult;
2271}
2272
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002273static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002274 static bool gInitialized = false;
2275 if (gInitialized) {
2276 return;
2277 }
2278
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002279 gPdfOps.set("q", PdfOp_q);
2280 gPdfOps.set("Q", PdfOp_Q);
2281 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002282
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002283 gPdfOps.set("TD", PdfOp_TD);
2284 gPdfOps.set("Td", PdfOp_Td);
2285 gPdfOps.set("Tm", PdfOp_Tm);
2286 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002287
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002288 gPdfOps.set("m", PdfOp_m);
2289 gPdfOps.set("l", PdfOp_l);
2290 gPdfOps.set("c", PdfOp_c);
2291 gPdfOps.set("v", PdfOp_v);
2292 gPdfOps.set("y", PdfOp_y);
2293 gPdfOps.set("h", PdfOp_h);
2294 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002295
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002296 gPdfOps.set("S", PdfOp_S);
2297 gPdfOps.set("s", PdfOp_s);
2298 gPdfOps.set("f", PdfOp_f);
2299 gPdfOps.set("F", PdfOp_F);
2300 gPdfOps.set("f*", PdfOp_f_star);
2301 gPdfOps.set("B", PdfOp_B);
2302 gPdfOps.set("B*", PdfOp_B_star);
2303 gPdfOps.set("b", PdfOp_b);
2304 gPdfOps.set("b*", PdfOp_b_star);
2305 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002306
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002307 gPdfOps.set("BT", PdfOp_BT);
2308 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002309
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002310 gPdfOps.set("Tj", PdfOp_Tj);
2311 gPdfOps.set("'", PdfOp_quote);
2312 gPdfOps.set("\"", PdfOp_doublequote);
2313 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002314
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002315 gPdfOps.set("CS", PdfOp_CS);
2316 gPdfOps.set("cs", PdfOp_cs);
2317 gPdfOps.set("SC", PdfOp_SC);
2318 gPdfOps.set("SCN", PdfOp_SCN);
2319 gPdfOps.set("sc", PdfOp_sc);
2320 gPdfOps.set("scn", PdfOp_scn);
2321 gPdfOps.set("G", PdfOp_G);
2322 gPdfOps.set("g", PdfOp_g);
2323 gPdfOps.set("RG", PdfOp_RG);
2324 gPdfOps.set("rg", PdfOp_rg);
2325 gPdfOps.set("K", PdfOp_K);
2326 gPdfOps.set("k", PdfOp_k);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002327
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002328 gPdfOps.set("W", PdfOp_W);
2329 gPdfOps.set("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002330
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002331 gPdfOps.set("BX", PdfOp_BX);
2332 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002333
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002334 gPdfOps.set("BI", PdfOp_BI);
2335 gPdfOps.set("ID", PdfOp_ID);
2336 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002337
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002338 gPdfOps.set("w", PdfOp_w);
2339 gPdfOps.set("J", PdfOp_J);
2340 gPdfOps.set("j", PdfOp_j);
2341 gPdfOps.set("M", PdfOp_M);
2342 gPdfOps.set("d", PdfOp_d);
2343 gPdfOps.set("ri", PdfOp_ri);
2344 gPdfOps.set("i", PdfOp_i);
2345 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002346
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002347 gPdfOps.set("Tc", PdfOp_Tc);
2348 gPdfOps.set("Tw", PdfOp_Tw);
2349 gPdfOps.set("Tz", PdfOp_Tz);
2350 gPdfOps.set("TL", PdfOp_TL);
2351 gPdfOps.set("Tf", PdfOp_Tf);
2352 gPdfOps.set("Tr", PdfOp_Tr);
2353 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002354
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002355 gPdfOps.set("d0", PdfOp_d0);
2356 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002357
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002358 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002359
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002360 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002361
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002362 gPdfOps.set("MP", PdfOp_MP);
2363 gPdfOps.set("DP", PdfOp_DP);
2364 gPdfOps.set("BMC", PdfOp_BMC);
2365 gPdfOps.set("BDC", PdfOp_BDC);
2366 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002367
2368 gInitialized = true;
2369}
2370
2371class InitPdfOps {
2372public:
2373 InitPdfOps() {
2374 initPdfOperatorRenderes();
2375 }
2376};
2377
2378InitPdfOps gInitPdfOps;
2379
2380void reportPdfRenderStats() {
2381 std::map<std::string, int>::iterator iter;
2382
2383 for (int i = 0 ; i < kCount_PdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002384 SkTDict<int>::Iter iter(gRenderStats[i]);
2385 const char* key;
2386 int value = 0;
2387 while ((key = iter.next(&value)) != NULL) {
2388 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002389 }
2390 }
2391}
2392
2393PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002394 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002395 {
2396 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002397 PdfOperatorRenderer pdfOperatorRenderer = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002398 if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) && pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002399 // caller, main work is done by pdfOperatorRenderer(...)
2400 PdfTokenLooper* childLooper = NULL;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002401 PdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
2402
2403 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002404 gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
2405 gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002406
2407 if (childLooper) {
2408 childLooper->setUp(this);
2409 childLooper->loop();
2410 delete childLooper;
2411 }
2412 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002413 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002414 gRenderStats[kUnsupported_PdfResult].find(token.fKeyword, token.fKeywordLength, &cnt);
2415 gRenderStats[kUnsupported_PdfResult].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002416 }
2417 }
2418 else if (token.fType == kObject_TokenType)
2419 {
2420 fPdfContext->fObjectStack.push( token.fObject );
2421 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002422 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002423 // TODO(edisonn): deine or use assert not reached
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002424 return kIgnoreError_PdfResult;
2425 }
2426 return kOK_PdfResult;
2427}
2428
2429void PdfMainLooper::loop() {
2430 PdfToken token;
2431 while (readToken(fTokenizer, &token)) {
2432 consumeToken(token);
2433 }
2434}
2435
2436PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002437 SkASSERT(false);
2438 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002439}
2440
2441void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002442 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002443}
2444
2445PdfResult PdfInlineImageLooper::done() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002446 return kNYI_PdfResult;
2447}
2448
2449PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2450 return fParent->consumeToken(token);
2451}
2452
2453void PdfCompatibilitySectionLooper::loop() {
2454 // TODO(edisonn): save stacks position, or create a new stack?
2455 // TODO(edisonn): what happens if we pop out more variables then when we started?
2456 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2457 // pop-ing too much will not affect outside the section.
2458 PdfToken token;
2459 while (readToken(fTokenizer, &token)) {
2460 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2461 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2462 looper->setUp(this);
2463 looper->loop();
2464 delete looper;
2465 } else {
2466 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
2467 fParent->consumeToken(token);
2468 }
2469 }
2470 // TODO(edisonn): restore stack.
2471}
2472
2473// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2474// TODO(edisonn): Add API for Forms viewing and editing
2475// e.g. SkBitmap getPage(int page);
2476// int formsCount();
2477// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2478// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2479// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2480// resources needed, so the preview is fast.
2481// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2482// references automatically.
2483
edisonn@google.com222382b2013-07-10 22:33:10 +00002484PdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00002485
edisonn@google.com444e25a2013-07-11 15:20:50 +00002486bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00002487 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002488 return false;
2489 }
2490
edisonn@google.com222382b2013-07-10 22:33:10 +00002491 if (page < 0 || page >= pages()) {
2492 return false;
2493 }
2494
edisonn@google.com222382b2013-07-10 22:33:10 +00002495 PdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002496
edisonn@google.com222382b2013-07-10 22:33:10 +00002497 pdfContext.fOriginalMatrix = SkMatrix::I();
2498 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2499
2500 gPdfContext = &pdfContext;
2501
2502 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00002503 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00002504 SkScalar w = dst.width();
2505 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00002506
edisonn@google.com444e25a2013-07-11 15:20:50 +00002507 SkScalar wp = fPdfDoc->MediaBox(page).width();
2508 SkScalar hp = fPdfDoc->MediaBox(page).height();
2509
2510 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 +00002511// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2512
2513 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2514 // TODO(edisonn): add flagg for no clipping.
2515 // Use larger image to make sure we do not draw anything outside of page
2516 // could be used in tests.
2517
2518#ifdef PDF_DEBUG_3X
2519 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)};
2520#else
2521 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2522#endif
2523 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2524 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2525
2526 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2527 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2528
2529 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2530 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2531
2532 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2533 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2534
2535
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002536 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
2537 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
2538 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
edisonn@google.com222382b2013-07-10 22:33:10 +00002539
edisonn@google.com222382b2013-07-10 22:33:10 +00002540#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00002541 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00002542#endif
2543
edisonn@google.com444e25a2013-07-11 15:20:50 +00002544 canvas->setMatrix(pdfContext.fOriginalMatrix);
2545
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002546 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2547
2548 // TODO(edisonn:) erase with white before draw?
edisonn@google.com222382b2013-07-10 22:33:10 +00002549// SkPaint paint;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002550// paint.setColor(SK_ColorWHITE);
edisonn@google.com222382b2013-07-10 22:33:10 +00002551// canvas->drawRect(rect, paint);
2552
edisonn@google.com222382b2013-07-10 22:33:10 +00002553
2554 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002555 return true;
2556}
edisonn@google.com222382b2013-07-10 22:33:10 +00002557
2558bool SkPdfRenderer::load(const SkString inputFileName) {
2559 unload();
2560
2561 // TODO(edisonn): create static function that could return NULL if there are errors
2562 fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00002563 if (fPdfDoc->pages() == 0) {
2564 delete fPdfDoc;
2565 fPdfDoc = NULL;
2566 }
edisonn@google.com222382b2013-07-10 22:33:10 +00002567
2568 return fPdfDoc != NULL;
2569}
2570
edisonn@google.com147adb12013-07-24 15:56:19 +00002571bool SkPdfRenderer::load(SkStream* stream) {
2572 unload();
2573
2574 // TODO(edisonn): create static function that could return NULL if there are errors
2575 fPdfDoc = new SkNativeParsedPDF(stream);
2576 if (fPdfDoc->pages() == 0) {
2577 delete fPdfDoc;
2578 fPdfDoc = NULL;
2579 }
2580
2581 return fPdfDoc != NULL;
2582}
2583
2584
edisonn@google.com222382b2013-07-10 22:33:10 +00002585int SkPdfRenderer::pages() const {
2586 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2587}
2588
2589void SkPdfRenderer::unload() {
2590 delete fPdfDoc;
2591 fPdfDoc = NULL;
2592}
2593
2594SkRect SkPdfRenderer::MediaBox(int page) const {
2595 SkASSERT(fPdfDoc);
2596 return fPdfDoc->MediaBox(page);
2597}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002598
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002599size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002600 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2601}
edisonn@google.com147adb12013-07-24 15:56:19 +00002602
2603bool SkPDFNativeRenderToBitmap(SkStream* stream,
2604 SkBitmap* output,
2605 int page,
2606 SkPdfContent content,
2607 double dpi) {
2608 SkASSERT(page >= 0);
2609 SkPdfRenderer renderer;
2610 renderer.load(stream);
2611 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
2612 return false;
2613 }
2614
2615 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
2616
2617 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2618 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2619
2620 rect = SkRect::MakeWH(width, height);
2621
2622 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
2623
2624 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*output)));
2625 SkCanvas canvas(device);
2626
2627 return renderer.renderPage(page, &canvas, rect);
2628}