blob: 4f36c0bc91fcd41dc21dd9519df28a83d27e7477 [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++;
edisonn@google.com0f901902013-08-07 11:56:16 +0000239 gLastOpKeyword++;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000240#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.
edisonn@google.com0f901902013-08-07 11:56:16 +0000243 if (gLastKeyword[0] && hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000244 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.com0f901902013-08-07 11:56:16 +0000314
315 if (ret && token->fType == kKeyword_TokenType && token->fKeyword && token->fKeywordLength > 0 && token->fKeywordLength < 100) {
316 strncpy(gLastKeyword, token->fKeyword, token->fKeywordLength);
317 gLastKeyword[token->fKeywordLength] = '\0';
318 } else {
319 gLastKeyword[0] = '\0';
320 }
321
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000322#endif
323
324 return ret;
325}
326
327
328
329typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
330
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000331SkTDict<PdfOperatorRenderer> gPdfOps(100);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000332
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000333
334template <typename T> class SkTDictWithDefaultConstructor : public SkTDict<T> {
335public:
336 SkTDictWithDefaultConstructor() : SkTDict<T>(10) {}
337};
338
339SkTDictWithDefaultConstructor<int> gRenderStats[kCount_PdfResult];
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000340
edisonn@google.com571c70b2013-07-10 17:09:50 +0000341const char* gRenderStatsNames[kCount_PdfResult] = {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000342 "Success",
343 "Partially implemented",
344 "Not yet implemented",
345 "Ignore Error",
346 "Error",
347 "Unsupported/Unknown"
348};
349
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000350static PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000351 const SkPdfObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000352 SkCanvas* canvas)
353{
354
355 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
356 if (skfont == NULL) {
357 skfont = SkPdfFont::Default();
358 }
359
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000360
edisonn@google.com571c70b2013-07-10 17:09:50 +0000361 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000362 // TODO(edisonn): report warning
363 return kIgnoreError_PdfResult;
364 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000365 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000366
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000367 SkUnencodedText binary(str);
368
369 SkDecodedText decoded;
370
371 if (skfont->encoding() == NULL) {
372 // TODO(edisonn): report warning
373 return kNYI_PdfResult;
374 }
375
376 skfont->encoding()->decodeText(binary, &decoded);
377
378 SkPaint paint;
379 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
380 // Or maybe just not call setTextSize at all?
381 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
382 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
383 }
384
385// if (fCurFont && fCurFont->GetFontScale() != 0) {
386// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
387// }
388
389 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
390
391 canvas->save();
392
393#if 1
394 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
395
396 SkPoint point1;
397 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
398
399 SkMatrix mirror;
400 mirror.setTranslate(0, -point1.y());
401 // TODO(edisonn): fix rotated text, and skewed too
402 mirror.postScale(SK_Scalar1, -SK_Scalar1);
403 // TODO(edisonn): post rotate, skew
404 mirror.postTranslate(0, point1.y());
405
406 matrix.postConcat(mirror);
407
408 canvas->setMatrix(matrix);
409
410 SkTraceMatrix(matrix, "mirrored");
411#endif
412
edisonn@google.com6e49c342013-06-27 20:03:43 +0000413 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000414 canvas->restore();
415
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000416 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000417}
418
419// TODO(edisonn): create header files with declarations!
420PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
421PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
422PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
423PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
424
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000425// TODO(edisonn): perf!!!
426
427static SkColorTable* getGrayColortable() {
428 static SkColorTable* grayColortable = NULL;
429 if (grayColortable == NULL) {
430 SkPMColor* colors = new SkPMColor[256];
431 for (int i = 0 ; i < 256; i++) {
432 colors[i] = SkPreMultiplyARGB(255, i, i, i);
433 }
434 grayColortable = new SkColorTable(colors, 256);
435 }
436 return grayColortable;
437}
438
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000439static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream, size_t uncompressedStreamLength,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000440 int width, int height, int bytesPerLine,
441 int bpc, const std::string& colorSpace,
442 bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000443 SkBitmap* bitmap = new SkBitmap();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000444
edisonn@google.com571c70b2013-07-10 17:09:50 +0000445 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000446//#define MAX_COMPONENTS 10
447
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000448 // TODO(edisonn): assume start of lines are aligned at 32 bits?
449 // Is there a faster way to load the uncompressed stream into a bitmap?
450
451 // minimal support for now
452 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
453 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
454
455 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000456 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000457 for (int w = 0 ; w < width; w++) {
458 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
459 uncompressedStream[3 * w + 1],
460 uncompressedStream[3 * w + 2]);
461 i++;
462 }
463 uncompressedStream += bytesPerLine;
464 }
465
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000466 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
467 bitmap->setPixels(uncompressedStreamArgb);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000468 }
469 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
470 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
471
472 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000473 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000474 for (int w = 0 ; w < width; w++) {
475 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
476 uncompressedStream[w];
477 i++;
478 }
479 uncompressedStream += bytesPerLine;
480 }
481
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000482 bitmap->setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000483 width, height);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000484 bitmap->setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000485 }
486
487 // TODO(edisonn): Report Warning, NYI, or error
488 return bitmap;
489}
490
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000491// utils
492
493// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
494// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
495// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
496// skia format, through a table
497
498// this functions returns the image, it does not look at the smask.
499
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000500static SkBitmap* getImageFromObjectCore(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000501 if (image == NULL || !image->hasStream()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000502 // TODO(edisonn): report warning to be used in testing.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000503 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000504 }
505
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000506 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc);
507 int64_t width = image->Width(pdfContext->fPdfDoc);
508 int64_t height = image->Height(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000509 std::string colorSpace = "DeviceRGB";
510
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000511 bool indexed = false;
512 SkPMColor colors[256];
513 int cnt = 0;
514
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000515 // TODO(edisonn): color space can be an array too!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000516 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
517 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000518 } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) {
519 SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc);
520 if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") &&
521 (array->objAtAIndex(1)->isName("DeviceRGB") || array->objAtAIndex(1)->isName("RGB")) &&
522 array->objAtAIndex(2)->isInteger() &&
523 array->objAtAIndex(3)->isHexString()
524 ) {
525 // TODO(edisonn): suport only DeviceRGB for now.
526 indexed = true;
527 cnt = array->objAtAIndex(2)->intValue() + 1;
528 if (cnt > 256) {
529 // TODO(edionn): report NYIs
530 return NULL;
531 }
532 SkColorTable colorTable(cnt);
533 NotOwnedString data = array->objAtAIndex(3)->strRef();
534 if (data.fBytes != (unsigned int)cnt * 3) {
535 // TODO(edionn): report error/warning
536 return NULL;
537 }
538 for (int i = 0 ; i < cnt; i++) {
539 colors[i] = SkPreMultiplyARGB(0xff, data.fBuffer[3 * i], data.fBuffer[3 * i + 1], data.fBuffer[3 * i + 2]);
540 }
541 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000542 }
543
544/*
545 bool imageMask = image->imageMask();
546
547 if (imageMask) {
548 if (bpc != 0 && bpc != 1) {
549 // TODO(edisonn): report warning to be used in testing.
550 return SkBitmap();
551 }
552 bpc = 1;
553 }
554*/
555
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000556 const unsigned char* uncompressedStream = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000557 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000558
edisonn@google.com571c70b2013-07-10 17:09:50 +0000559 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000560
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000561 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000562 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000563 // TODO(edisonn): report warning to be used in testing.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000564 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000565 }
566
edisonn@google.com571c70b2013-07-10 17:09:50 +0000567 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
568
569 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
570 streamDict->getFilterAsName(NULL) == "DCTDecode") ||
571 (streamDict->isFilterAArray(NULL) &&
572 streamDict->getFilterAsArray(NULL)->size() > 0 &&
573 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
574 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() == "DCTDecode"))) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000575 SkBitmap* bitmap = new SkBitmap();
576 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000577 return bitmap;
578 }
579
580
581
582 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
583// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
584// obj.GetDictionary().GetKey(PdfName("Filter")));
585// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
586// value = resolveReferenceObject(pdfContext->fPdfDoc,
587// &value->GetArray()[0]);
588// }
589// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
590// SkStream stream = SkStream::
591// SkImageDecoder::Factory()
592// }
593
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000594 // TODO(edisonn): assumes RGB for now, since it is the only onwe implemented
595 if (indexed) {
596 SkBitmap* bitmap = new SkBitmap();
597 bitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
598 SkColorTable* colorTable = new SkColorTable(colors, cnt);
599 bitmap->setPixels((void*)uncompressedStream, colorTable);
600 return bitmap;
601 }
602
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000603 int bytesPerLine = (int)(uncompressedStreamLength / height);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000604#ifdef PDF_TRACE
605 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000606 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000607 }
608#endif
609
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000610 SkBitmap* bitmap = transferImageStreamToBitmap(
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000611 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000612 (int)width, (int)height, bytesPerLine,
613 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000614 transparencyMask);
615
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000616 return bitmap;
617}
618
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000619static SkBitmap* getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
620 if (!transparencyMask) {
621 if (!image->hasData(SkPdfObject::kBitmap_Data)) {
622 SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask);
623 image->setData(bitmap, SkPdfObject::kBitmap_Data);
624 }
625 return (SkBitmap*) image->data(SkPdfObject::kBitmap_Data);
626 } else {
627 return getImageFromObjectCore(pdfContext, image, transparencyMask);
628 }
629}
630
631static SkBitmap* getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000632 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000633
634 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000635 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000636 }
637
638 // TODO(edisonn): implement GS SMask. Default to empty right now.
639 return pdfContext->fGraphicsState.fSMask;
640}
641
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000642static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000643 if (skpdfimage == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000644 return kIgnoreError_PdfResult;
645 }
646
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000647 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
648 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000649
650 canvas->save();
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000651 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000652
edisonn@google.com571c70b2013-07-10 17:09:50 +0000653 SkScalar z = SkIntToScalar(0);
654 SkScalar one = SkIntToScalar(1);
655
656 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
657 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
658 SkMatrix flip;
659 SkAssertResult(flip.setPolyToPoly(from, to, 4));
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000660 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000661 solveImageFlip.preConcat(flip);
662 canvas->setMatrix(solveImageFlip);
edisonn@google.com0f901902013-08-07 11:56:16 +0000663
664#ifdef PDF_TRACE
665 SkPoint final[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
666 solveImageFlip.mapPoints(final, 4);
667 printf("IMAGE rect = ");
668 for (int i = 0; i < 4; i++) {
669 printf("(%f %f) ", SkScalarToDouble(final[i].x()), SkScalarToDouble(final[i].y()));
670 }
671 printf("\n");
672#endif // PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +0000673
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000674 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
675
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000676 // TODO(edisonn): soft mask type? alpha/luminosity.
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000677 SkPaint paint;
678 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
679
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000680 if (!sMask || sMask->empty()) {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000681 canvas->drawBitmapRect(*image, dst, &paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000682 } else {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000683 canvas->saveLayer(&dst, &paint);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000684 canvas->drawBitmapRect(*image, dst, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000685 SkPaint xfer;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000686 // TODO(edisonn): is the blend mode specified already implicitly/explicitly in pdf?
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000687 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000688 canvas->drawBitmapRect(*sMask, dst, &xfer);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000689 canvas->restore();
690 }
691
692 canvas->restore();
693
694 return kPartial_PdfResult;
695}
696
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000697//TODO(edisonn): options for implementing isolation and knockout
698// 1) emulate them (current solution)
699// PRO: simple
700// CON: will need to use readPixels, which means serious perf issues
701// 2) Compile a plan for an array of matrixes, compose the result at the end
702// PRO: might be faster then 1, no need to readPixels
703// CON: multiple drawings (but on smaller areas), pay a price at loading pdf to compute a pdf draw plan
704// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
705// 3) support them natively in SkCanvas
706// PRO: simple
707// CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1)
708// 4) compile a plan using pathops, and render once without any fancy rules with backdrop
709// PRO: simple, fast
710// CON: pathops must be bug free first + time to compute new paths
711// pay a price at loading pdf to compute a pdf draw plan
712// 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 +0000713// 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 +0000714
715
716// TODO(edisonn): draw plan from point! - list of draw ops of a point, like a tree!
717// TODO(edisonn): Minimal PDF to draw some points - remove everything that it is not needed, save pdf uncompressed
718
719
720
721static void doGroup_before(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup, bool page) {
722 SkRect bboxOrig = bbox;
723 SkBitmap backdrop;
724 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
725// bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000726 SkPaint paint;
727 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
728 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000729}
730
edisonn@google.com251176e2013-08-01 13:24:00 +0000731// TODO(edisonn): non isolation implemented in skia
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000732//static void doGroup_after(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup) {
edisonn@google.com251176e2013-08-01 13:24:00 +0000733// if not isolated
734// canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000735//}
736
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000737static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000738 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000739 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000740 }
741
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000742 if (!skobj->has_BBox()) {
743 return kIgnoreError_PdfResult;
744 }
745
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000746 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000747
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000748 canvas->save();
749
750
edisonn@google.com571c70b2013-07-10 17:09:50 +0000751 if (skobj->Resources(pdfContext->fPdfDoc)) {
752 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000753 }
754
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000755 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000756
edisonn@google.com571c70b2013-07-10 17:09:50 +0000757 if (skobj->has_Matrix()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000758 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
759 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
760 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000761 // TODO(edisonn) reset matrixTm and matricTlm also?
762 }
763
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000764 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com0f901902013-08-07 11:56:16 +0000765 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000766
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000767 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000768
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000769 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
770 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000771
772 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
773 // For this PdfContentsTokenizer needs to be extended.
774
edisonn@google.come878e722013-07-29 19:10:58 +0000775 // This is a group?
776 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000777 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
778 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
edisonn@google.come878e722013-07-29 19:10:58 +0000779 }
780
edisonn@google.com571c70b2013-07-10 17:09:50 +0000781 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000782
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000783 SkPdfNativeTokenizer* tokenizer =
784 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000785 if (tokenizer != NULL) {
786 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
787 looper.loop();
788 delete tokenizer;
789 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000790
791 // TODO(edisonn): should we restore the variable stack at the same state?
792 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000793
794 if (skobj->has_Group()) {
795 canvas->restore();
796 }
797
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000798 canvas->restore();
799 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000800 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000801}
802
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000803
804// TODO(edisonn): Extract a class like ObjWithStream
805static PdfResult doXObject_Pattern(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1PatternDictionary* skobj) {
806 if (!skobj || !skobj->hasStream()) {
807 return kIgnoreError_PdfResult;
808 }
809
810 if (!skobj->has_BBox()) {
811 return kIgnoreError_PdfResult;
812 }
813
814 PdfOp_q(pdfContext, canvas, NULL);
815
816 canvas->save();
817
818
819 if (skobj->Resources(pdfContext->fPdfDoc)) {
820 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
821 }
822
edisonn@google.com0f901902013-08-07 11:56:16 +0000823 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000824
825 if (skobj->has_Matrix()) {
edisonn@google.com0f901902013-08-07 11:56:16 +0000826 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000827 }
828
edisonn@google.com0f901902013-08-07 11:56:16 +0000829 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000830
edisonn@google.com0f901902013-08-07 11:56:16 +0000831 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix);
832 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000833
834 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
835 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
836
837 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
838 // For this PdfContentsTokenizer needs to be extended.
839
840 SkPdfStream* stream = (SkPdfStream*)skobj;
841
842 SkPdfNativeTokenizer* tokenizer =
843 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
844 if (tokenizer != NULL) {
845 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
846 looper.loop();
847 delete tokenizer;
848 }
849
850 // TODO(edisonn): should we restore the variable stack at the same state?
851 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
852
853 canvas->restore();
854 PdfOp_Q(pdfContext, canvas, NULL);
855 return kPartial_PdfResult;
856}
857
858
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000859//static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
860// return kNYI_PdfResult;
861//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000862
edisonn@google.com571c70b2013-07-10 17:09:50 +0000863PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
864 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000865 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000866 }
867
868 PdfOp_q(pdfContext, canvas, NULL);
869 canvas->save();
870
871 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
872 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
873
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000874 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
875 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000876
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000877 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000878
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000879 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000880
881 SkRect rm = bBox;
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000882 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000883
884 SkTraceRect(rm, "bbox mapped");
885
886 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
887
888 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
889 // For this PdfContentsTokenizer needs to be extended.
890
edisonn@google.com571c70b2013-07-10 17:09:50 +0000891 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000892
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000893 SkPdfNativeTokenizer* tokenizer =
894 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000895 if (tokenizer != NULL) {
896 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
897 looper.loop();
898 delete tokenizer;
899 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000900
901 // TODO(edisonn): should we restore the variable stack at the same state?
902 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
903 canvas->restore();
904 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000905
906 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000907}
908
909
edisonn@google.com571c70b2013-07-10 17:09:50 +0000910// TODO(edisonn): make sure the pointer is unique
911std::set<const SkPdfObject*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000912
913class CheckRecursiveRendering {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000914 const SkPdfObject* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000915public:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000916 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000917 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000918 }
919
920 ~CheckRecursiveRendering() {
921 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000922 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000923 }
924
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000925 static bool IsInRendering(const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000926 return gInRendering.find(obj) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000927 }
928};
929
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000930static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000931 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000932 // Oops, corrupt PDF!
933 return kIgnoreError_PdfResult;
934 }
935
edisonn@google.com571c70b2013-07-10 17:09:50 +0000936 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000937
edisonn@google.com571c70b2013-07-10 17:09:50 +0000938 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000939 {
940 case kImageDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000941 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000942 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000943 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000944 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
945 //return doXObject_PS(skxobj.asPS());
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000946 default: {
947 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != kNone_SkPdfObjectType) {
948 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
949 return doXObject_Pattern(pdfContext, canvas, pattern);
950 }
951 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000952 }
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000953 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000954}
955
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000956static PdfResult doPage(PdfContext* pdfContext, SkCanvas* canvas, SkPdfPageObjectDictionary* skobj) {
957 if (!skobj) {
958 return kIgnoreError_PdfResult;
959 }
960
961 if (!skobj->isContentsAStream(pdfContext->fPdfDoc)) {
962 return kNYI_PdfResult;
963 }
964
965 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
966
967 if (!stream) {
968 return kIgnoreError_PdfResult;
969 }
970
971 if (CheckRecursiveRendering::IsInRendering(skobj)) {
972 // Oops, corrupt PDF!
973 return kIgnoreError_PdfResult;
974 }
975 CheckRecursiveRendering checkRecursion(skobj);
976
977
978 PdfOp_q(pdfContext, canvas, NULL);
979
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000980
981 if (skobj->Resources(pdfContext->fPdfDoc)) {
982 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
983 }
984
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000985 // TODO(edisonn): MediaBox can be inherited!!!!
986 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000987 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000988 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
989 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
990 } else {
991 canvas->save();
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000992 }
993
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000994
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000995 SkPdfNativeTokenizer* tokenizer =
996 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
997 if (tokenizer != NULL) {
998 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
999 looper.loop();
1000 delete tokenizer;
1001 }
1002
1003 // TODO(edisonn): should we restore the variable stack at the same state?
1004 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
1005 canvas->restore();
1006 PdfOp_Q(pdfContext, canvas, NULL);
1007 return kPartial_PdfResult;
1008}
1009
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001010PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1011 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1012 canvas->save();
1013 return kOK_PdfResult;
1014}
1015
1016PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1017 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1018 pdfContext->fStateStack.pop();
1019 canvas->restore();
1020 return kOK_PdfResult;
1021}
1022
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001023static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001024 double array[6];
1025 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001026 array[5 - i] = pdfContext->fObjectStack.top()->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001027 pdfContext->fObjectStack.pop();
1028 }
1029
1030 // a b
1031 // c d
1032 // e f
1033
1034 // 0 1
1035 // 2 3
1036 // 4 5
1037
1038 // sx ky
1039 // kx sy
1040 // tx ty
1041 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1042
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001043 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001044
1045#ifdef PDF_TRACE
1046 printf("cm ");
1047 for (int i = 0 ; i < 6 ; i++) {
1048 printf("%f ", array[i]);
1049 }
1050 printf("\n");
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001051 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001052#endif
1053
1054 return kOK_PdfResult;
1055}
1056
1057//leading TL Set the text leading, Tl
1058//, to leading, which is a number expressed in unscaled text
1059//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001060static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001061 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001062
1063 pdfContext->fGraphicsState.fTextLeading = ty;
1064
1065 return kOK_PdfResult;
1066}
1067
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001068static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001069#ifdef PDF_TRACE
1070 printf("stack size = %i\n", (int)pdfContext->fObjectStack.size());
1071#endif
edisonn@google.com571c70b2013-07-10 17:09:50 +00001072 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001073 SkPdfObject* obj = pdfContext->fObjectStack.top();
1074 obj = obj;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001075 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001076
1077 double array[6] = {1, 0, 0, 1, tx, ty};
1078 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1079
1080 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1081 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1082
1083 return kPartial_PdfResult;
1084}
1085
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001086static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001087 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1088 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001089
edisonn@google.com571c70b2013-07-10 17:09:50 +00001090 // TODO(edisonn): Create factory methods or constructors so native is hidden
1091 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001092 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001093
1094 PdfOp_TL(pdfContext, canvas, looper);
1095
edisonn@google.com571c70b2013-07-10 17:09:50 +00001096 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001097 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001098
edisonn@google.com571c70b2013-07-10 17:09:50 +00001099 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001100 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001101
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001102 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1103
1104 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
1105 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001106
1107 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001108}
1109
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001110static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001111 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1112 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1113 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1114 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1115 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1116 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001117
1118 double array[6];
1119 array[0] = a;
1120 array[1] = b;
1121 array[2] = c;
1122 array[3] = d;
1123 array[4] = e;
1124 array[5] = f;
1125
1126 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001127 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001128
1129 // TODO(edisonn): Text positioning.
1130 pdfContext->fGraphicsState.fMatrixTm = matrix;
1131 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1132
1133 return kPartial_PdfResult;
1134}
1135
1136//— T* Move to the start of the next line. This operator has the same effect as the code
1137//0 Tl Td
1138//where Tl is the current leading parameter in the text state
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001139static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001140 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
1141 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001142
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001143 pdfContext->fObjectStack.push(zero);
1144 pdfContext->fObjectStack.push(tl);
1145
1146 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1147
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001148 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001149}
1150
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001151static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001152 if (pdfContext->fGraphicsState.fPathClosed) {
1153 pdfContext->fGraphicsState.fPath.reset();
1154 pdfContext->fGraphicsState.fPathClosed = false;
1155 }
1156
edisonn@google.com571c70b2013-07-10 17:09:50 +00001157 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1158 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001159
1160 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1161 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1162
1163 return kOK_PdfResult;
1164}
1165
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001166static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001167 if (pdfContext->fGraphicsState.fPathClosed) {
1168 pdfContext->fGraphicsState.fPath.reset();
1169 pdfContext->fGraphicsState.fPathClosed = false;
1170 }
1171
edisonn@google.com571c70b2013-07-10 17:09:50 +00001172 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1173 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001174
1175 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1176 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1177
1178 return kOK_PdfResult;
1179}
1180
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001181static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001182 if (pdfContext->fGraphicsState.fPathClosed) {
1183 pdfContext->fGraphicsState.fPath.reset();
1184 pdfContext->fGraphicsState.fPathClosed = false;
1185 }
1186
edisonn@google.com571c70b2013-07-10 17:09:50 +00001187 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1188 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1189 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1190 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1191 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1192 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001193
1194 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1195 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1196 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1197
1198 pdfContext->fGraphicsState.fCurPosX = x3;
1199 pdfContext->fGraphicsState.fCurPosY = y3;
1200
1201 return kOK_PdfResult;
1202}
1203
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001204static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001205 if (pdfContext->fGraphicsState.fPathClosed) {
1206 pdfContext->fGraphicsState.fPath.reset();
1207 pdfContext->fGraphicsState.fPathClosed = false;
1208 }
1209
edisonn@google.com571c70b2013-07-10 17:09:50 +00001210 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1211 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1212 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1213 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001214 double y1 = pdfContext->fGraphicsState.fCurPosY;
1215 double x1 = pdfContext->fGraphicsState.fCurPosX;
1216
1217 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1218 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1219 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1220
1221 pdfContext->fGraphicsState.fCurPosX = x3;
1222 pdfContext->fGraphicsState.fCurPosY = y3;
1223
1224 return kOK_PdfResult;
1225}
1226
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001227static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001228 if (pdfContext->fGraphicsState.fPathClosed) {
1229 pdfContext->fGraphicsState.fPath.reset();
1230 pdfContext->fGraphicsState.fPathClosed = false;
1231 }
1232
edisonn@google.com571c70b2013-07-10 17:09:50 +00001233 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1234 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001235 double y2 = pdfContext->fGraphicsState.fCurPosY;
1236 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001237 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1238 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001239
1240 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1241 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1242 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1243
1244 pdfContext->fGraphicsState.fCurPosX = x3;
1245 pdfContext->fGraphicsState.fCurPosY = y3;
1246
1247 return kOK_PdfResult;
1248}
1249
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001250static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001251 if (pdfContext->fGraphicsState.fPathClosed) {
1252 pdfContext->fGraphicsState.fPath.reset();
1253 pdfContext->fGraphicsState.fPathClosed = false;
1254 }
1255
edisonn@google.com571c70b2013-07-10 17:09:50 +00001256 double height = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1257 double width = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1258 double y = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1259 double x = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001260
1261 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1262 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1263
1264 pdfContext->fGraphicsState.fCurPosX = x;
1265 pdfContext->fGraphicsState.fCurPosY = y + height;
1266
1267 return kOK_PdfResult;
1268}
1269
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001270static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001271 pdfContext->fGraphicsState.fPath.close();
1272 return kOK_PdfResult;
1273}
1274
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001275static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001276 SkPath path = pdfContext->fGraphicsState.fPath;
1277
1278 if (close) {
1279 path.close();
1280 }
1281
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001282 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001283
1284 SkPaint paint;
1285
1286 SkPoint line[2];
1287 if (fill && !stroke && path.isLine(line)) {
1288 paint.setStyle(SkPaint::kStroke_Style);
1289
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001290 // TODO(edisonn): implement this with patterns
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001291 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1292 paint.setStrokeWidth(SkDoubleToScalar(0));
1293
1294 canvas->drawPath(path, paint);
1295 } else {
1296 if (fill) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001297 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0 &&
1298 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1299
1300 // TODO(edisonn): we can use a shader here, like imageshader to draw fast. ultimately,
1301 // if this is not possible, and we are in rasper mode, and the cells don't intersect, we could even have multiple cpus.
1302
1303 canvas->save();
1304 PdfOp_q(pdfContext, canvas, NULL);
1305
1306 if (evenOdd) {
1307 path.setFillType(SkPath::kEvenOdd_FillType);
1308 }
1309 canvas->clipPath(path);
1310
1311 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(pdfContext->fGraphicsState.fNonStroking.fPattern) != kNone_SkPdfObjectType) {
1312 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState.fNonStroking.fPattern;
1313
1314 // TODO(edisonn): constants
1315 // TODO(edisonn): colored
1316 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001317 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
1318 // it will change the result iterating in reverse
1319 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
1320 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001321
1322 SkRect bounds = path.getBounds();
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001323
1324 // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001325 // TODO(edisonn): don't do that!
1326 bounds.sort();
1327
1328 SkScalar x;
1329 SkScalar y;
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001330
1331 y = bounds.top();
1332 int totalx = 0;
1333 int totaly = 0;
1334 while (y < bounds.bottom()) {
1335 x = bounds.left();
1336 totalx = 0;
1337
1338 while (x < bounds.right()) {
1339 doXObject(pdfContext, canvas, pattern);
1340
edisonn@google.com0f901902013-08-07 11:56:16 +00001341 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(xStep), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001342 totalx += xStep;
1343 x += SkIntToScalar(xStep);
1344 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001345 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(-totalx), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001346
edisonn@google.com0f901902013-08-07 11:56:16 +00001347 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(-yStep));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001348 totaly += yStep;
1349 y += SkIntToScalar(yStep);
1350 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001351 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(totaly));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001352 }
1353 }
1354
1355 // apply matrix
1356 // get xstep, y step, bbox ... for cliping, and bos of the path
1357
1358 PdfOp_Q(pdfContext, canvas, NULL);
1359 canvas->restore();
1360 } else {
1361 paint.setStyle(SkPaint::kFill_Style);
1362 if (evenOdd) {
1363 path.setFillType(SkPath::kEvenOdd_FillType);
1364 }
1365
1366 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1367
1368 canvas->drawPath(path, paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001369 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001370 }
1371
1372 if (stroke) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001373 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0) {
1374 // TODO(edisonn): implement Pattern for strokes
1375 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001376
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001377 paint.setColor(SK_ColorGREEN);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001378
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001379 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1380 canvas->drawPath(path, paint);
1381 } else {
1382 paint.setStyle(SkPaint::kStroke_Style);
1383
1384 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1385
1386 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1387 canvas->drawPath(path, paint);
1388 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001389 }
1390 }
1391
1392 pdfContext->fGraphicsState.fPath.reset();
1393 // todo zoom ... other stuff ?
1394
1395 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1396#ifndef PDF_DEBUG_NO_CLIPING
1397 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1398#endif
1399 }
1400
1401 //pdfContext->fGraphicsState.fClipPath.reset();
1402 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1403
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001404 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001405
1406}
1407
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001408static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001409 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1410}
1411
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001412static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001413 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1414}
1415
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001416static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001417 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1418}
1419
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001420static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001421 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1422}
1423
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001424static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001425 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1426}
1427
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001428static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001429 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1430}
1431
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001432static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001433 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1434}
1435
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001436static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001437 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1438}
1439
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001440static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001441 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1442}
1443
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001444static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001445 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001446 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1447#ifndef PDF_DEBUG_NO_CLIPING
1448 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1449#endif
1450 }
1451
1452 //pdfContext->fGraphicsState.fClipPath.reset();
1453 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1454
1455 pdfContext->fGraphicsState.fPathClosed = true;
1456
1457 return kOK_PdfResult;
1458}
1459
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001460static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001461 pdfContext->fGraphicsState.fTextBlock = true;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001462 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
1463 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001464
1465 return kPartial_PdfResult;
1466}
1467
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001468static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001469 if (!pdfContext->fGraphicsState.fTextBlock) {
1470 return kIgnoreError_PdfResult;
1471 }
1472 // 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 +00001473 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001474}
1475
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001476PdfResult skpdfGraphicsStateApplyFontCore(PdfContext* pdfContext, const SkPdfObject* fontName, double fontSize) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001477#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001478 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001479#endif
1480
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001481 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1482 // TODO(edisonn): try to recover and draw it any way?
1483 return kIgnoreError_PdfResult;
1484 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001485
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001486 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1487 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1488 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1489 // TODO(edisonn): try to recover and draw it any way?
1490 return kIgnoreError_PdfResult;
1491 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001492
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001493 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1494
1495 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1496
1497 if (skfont) {
1498 pdfContext->fGraphicsState.fSkFont = skfont;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001499 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001500 pdfContext->fGraphicsState.fCurFontSize = fontSize;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001501 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001502}
1503
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001504//font size Tf Set the text font, Tf
1505//, to font and the text font size, Tfs, to size. font is the name of a
1506//font resource in the Fontsubdictionary of the current resource dictionary; size is
1507//a number representing a scale factor. There is no initial value for either font or
1508//size; they must be specified explicitly using Tf before any text is shown.
1509static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1510 double fontSize = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1511 SkPdfObject* fontName = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1512 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1513}
1514
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001515static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001516 if (!pdfContext->fGraphicsState.fTextBlock) {
1517 // TODO(edisonn): try to recover and draw it any way?
1518 return kIgnoreError_PdfResult;
1519 }
1520
1521 PdfResult ret = DrawText(pdfContext,
1522 pdfContext->fObjectStack.top(),
1523 canvas);
1524 pdfContext->fObjectStack.pop();
1525
1526 return ret;
1527}
1528
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001529static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001530 if (!pdfContext->fGraphicsState.fTextBlock) {
1531 // TODO(edisonn): try to recover and draw it any way?
1532 return kIgnoreError_PdfResult;
1533 }
1534
1535 PdfOp_T_star(pdfContext, canvas, looper);
1536 // Do not pop, and push, just transfer the param to Tj
1537 return PdfOp_Tj(pdfContext, canvas, looper);
1538}
1539
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001540static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001541 if (!pdfContext->fGraphicsState.fTextBlock) {
1542 // TODO(edisonn): try to recover and draw it any way?
1543 return kIgnoreError_PdfResult;
1544 }
1545
1546 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1547 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1548 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1549
1550 pdfContext->fObjectStack.push(aw);
1551 PdfOp_Tw(pdfContext, canvas, looper);
1552
1553 pdfContext->fObjectStack.push(ac);
1554 PdfOp_Tc(pdfContext, canvas, looper);
1555
1556 pdfContext->fObjectStack.push(str);
1557 PdfOp_quote(pdfContext, canvas, looper);
1558
1559 return kPartial_PdfResult;
1560}
1561
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001562static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001563 if (!pdfContext->fGraphicsState.fTextBlock) {
1564 // TODO(edisonn): try to recover and draw it any way?
1565 return kIgnoreError_PdfResult;
1566 }
1567
edisonn@google.com571c70b2013-07-10 17:09:50 +00001568 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001569 pdfContext->fObjectStack.pop();
1570
edisonn@google.com571c70b2013-07-10 17:09:50 +00001571 if (!array->isArray()) {
1572 return kIgnoreError_PdfResult;
1573 }
1574
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001575 for( int i=0; i<static_cast<int>(array->size()); i++ )
1576 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001577 if( (*array)[i]->isAnyString()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001578 SkPdfObject* obj = (*array)[i];
1579 DrawText(pdfContext,
1580 obj,
1581 canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001582 } else if ((*array)[i]->isNumber()) {
1583 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001584 SkMatrix matrix;
1585 matrix.setAll(SkDoubleToScalar(1),
1586 SkDoubleToScalar(0),
1587 // TODO(edisonn): use writing mode, vertical/horizontal.
1588 SkDoubleToScalar(-dx), // amount is substracted!!!
1589 SkDoubleToScalar(0),
1590 SkDoubleToScalar(1),
1591 SkDoubleToScalar(0),
1592 SkDoubleToScalar(0),
1593 SkDoubleToScalar(0),
1594 SkDoubleToScalar(1));
1595
1596 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1597 }
1598 }
1599 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1600}
1601
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001602static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001603 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->strRef(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001604 return kOK_PdfResult;
1605}
1606
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001607static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001608 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1609}
1610
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001611static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001612 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1613}
1614
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001615static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001616 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001617// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001618
1619 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1620
1621 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001622 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001623 doubles = false;
1624 }
1625
1626#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001627 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001628#endif
1629
1630 for (int i = n - 1; i >= 0 ; i--) {
1631 if (doubles) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001632 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1633// } else {
1634// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001635 }
1636 }
1637
1638 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001639 // TODO(edisonn): do possible field values to enum at parsing time!
1640 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001641 if (colorOperator->fColorSpace.equals("DeviceRGB") || colorOperator->fColorSpace.equals("RGB")) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001642 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]), (U8CPU)(255*c[1]), (U8CPU)(255*c[2])));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001643 }
1644 return kPartial_PdfResult;
1645}
1646
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001647static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001648 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1649}
1650
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001651static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001652 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1653}
1654
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001655static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001656 if (pdfContext->fObjectStack.top()->isName()) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001657 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1658
1659 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001660 SkPdfDictionary* patternResources = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
edisonn@google.com276fed92013-08-01 21:20:47 +00001661
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001662 if (patternResources == NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001663#ifdef PDF_TRACE
1664 printf("ExtGState is NULL!\n");
1665#endif
1666 return kIgnoreError_PdfResult;
1667 }
1668
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001669 colorOperator->setPatternColorSpace(pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001670 }
1671
1672 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1673 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1674
1675 return kPartial_PdfResult;
1676}
1677
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001678static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001679 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1680}
1681
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001682static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001683 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1684}
1685
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001686static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001687 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001688 return kNYI_PdfResult;
1689}
1690
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001691static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001692 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1693}
1694
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001695static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001696 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1697}
1698
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001699static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001700 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1701 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1702 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001703
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001704 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001705 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001706 return kOK_PdfResult;
1707}
1708
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001709static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001710 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1711}
1712
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001713static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001714 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1715}
1716
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001717static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001718 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001719 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1720 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1721 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1722 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001723
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001724 colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001725 // TODO(edisonn): Set color.
1726 return kNYI_PdfResult;
1727}
1728
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001729static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001730 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1731}
1732
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001733static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001734 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1735}
1736
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001737static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001738 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1739 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1740
1741 return kOK_PdfResult;
1742}
1743
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001744static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001745 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1746
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001747 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1748 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1749
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001750 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001751}
1752
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001753static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001754 *looper = new PdfCompatibilitySectionLooper();
1755 return kOK_PdfResult;
1756}
1757
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001758static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001759#ifdef ASSERT_BAD_PDF_OPS
1760 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1761 // have the assert when testing good pdfs.
1762#endif
1763 return kIgnoreError_PdfResult;
1764}
1765
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001766static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001767 *looper = new PdfInlineImageLooper();
1768 return kOK_PdfResult;
1769}
1770
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001771static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001772#ifdef ASSERT_BAD_PDF_OPS
1773 SkASSERT(false); // must be processed in inline image looper, but let's
1774 // have the assert when testing good pdfs.
1775#endif
1776 return kIgnoreError_PdfResult;
1777}
1778
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001779static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001780#ifdef ASSERT_BAD_PDF_OPS
1781 SkASSERT(false); // must be processed in inline image looper, but let's
1782 // have the assert when testing good pdfs.
1783#endif
1784 return kIgnoreError_PdfResult;
1785}
1786
edisonn@google.com24cdf132013-07-30 16:06:12 +00001787
1788// TODO(edisonn): security review here, make sure all parameters are valid, and safe.
1789PdfResult skpdfGraphicsStateApply_ca(PdfContext* pdfContext, double ca) {
1790 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
1791 return kOK_PdfResult;
1792}
1793
1794PdfResult skpdfGraphicsStateApply_CA(PdfContext* pdfContext, double CA) {
1795 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
1796 return kOK_PdfResult;
1797}
1798
1799PdfResult skpdfGraphicsStateApplyLW(PdfContext* pdfContext, double lineWidth) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001800 pdfContext->fGraphicsState.fLineWidth = lineWidth;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001801 return kOK_PdfResult;
1802}
1803
1804PdfResult skpdfGraphicsStateApplyLC(PdfContext* pdfContext, int64_t lineCap) {
1805 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
1806 return kOK_PdfResult;
1807}
1808
1809PdfResult skpdfGraphicsStateApplyLJ(PdfContext* pdfContext, int64_t lineJoin) {
1810 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
1811 return kOK_PdfResult;
1812}
1813
1814PdfResult skpdfGraphicsStateApplyML(PdfContext* pdfContext, double miterLimit) {
1815 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
1816 return kOK_PdfResult;
1817}
1818
1819// TODO(edisonn): implement all rules, as of now 3) and 5) and 6) do not seem suported by skia, but I am not sure
1820/*
18211) [ ] 0 No dash; solid, unbroken lines
18222) [3] 0 3 units on, 3 units off, …
18233) [2] 1 1 on, 2 off, 2 on, 2 off, …
18244) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
18255) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
18266) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
1827 */
1828
1829PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* intervals, SkPdfObject* phase) {
1830 int cnt = intervals->size();
1831 if (cnt >= 256) {
1832 // TODO(edisonn): report error/warning, unsuported;
1833 // TODO(edisonn): alloc memory
1834 return kIgnoreError_PdfResult;
1835 }
1836 for (int i = 0; i < cnt; i++) {
1837 if (!intervals->objAtAIndex(i)->isNumber()) {
1838 // TODO(edisonn): report error/warning
1839 return kIgnoreError_PdfResult;
1840 }
1841 }
1842
edisonn@google.com24cdf132013-07-30 16:06:12 +00001843 double total = 0;
1844 for (int i = 0 ; i < cnt; i++) {
1845 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
1846 total += pdfContext->fGraphicsState.fDashArray[i];
1847 }
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001848 if (cnt & 1) {
1849 if (cnt == 1) {
1850 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
1851 cnt++;
1852 } else {
1853 // TODO(edisonn): report error/warning
1854 return kNYI_PdfResult;
1855 }
1856 }
1857 pdfContext->fGraphicsState.fDashArrayLength = cnt;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001858 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
1859 if (pdfContext->fGraphicsState.fDashPhase == 0) {
1860 // other rules, changes?
1861 pdfContext->fGraphicsState.fDashPhase = total;
1862 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001863
1864 return kOK_PdfResult;
1865}
1866
edisonn@google.com24cdf132013-07-30 16:06:12 +00001867PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* dash) {
1868 // TODO(edisonn): verify input
1869 if (!dash || dash->isArray() || dash->size() != 2 || !dash->objAtAIndex(0)->isArray() || !dash->objAtAIndex(1)->isNumber()) {
1870 // TODO(edisonn): report error/warning
1871 return kIgnoreError_PdfResult;
1872 }
1873 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0), dash->objAtAIndex(1));
1874}
1875
1876void skpdfGraphicsStateApplyFont(PdfContext* pdfContext, SkPdfArray* fontAndSize) {
1877 if (!fontAndSize || fontAndSize->isArray() || fontAndSize->size() != 2 || !fontAndSize->objAtAIndex(0)->isName() || !fontAndSize->objAtAIndex(1)->isNumber()) {
1878 // TODO(edisonn): report error/warning
1879 return;
1880 }
1881 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), fontAndSize->objAtAIndex(1)->numberValue());
1882}
1883
1884
1885//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1886static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1887 double lw = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1888 return skpdfGraphicsStateApplyLW(pdfContext, lw);
1889}
1890
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001891//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 +00001892static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001893 int64_t lc = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1894 return skpdfGraphicsStateApplyLC(pdfContext, lc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001895}
1896
1897//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 +00001898static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001899 double lj = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1900 return skpdfGraphicsStateApplyLJ(pdfContext, lj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001901}
1902
1903//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001904static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001905 double ml = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1906 return skpdfGraphicsStateApplyML(pdfContext, ml);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001907}
1908
1909//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1910//page 155).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001911static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001912 SkPdfObject* phase = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1913 SkPdfObject* array = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001914
edisonn@google.com24cdf132013-07-30 16:06:12 +00001915 if (!array->isArray()) {
1916 return kIgnoreError_PdfResult;
1917 }
1918
1919 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)array, phase);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001920}
1921
1922//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 +00001923static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001924 pdfContext->fObjectStack.pop();
1925
1926 return kNYI_PdfResult;
1927}
1928
1929//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1930//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1931//fies the output device’s default flatness tolerance.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001932static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001933 pdfContext->fObjectStack.pop();
1934
1935 return kNYI_PdfResult;
1936}
1937
edisonn@google.come878e722013-07-29 19:10:58 +00001938SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
1939
1940class InitBlendModes {
1941public:
1942 InitBlendModes() {
1943 // TODO(edisonn): use the python code generator?
1944 // TABLE 7.2 Standard separable blend modes
1945 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
1946 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
1947 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
1948 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
1949 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
1950 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
1951 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
1952 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
1953 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
1954 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
1955 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
1956 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
1957
1958 // TABLE 7.3 Standard nonseparable blend modes
1959 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
1960 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
1961 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
1962 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
1963 }
1964};
1965
1966InitBlendModes _gDummyInniter;
1967
1968SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
1969 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1970 if (gPdfBlendModes.find(blendMode, len, &mode)) {
1971 return mode;
1972 }
1973
1974 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1975}
1976
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001977void skpdfGraphicsStateApplyBM_name(PdfContext* pdfContext, const std::string& blendMode) {
edisonn@google.come878e722013-07-29 19:10:58 +00001978 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.length());
1979 if (mode <= SkXfermode::kLastMode) {
1980 pdfContext->fGraphicsState.fBlendModesLength = 1;
1981 pdfContext->fGraphicsState.fBlendModes[0] = mode;
1982 } else {
1983 // TODO(edisonn): report unknown blend mode
1984 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001985}
1986
1987void skpdfGraphicsStateApplyBM_array(PdfContext* pdfContext, SkPdfArray* blendModes) {
edisonn@google.come878e722013-07-29 19:10:58 +00001988 if (!blendModes || blendModes->isArray() || blendModes->size() == 0 || blendModes->size() > 256) {
1989 // TODO(edisonn): report error/warning
1990 return;
1991 }
1992 SkXfermode::Mode modes[256];
1993 int cnt = blendModes->size();
1994 for (int i = 0; i < cnt; i++) {
1995 SkPdfObject* name = blendModes->objAtAIndex(i);
1996 if (!name->isName()) {
1997 // TODO(edisonn): report error/warning
1998 return;
1999 }
2000 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
2001 if (mode > SkXfermode::kLastMode) {
2002 // TODO(edisonn): report error/warning
2003 return;
2004 }
2005 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002006
edisonn@google.come878e722013-07-29 19:10:58 +00002007 pdfContext->fGraphicsState.fBlendModesLength = cnt;
2008 for (int i = 0; i < cnt; i++) {
2009 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
2010 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002011}
2012
2013void skpdfGraphicsStateApplySMask_dict(PdfContext* pdfContext, SkPdfDictionary* sMask) {
2014 // TODO(edisonn): verify input
edisonn@google.come878e722013-07-29 19:10:58 +00002015 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002016 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
edisonn@google.come878e722013-07-29 19:10:58 +00002017 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
2018 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
2019 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
2020 } else {
2021 // TODO (edisonn): report error/warning
2022 }
2023}
2024
2025void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string& sMask) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002026 if (sMask == "None") {
2027 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002028 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002029 return;
2030 }
2031
edisonn@google.come878e722013-07-29 19:10:58 +00002032 //Next, get the ExtGState Dictionary from the Resource Dictionary:
2033 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2034
2035 if (extGStateDictionary == NULL) {
2036#ifdef PDF_TRACE
2037 printf("ExtGState is NULL!\n");
2038#endif
2039 // TODO (edisonn): report error/warning
2040 return;
2041 }
2042
2043 SkPdfObject* obj = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
2044 if (!obj || !obj->isDictionary()) {
2045 // TODO (edisonn): report error/warning
2046 return;
2047 }
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002048
2049 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002050 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002051
edisonn@google.come878e722013-07-29 19:10:58 +00002052 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002053}
2054
2055void skpdfGraphicsStateApplyAIS(PdfContext* pdfContext, bool alphaSource) {
2056 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2057}
2058
2059
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002060//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2061//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 +00002062static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002063 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002064
2065#ifdef PDF_TRACE
2066 std::string str;
2067#endif
2068
2069 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00002070 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002071
2072 if (extGStateDictionary == NULL) {
2073#ifdef PDF_TRACE
2074 printf("ExtGState is NULL!\n");
2075#endif
2076 return kIgnoreError_PdfResult;
2077 }
2078
edisonn@google.com571c70b2013-07-10 17:09:50 +00002079 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002080
edisonn@google.com571c70b2013-07-10 17:09:50 +00002081 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2082 return kIgnoreError_PdfResult;
2083 }
2084 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002085
2086 // TODO(edisonn): now load all those properties in graphic state.
2087 if (gs == NULL) {
2088 return kIgnoreError_PdfResult;
2089 }
2090
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002091 if (gs->has_LW()) {
2092 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2093 }
2094
2095 if (gs->has_LC()) {
2096 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2097 }
2098
2099 if (gs->has_LJ()) {
2100 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2101 }
2102
2103 if (gs->has_ML()) {
2104 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2105 }
2106
2107 if (gs->has_D()) {
2108 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2109 }
2110
2111 if (gs->has_Font()) {
2112 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2113 }
2114
2115 if (gs->has_BM()) {
2116 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2117 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2118 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2119 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2120 } else {
2121 // TODO(edisonn): report/warn
2122 }
2123 }
2124
2125 if (gs->has_SMask()) {
2126 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2127 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2128 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
2129 skpdfGraphicsStateApplySMask_dict(pdfContext, gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
2130 } else {
2131 // TODO(edisonn): report/warn
2132 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002133 }
2134
2135 if (gs->has_ca()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002136 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002137 }
2138
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002139 if (gs->has_CA()) {
2140 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2141 }
2142
2143 if (gs->has_AIS()) {
2144 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002145 }
2146
edisonn@google.com9a43c182013-08-01 20:06:42 +00002147 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002148}
2149
2150//charSpace Tc Set the character spacing, Tc
2151//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2152//Initial value: 0.
2153PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002154 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002155 pdfContext->fGraphicsState.fCharSpace = charSpace;
2156
2157 return kOK_PdfResult;
2158}
2159
2160//wordSpace Tw Set the word spacing, T
2161//w
2162//, to wordSpace, which is a number expressed in unscaled
2163//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2164//value: 0.
2165PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002166 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002167 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2168
2169 return kOK_PdfResult;
2170}
2171
2172//scale Tz Set the horizontal scaling, Th
2173//, to (scale ˜ 100). scale is a number specifying the
2174//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002175static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002176 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002177
2178 return kNYI_PdfResult;
2179}
2180
2181//render Tr Set the text rendering mode, T
2182//mode, to render, which is an integer. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002183static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002184 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002185
2186 return kNYI_PdfResult;
2187}
2188//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2189//units. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002190static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002191 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002192
2193 return kNYI_PdfResult;
2194}
2195
2196//wx wy d0
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002197static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002198 pdfContext->fObjectStack.pop();
2199 pdfContext->fObjectStack.pop();
2200
2201 return kNYI_PdfResult;
2202}
2203
2204//wx wy llx lly urx ury d1
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002205static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002206 pdfContext->fObjectStack.pop();
2207 pdfContext->fObjectStack.pop();
2208 pdfContext->fObjectStack.pop();
2209 pdfContext->fObjectStack.pop();
2210 pdfContext->fObjectStack.pop();
2211 pdfContext->fObjectStack.pop();
2212
2213 return kNYI_PdfResult;
2214}
2215
2216//name sh
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002217static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002218 pdfContext->fObjectStack.pop();
2219
2220 return kNYI_PdfResult;
2221}
2222
2223//name Do
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002224static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002225 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002226
edisonn@google.com571c70b2013-07-10 17:09:50 +00002227 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002228
2229 if (xObject == NULL) {
2230#ifdef PDF_TRACE
2231 printf("XObject is NULL!\n");
2232#endif
2233 return kIgnoreError_PdfResult;
2234 }
2235
edisonn@google.com571c70b2013-07-10 17:09:50 +00002236 SkPdfObject* value = xObject->get(name);
2237 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002238
2239#ifdef PDF_TRACE
2240// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002241// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002242#endif
2243
edisonn@google.com571c70b2013-07-10 17:09:50 +00002244 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002245}
2246
2247//tag MP Designate a marked-content point. tag is a name object indicating the role or
2248//significance of the point.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002249static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002250 pdfContext->fObjectStack.pop();
2251
2252 return kNYI_PdfResult;
2253}
2254
2255//tag properties DP Designate a marked-content point with an associated property list. tag is a
2256//name object indicating the role or significance of the point; properties is
2257//either an inline dictionary containing the property list or a name object
2258//associated with it in the Properties subdictionary of the current resource
2259//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002260static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002261 pdfContext->fObjectStack.pop();
2262 pdfContext->fObjectStack.pop();
2263
2264 return kNYI_PdfResult;
2265}
2266
2267//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2268//tag is a name object indicating the role or significance of the sequence.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002269static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002270 pdfContext->fObjectStack.pop();
2271
2272 return kNYI_PdfResult;
2273}
2274
2275//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2276//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2277//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 +00002278static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002279 pdfContext->fObjectStack.pop();
2280 pdfContext->fObjectStack.pop();
2281
2282 return kNYI_PdfResult;
2283}
2284
2285//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002286static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002287 return kNYI_PdfResult;
2288}
2289
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002290static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002291 static bool gInitialized = false;
2292 if (gInitialized) {
2293 return;
2294 }
2295
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002296 gPdfOps.set("q", PdfOp_q);
2297 gPdfOps.set("Q", PdfOp_Q);
2298 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002299
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002300 gPdfOps.set("TD", PdfOp_TD);
2301 gPdfOps.set("Td", PdfOp_Td);
2302 gPdfOps.set("Tm", PdfOp_Tm);
2303 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002304
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002305 gPdfOps.set("m", PdfOp_m);
2306 gPdfOps.set("l", PdfOp_l);
2307 gPdfOps.set("c", PdfOp_c);
2308 gPdfOps.set("v", PdfOp_v);
2309 gPdfOps.set("y", PdfOp_y);
2310 gPdfOps.set("h", PdfOp_h);
2311 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002312
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002313 gPdfOps.set("S", PdfOp_S);
2314 gPdfOps.set("s", PdfOp_s);
2315 gPdfOps.set("f", PdfOp_f);
2316 gPdfOps.set("F", PdfOp_F);
2317 gPdfOps.set("f*", PdfOp_f_star);
2318 gPdfOps.set("B", PdfOp_B);
2319 gPdfOps.set("B*", PdfOp_B_star);
2320 gPdfOps.set("b", PdfOp_b);
2321 gPdfOps.set("b*", PdfOp_b_star);
2322 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002323
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002324 gPdfOps.set("BT", PdfOp_BT);
2325 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002326
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002327 gPdfOps.set("Tj", PdfOp_Tj);
2328 gPdfOps.set("'", PdfOp_quote);
2329 gPdfOps.set("\"", PdfOp_doublequote);
2330 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002331
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002332 gPdfOps.set("CS", PdfOp_CS);
2333 gPdfOps.set("cs", PdfOp_cs);
2334 gPdfOps.set("SC", PdfOp_SC);
2335 gPdfOps.set("SCN", PdfOp_SCN);
2336 gPdfOps.set("sc", PdfOp_sc);
2337 gPdfOps.set("scn", PdfOp_scn);
2338 gPdfOps.set("G", PdfOp_G);
2339 gPdfOps.set("g", PdfOp_g);
2340 gPdfOps.set("RG", PdfOp_RG);
2341 gPdfOps.set("rg", PdfOp_rg);
2342 gPdfOps.set("K", PdfOp_K);
2343 gPdfOps.set("k", PdfOp_k);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002344
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002345 gPdfOps.set("W", PdfOp_W);
2346 gPdfOps.set("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002347
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002348 gPdfOps.set("BX", PdfOp_BX);
2349 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002350
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002351 gPdfOps.set("BI", PdfOp_BI);
2352 gPdfOps.set("ID", PdfOp_ID);
2353 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002354
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002355 gPdfOps.set("w", PdfOp_w);
2356 gPdfOps.set("J", PdfOp_J);
2357 gPdfOps.set("j", PdfOp_j);
2358 gPdfOps.set("M", PdfOp_M);
2359 gPdfOps.set("d", PdfOp_d);
2360 gPdfOps.set("ri", PdfOp_ri);
2361 gPdfOps.set("i", PdfOp_i);
2362 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002363
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002364 gPdfOps.set("Tc", PdfOp_Tc);
2365 gPdfOps.set("Tw", PdfOp_Tw);
2366 gPdfOps.set("Tz", PdfOp_Tz);
2367 gPdfOps.set("TL", PdfOp_TL);
2368 gPdfOps.set("Tf", PdfOp_Tf);
2369 gPdfOps.set("Tr", PdfOp_Tr);
2370 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002371
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002372 gPdfOps.set("d0", PdfOp_d0);
2373 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002374
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002375 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002376
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002377 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002378
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002379 gPdfOps.set("MP", PdfOp_MP);
2380 gPdfOps.set("DP", PdfOp_DP);
2381 gPdfOps.set("BMC", PdfOp_BMC);
2382 gPdfOps.set("BDC", PdfOp_BDC);
2383 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002384
2385 gInitialized = true;
2386}
2387
2388class InitPdfOps {
2389public:
2390 InitPdfOps() {
2391 initPdfOperatorRenderes();
2392 }
2393};
2394
2395InitPdfOps gInitPdfOps;
2396
2397void reportPdfRenderStats() {
2398 std::map<std::string, int>::iterator iter;
2399
2400 for (int i = 0 ; i < kCount_PdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002401 SkTDict<int>::Iter iter(gRenderStats[i]);
2402 const char* key;
2403 int value = 0;
2404 while ((key = iter.next(&value)) != NULL) {
2405 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002406 }
2407 }
2408}
2409
2410PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002411 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002412 {
2413 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002414 PdfOperatorRenderer pdfOperatorRenderer = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002415 if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) && pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002416 // caller, main work is done by pdfOperatorRenderer(...)
2417 PdfTokenLooper* childLooper = NULL;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002418 PdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
2419
2420 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002421 gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
2422 gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002423
2424 if (childLooper) {
2425 childLooper->setUp(this);
2426 childLooper->loop();
2427 delete childLooper;
2428 }
2429 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002430 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002431 gRenderStats[kUnsupported_PdfResult].find(token.fKeyword, token.fKeywordLength, &cnt);
2432 gRenderStats[kUnsupported_PdfResult].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002433 }
2434 }
2435 else if (token.fType == kObject_TokenType)
2436 {
2437 fPdfContext->fObjectStack.push( token.fObject );
2438 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002439 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002440 // TODO(edisonn): deine or use assert not reached
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002441 return kIgnoreError_PdfResult;
2442 }
2443 return kOK_PdfResult;
2444}
2445
2446void PdfMainLooper::loop() {
2447 PdfToken token;
2448 while (readToken(fTokenizer, &token)) {
2449 consumeToken(token);
2450 }
2451}
2452
2453PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002454 SkASSERT(false);
2455 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002456}
2457
2458void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002459 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002460}
2461
2462PdfResult PdfInlineImageLooper::done() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002463 return kNYI_PdfResult;
2464}
2465
2466PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2467 return fParent->consumeToken(token);
2468}
2469
2470void PdfCompatibilitySectionLooper::loop() {
2471 // TODO(edisonn): save stacks position, or create a new stack?
2472 // TODO(edisonn): what happens if we pop out more variables then when we started?
2473 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2474 // pop-ing too much will not affect outside the section.
2475 PdfToken token;
2476 while (readToken(fTokenizer, &token)) {
2477 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2478 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2479 looper->setUp(this);
2480 looper->loop();
2481 delete looper;
2482 } else {
2483 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
2484 fParent->consumeToken(token);
2485 }
2486 }
2487 // TODO(edisonn): restore stack.
2488}
2489
2490// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2491// TODO(edisonn): Add API for Forms viewing and editing
2492// e.g. SkBitmap getPage(int page);
2493// int formsCount();
2494// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2495// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2496// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2497// resources needed, so the preview is fast.
2498// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2499// references automatically.
2500
edisonn@google.com222382b2013-07-10 22:33:10 +00002501PdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00002502
edisonn@google.com444e25a2013-07-11 15:20:50 +00002503bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00002504 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002505 return false;
2506 }
2507
edisonn@google.com222382b2013-07-10 22:33:10 +00002508 if (page < 0 || page >= pages()) {
2509 return false;
2510 }
2511
edisonn@google.com222382b2013-07-10 22:33:10 +00002512 PdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002513
edisonn@google.com222382b2013-07-10 22:33:10 +00002514 pdfContext.fOriginalMatrix = SkMatrix::I();
2515 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2516
2517 gPdfContext = &pdfContext;
2518
2519 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00002520 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00002521 SkScalar w = dst.width();
2522 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00002523
edisonn@google.com444e25a2013-07-11 15:20:50 +00002524 SkScalar wp = fPdfDoc->MediaBox(page).width();
2525 SkScalar hp = fPdfDoc->MediaBox(page).height();
2526
2527 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 +00002528// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2529
2530 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2531 // TODO(edisonn): add flagg for no clipping.
2532 // Use larger image to make sure we do not draw anything outside of page
2533 // could be used in tests.
2534
2535#ifdef PDF_DEBUG_3X
2536 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)};
2537#else
2538 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2539#endif
2540 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2541 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2542
2543 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2544 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2545
2546 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2547 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2548
2549 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2550 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2551
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002552 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
edisonn@google.com0f901902013-08-07 11:56:16 +00002553 pdfContext.fGraphicsState.fContentStreamMatrix = pdfContext.fOriginalMatrix;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002554 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
2555 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
edisonn@google.com222382b2013-07-10 22:33:10 +00002556
edisonn@google.com222382b2013-07-10 22:33:10 +00002557#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00002558 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00002559#endif
2560
edisonn@google.com444e25a2013-07-11 15:20:50 +00002561 canvas->setMatrix(pdfContext.fOriginalMatrix);
2562
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002563 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2564
2565 // TODO(edisonn:) erase with white before draw?
edisonn@google.com222382b2013-07-10 22:33:10 +00002566// SkPaint paint;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002567// paint.setColor(SK_ColorWHITE);
edisonn@google.com222382b2013-07-10 22:33:10 +00002568// canvas->drawRect(rect, paint);
2569
edisonn@google.com222382b2013-07-10 22:33:10 +00002570
2571 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002572 return true;
2573}
edisonn@google.com222382b2013-07-10 22:33:10 +00002574
2575bool SkPdfRenderer::load(const SkString inputFileName) {
2576 unload();
2577
2578 // TODO(edisonn): create static function that could return NULL if there are errors
2579 fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00002580 if (fPdfDoc->pages() == 0) {
2581 delete fPdfDoc;
2582 fPdfDoc = NULL;
2583 }
edisonn@google.com222382b2013-07-10 22:33:10 +00002584
2585 return fPdfDoc != NULL;
2586}
2587
edisonn@google.com147adb12013-07-24 15:56:19 +00002588bool SkPdfRenderer::load(SkStream* stream) {
2589 unload();
2590
2591 // TODO(edisonn): create static function that could return NULL if there are errors
2592 fPdfDoc = new SkNativeParsedPDF(stream);
2593 if (fPdfDoc->pages() == 0) {
2594 delete fPdfDoc;
2595 fPdfDoc = NULL;
2596 }
2597
2598 return fPdfDoc != NULL;
2599}
2600
2601
edisonn@google.com222382b2013-07-10 22:33:10 +00002602int SkPdfRenderer::pages() const {
2603 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2604}
2605
2606void SkPdfRenderer::unload() {
2607 delete fPdfDoc;
2608 fPdfDoc = NULL;
2609}
2610
2611SkRect SkPdfRenderer::MediaBox(int page) const {
2612 SkASSERT(fPdfDoc);
2613 return fPdfDoc->MediaBox(page);
2614}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002615
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002616size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002617 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2618}
edisonn@google.com147adb12013-07-24 15:56:19 +00002619
2620bool SkPDFNativeRenderToBitmap(SkStream* stream,
2621 SkBitmap* output,
2622 int page,
2623 SkPdfContent content,
2624 double dpi) {
2625 SkASSERT(page >= 0);
2626 SkPdfRenderer renderer;
2627 renderer.load(stream);
2628 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
2629 return false;
2630 }
2631
2632 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
2633
2634 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2635 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2636
2637 rect = SkRect::MakeWH(width, height);
2638
2639 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
2640
2641 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*output)));
2642 SkCanvas canvas(device);
2643
2644 return renderer.renderPage(page, &canvas, rect);
2645}