blob: 85e3b6e3655214b0b0bef17faf345d21e1f72896 [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
edisonn@google.com6e49c342013-06-27 20:03:43 +0000393 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000394 canvas->restore();
395
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000396 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000397}
398
399// TODO(edisonn): create header files with declarations!
400PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
401PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
402PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
403PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
404
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000405// TODO(edisonn): perf!!!
406
407static SkColorTable* getGrayColortable() {
408 static SkColorTable* grayColortable = NULL;
409 if (grayColortable == NULL) {
410 SkPMColor* colors = new SkPMColor[256];
411 for (int i = 0 ; i < 256; i++) {
412 colors[i] = SkPreMultiplyARGB(255, i, i, i);
413 }
414 grayColortable = new SkColorTable(colors, 256);
415 }
416 return grayColortable;
417}
418
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000419static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream, size_t uncompressedStreamLength,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000420 int width, int height, int bytesPerLine,
421 int bpc, const std::string& colorSpace,
422 bool transparencyMask) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000423 SkBitmap* bitmap = new SkBitmap();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000424
edisonn@google.com571c70b2013-07-10 17:09:50 +0000425 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000426//#define MAX_COMPONENTS 10
427
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000428 // TODO(edisonn): assume start of lines are aligned at 32 bits?
429 // Is there a faster way to load the uncompressed stream into a bitmap?
430
431 // minimal support for now
432 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
433 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
434
435 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000436 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000437 for (int w = 0 ; w < width; w++) {
438 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
439 uncompressedStream[3 * w + 1],
440 uncompressedStream[3 * w + 2]);
441 i++;
442 }
443 uncompressedStream += bytesPerLine;
444 }
445
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000446 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
447 bitmap->setPixels(uncompressedStreamArgb);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000448 }
449 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
450 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
451
452 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000453 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000454 for (int w = 0 ; w < width; w++) {
455 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
456 uncompressedStream[w];
457 i++;
458 }
459 uncompressedStream += bytesPerLine;
460 }
461
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000462 bitmap->setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000463 width, height);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000464 bitmap->setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000465 }
466
467 // TODO(edisonn): Report Warning, NYI, or error
468 return bitmap;
469}
470
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000471// utils
472
473// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
474// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
475// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
476// skia format, through a table
477
478// this functions returns the image, it does not look at the smask.
479
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000480static SkBitmap* getImageFromObjectCore(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000481 if (image == NULL || !image->hasStream()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000482 // TODO(edisonn): report warning to be used in testing.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000483 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000484 }
485
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000486 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc);
487 int64_t width = image->Width(pdfContext->fPdfDoc);
488 int64_t height = image->Height(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000489 std::string colorSpace = "DeviceRGB";
490
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000491 bool indexed = false;
492 SkPMColor colors[256];
493 int cnt = 0;
494
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000495 // TODO(edisonn): color space can be an array too!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000496 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
497 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000498 } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) {
499 SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc);
500 if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") &&
501 (array->objAtAIndex(1)->isName("DeviceRGB") || array->objAtAIndex(1)->isName("RGB")) &&
502 array->objAtAIndex(2)->isInteger() &&
503 array->objAtAIndex(3)->isHexString()
504 ) {
505 // TODO(edisonn): suport only DeviceRGB for now.
506 indexed = true;
507 cnt = array->objAtAIndex(2)->intValue() + 1;
508 if (cnt > 256) {
509 // TODO(edionn): report NYIs
510 return NULL;
511 }
512 SkColorTable colorTable(cnt);
513 NotOwnedString data = array->objAtAIndex(3)->strRef();
514 if (data.fBytes != (unsigned int)cnt * 3) {
515 // TODO(edionn): report error/warning
516 return NULL;
517 }
518 for (int i = 0 ; i < cnt; i++) {
519 colors[i] = SkPreMultiplyARGB(0xff, data.fBuffer[3 * i], data.fBuffer[3 * i + 1], data.fBuffer[3 * i + 2]);
520 }
521 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000522 }
523
524/*
525 bool imageMask = image->imageMask();
526
527 if (imageMask) {
528 if (bpc != 0 && bpc != 1) {
529 // TODO(edisonn): report warning to be used in testing.
530 return SkBitmap();
531 }
532 bpc = 1;
533 }
534*/
535
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000536 const unsigned char* uncompressedStream = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000537 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000538
edisonn@google.com571c70b2013-07-10 17:09:50 +0000539 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000540
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000541 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000542 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000543 // TODO(edisonn): report warning to be used in testing.
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000544 return NULL;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000545 }
546
edisonn@google.com571c70b2013-07-10 17:09:50 +0000547 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
548
549 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
550 streamDict->getFilterAsName(NULL) == "DCTDecode") ||
551 (streamDict->isFilterAArray(NULL) &&
552 streamDict->getFilterAsArray(NULL)->size() > 0 &&
553 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
554 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() == "DCTDecode"))) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000555 SkBitmap* bitmap = new SkBitmap();
556 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000557 return bitmap;
558 }
559
560
561
562 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
563// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
564// obj.GetDictionary().GetKey(PdfName("Filter")));
565// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
566// value = resolveReferenceObject(pdfContext->fPdfDoc,
567// &value->GetArray()[0]);
568// }
569// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
570// SkStream stream = SkStream::
571// SkImageDecoder::Factory()
572// }
573
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000574 // TODO(edisonn): assumes RGB for now, since it is the only onwe implemented
575 if (indexed) {
576 SkBitmap* bitmap = new SkBitmap();
577 bitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
578 SkColorTable* colorTable = new SkColorTable(colors, cnt);
579 bitmap->setPixels((void*)uncompressedStream, colorTable);
580 return bitmap;
581 }
582
edisonn@google.com96ba3aa2013-07-28 20:04:35 +0000583 int bytesPerLine = (int)(uncompressedStreamLength / height);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000584#ifdef PDF_TRACE
585 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000586 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000587 }
588#endif
589
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000590 SkBitmap* bitmap = transferImageStreamToBitmap(
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000591 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000592 (int)width, (int)height, bytesPerLine,
593 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000594 transparencyMask);
595
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000596 return bitmap;
597}
598
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000599static SkBitmap* getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
600 if (!transparencyMask) {
601 if (!image->hasData(SkPdfObject::kBitmap_Data)) {
602 SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask);
603 image->setData(bitmap, SkPdfObject::kBitmap_Data);
604 }
605 return (SkBitmap*) image->data(SkPdfObject::kBitmap_Data);
606 } else {
607 return getImageFromObjectCore(pdfContext, image, transparencyMask);
608 }
609}
610
611static SkBitmap* getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000612 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000613
614 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000615 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000616 }
617
618 // TODO(edisonn): implement GS SMask. Default to empty right now.
619 return pdfContext->fGraphicsState.fSMask;
620}
621
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000622static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000623 if (skpdfimage == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000624 return kIgnoreError_PdfResult;
625 }
626
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000627 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
628 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000629
630 canvas->save();
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000631 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000632
edisonn@google.com571c70b2013-07-10 17:09:50 +0000633 SkScalar z = SkIntToScalar(0);
634 SkScalar one = SkIntToScalar(1);
635
636 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
637 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
638 SkMatrix flip;
639 SkAssertResult(flip.setPolyToPoly(from, to, 4));
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000640 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000641 solveImageFlip.preConcat(flip);
642 canvas->setMatrix(solveImageFlip);
edisonn@google.com0f901902013-08-07 11:56:16 +0000643
644#ifdef PDF_TRACE
645 SkPoint final[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
646 solveImageFlip.mapPoints(final, 4);
647 printf("IMAGE rect = ");
648 for (int i = 0; i < 4; i++) {
649 printf("(%f %f) ", SkScalarToDouble(final[i].x()), SkScalarToDouble(final[i].y()));
650 }
651 printf("\n");
652#endif // PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +0000653
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000654 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
655
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000656 // TODO(edisonn): soft mask type? alpha/luminosity.
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000657 SkPaint paint;
658 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
659
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000660 if (!sMask || sMask->empty()) {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000661 canvas->drawBitmapRect(*image, dst, &paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000662 } else {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000663 canvas->saveLayer(&dst, &paint);
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000664 canvas->drawBitmapRect(*image, dst, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000665 SkPaint xfer;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000666 // TODO(edisonn): is the blend mode specified already implicitly/explicitly in pdf?
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000667 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000668 canvas->drawBitmapRect(*sMask, dst, &xfer);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000669 canvas->restore();
670 }
671
672 canvas->restore();
673
674 return kPartial_PdfResult;
675}
676
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000677//TODO(edisonn): options for implementing isolation and knockout
678// 1) emulate them (current solution)
679// PRO: simple
680// CON: will need to use readPixels, which means serious perf issues
681// 2) Compile a plan for an array of matrixes, compose the result at the end
682// PRO: might be faster then 1, no need to readPixels
683// CON: multiple drawings (but on smaller areas), pay a price at loading pdf to compute a pdf draw plan
684// on average, a load with empty draw is 100ms on all the skps we have, for complete sites
685// 3) support them natively in SkCanvas
686// PRO: simple
687// CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1)
688// 4) compile a plan using pathops, and render once without any fancy rules with backdrop
689// PRO: simple, fast
690// CON: pathops must be bug free first + time to compute new paths
691// pay a price at loading pdf to compute a pdf draw plan
692// 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 +0000693// 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 +0000694
695
696// TODO(edisonn): draw plan from point! - list of draw ops of a point, like a tree!
697// TODO(edisonn): Minimal PDF to draw some points - remove everything that it is not needed, save pdf uncompressed
698
699
700
701static void doGroup_before(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup, bool page) {
702 SkRect bboxOrig = bbox;
703 SkBitmap backdrop;
704 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
705// bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000706 SkPaint paint;
707 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
708 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000709}
710
edisonn@google.com251176e2013-08-01 13:24:00 +0000711// TODO(edisonn): non isolation implemented in skia
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000712//static void doGroup_after(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, SkPdfTransparencyGroupDictionary* tgroup) {
edisonn@google.com251176e2013-08-01 13:24:00 +0000713// if not isolated
714// canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000715//}
716
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000717static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000718 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000719 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000720 }
721
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000722 if (!skobj->has_BBox()) {
723 return kIgnoreError_PdfResult;
724 }
725
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000726 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000727
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000728
edisonn@google.com571c70b2013-07-10 17:09:50 +0000729 if (skobj->Resources(pdfContext->fPdfDoc)) {
730 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000731 }
732
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000733 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000734
edisonn@google.com571c70b2013-07-10 17:09:50 +0000735 if (skobj->has_Matrix()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000736 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000737 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
738 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
739 pdfContext->fGraphicsState.fMatrixTm = matrix;
740 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000741 // TODO(edisonn) reset matrixTm and matricTlm also?
742 }
743
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000744 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com0f901902013-08-07 11:56:16 +0000745 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000746
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000747 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000748
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000749 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
750 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000751
752 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
753 // For this PdfContentsTokenizer needs to be extended.
754
edisonn@google.come878e722013-07-29 19:10:58 +0000755 // This is a group?
756 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000757 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
758 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
edisonn@google.come878e722013-07-29 19:10:58 +0000759 }
760
edisonn@google.com571c70b2013-07-10 17:09:50 +0000761 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000762
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000763 SkPdfNativeTokenizer* tokenizer =
764 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000765 if (tokenizer != NULL) {
766 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
767 looper.loop();
768 delete tokenizer;
769 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000770
771 // TODO(edisonn): should we restore the variable stack at the same state?
772 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000773
774 if (skobj->has_Group()) {
775 canvas->restore();
776 }
777
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000778 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000779 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000780}
781
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000782
783// TODO(edisonn): Extract a class like ObjWithStream
784static PdfResult doXObject_Pattern(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1PatternDictionary* skobj) {
785 if (!skobj || !skobj->hasStream()) {
786 return kIgnoreError_PdfResult;
787 }
788
789 if (!skobj->has_BBox()) {
790 return kIgnoreError_PdfResult;
791 }
792
793 PdfOp_q(pdfContext, canvas, NULL);
794
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000795
796 if (skobj->Resources(pdfContext->fPdfDoc)) {
797 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
798 }
799
edisonn@google.com0f901902013-08-07 11:56:16 +0000800 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000801
802 if (skobj->has_Matrix()) {
edisonn@google.com0f901902013-08-07 11:56:16 +0000803 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000804 }
805
edisonn@google.com0f901902013-08-07 11:56:16 +0000806 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000807
edisonn@google.com0f901902013-08-07 11:56:16 +0000808 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix);
809 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000810
811 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
812 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
813
814 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
815 // For this PdfContentsTokenizer needs to be extended.
816
817 SkPdfStream* stream = (SkPdfStream*)skobj;
818
819 SkPdfNativeTokenizer* tokenizer =
820 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
821 if (tokenizer != NULL) {
822 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
823 looper.loop();
824 delete tokenizer;
825 }
826
827 // TODO(edisonn): should we restore the variable stack at the same state?
828 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
829
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000830 PdfOp_Q(pdfContext, canvas, NULL);
831 return kPartial_PdfResult;
832}
833
834
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000835//static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
836// return kNYI_PdfResult;
837//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000838
edisonn@google.com571c70b2013-07-10 17:09:50 +0000839PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
840 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000841 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000842 }
843
844 PdfOp_q(pdfContext, canvas, NULL);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000845
846 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
847 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000848 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000849
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000850 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.come57c62d2013-08-07 18:04:15 +0000851 pdfContext->fGraphicsState.fCTM.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000852
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000853 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000854
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000855 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000856
857 SkRect rm = bBox;
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000858 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000859
860 SkTraceRect(rm, "bbox mapped");
861
862 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
863
864 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
865 // For this PdfContentsTokenizer needs to be extended.
866
edisonn@google.com571c70b2013-07-10 17:09:50 +0000867 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000868
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000869 SkPdfNativeTokenizer* tokenizer =
870 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000871 if (tokenizer != NULL) {
872 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
873 looper.loop();
874 delete tokenizer;
875 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000876
877 // TODO(edisonn): should we restore the variable stack at the same state?
878 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000879 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000880
881 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000882}
883
884
edisonn@google.com571c70b2013-07-10 17:09:50 +0000885// TODO(edisonn): make sure the pointer is unique
886std::set<const SkPdfObject*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000887
888class CheckRecursiveRendering {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000889 const SkPdfObject* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000890public:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000891 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000892 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000893 }
894
895 ~CheckRecursiveRendering() {
896 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000897 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000898 }
899
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000900 static bool IsInRendering(const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000901 return gInRendering.find(obj) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000902 }
903};
904
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000905static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000906 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000907 // Oops, corrupt PDF!
908 return kIgnoreError_PdfResult;
909 }
910
edisonn@google.com571c70b2013-07-10 17:09:50 +0000911 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000912
edisonn@google.com571c70b2013-07-10 17:09:50 +0000913 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000914 {
915 case kImageDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000916 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000917 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000918 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000919 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
920 //return doXObject_PS(skxobj.asPS());
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000921 default: {
922 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != kNone_SkPdfObjectType) {
923 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
924 return doXObject_Pattern(pdfContext, canvas, pattern);
925 }
926 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000927 }
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000928 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000929}
930
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000931static PdfResult doPage(PdfContext* pdfContext, SkCanvas* canvas, SkPdfPageObjectDictionary* skobj) {
932 if (!skobj) {
933 return kIgnoreError_PdfResult;
934 }
935
936 if (!skobj->isContentsAStream(pdfContext->fPdfDoc)) {
937 return kNYI_PdfResult;
938 }
939
940 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
941
942 if (!stream) {
943 return kIgnoreError_PdfResult;
944 }
945
946 if (CheckRecursiveRendering::IsInRendering(skobj)) {
947 // Oops, corrupt PDF!
948 return kIgnoreError_PdfResult;
949 }
950 CheckRecursiveRendering checkRecursion(skobj);
951
952
953 PdfOp_q(pdfContext, canvas, NULL);
954
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000955
956 if (skobj->Resources(pdfContext->fPdfDoc)) {
957 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
958 }
959
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000960 // TODO(edisonn): MediaBox can be inherited!!!!
961 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000962 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000963 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
964 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
965 } else {
966 canvas->save();
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000967 }
968
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000969
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000970 SkPdfNativeTokenizer* tokenizer =
971 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
972 if (tokenizer != NULL) {
973 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
974 looper.loop();
975 delete tokenizer;
976 }
977
978 // TODO(edisonn): should we restore the variable stack at the same state?
979 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
980 canvas->restore();
981 PdfOp_Q(pdfContext, canvas, NULL);
982 return kPartial_PdfResult;
983}
984
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000985PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
986 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
987 canvas->save();
988 return kOK_PdfResult;
989}
990
991PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
992 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
993 pdfContext->fStateStack.pop();
994 canvas->restore();
995 return kOK_PdfResult;
996}
997
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000998static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000999 double array[6];
1000 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001001 array[5 - i] = pdfContext->fObjectStack.top()->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001002 pdfContext->fObjectStack.pop();
1003 }
1004
1005 // a b
1006 // c d
1007 // e f
1008
1009 // 0 1
1010 // 2 3
1011 // 4 5
1012
1013 // sx ky
1014 // kx sy
1015 // tx ty
1016 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1017
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001018 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001019
1020#ifdef PDF_TRACE
1021 printf("cm ");
1022 for (int i = 0 ; i < 6 ; i++) {
1023 printf("%f ", array[i]);
1024 }
1025 printf("\n");
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001026 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001027#endif
1028
1029 return kOK_PdfResult;
1030}
1031
1032//leading TL Set the text leading, Tl
1033//, to leading, which is a number expressed in unscaled text
1034//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001035static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001036 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001037
1038 pdfContext->fGraphicsState.fTextLeading = ty;
1039
1040 return kOK_PdfResult;
1041}
1042
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001043static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001044#ifdef PDF_TRACE
1045 printf("stack size = %i\n", (int)pdfContext->fObjectStack.size());
1046#endif
edisonn@google.com571c70b2013-07-10 17:09:50 +00001047 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001048 SkPdfObject* obj = pdfContext->fObjectStack.top();
1049 obj = obj;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001050 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001051
edisonn@google.come57c62d2013-08-07 18:04:15 +00001052 double array[6] = {1, 0, 0, 1, tx, -ty};
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001053 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1054
1055 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1056 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1057
1058 return kPartial_PdfResult;
1059}
1060
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001061static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001062 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1063 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001064
edisonn@google.com571c70b2013-07-10 17:09:50 +00001065 // TODO(edisonn): Create factory methods or constructors so native is hidden
1066 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001067 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001068
1069 PdfOp_TL(pdfContext, canvas, looper);
1070
edisonn@google.com571c70b2013-07-10 17:09:50 +00001071 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001072 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001073
edisonn@google.com571c70b2013-07-10 17:09:50 +00001074 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001075 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001076
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001077 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1078
1079 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
1080 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001081
1082 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001083}
1084
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001085static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001086 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1087 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1088 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1089 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1090 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1091 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001092
1093 double array[6];
1094 array[0] = a;
1095 array[1] = b;
1096 array[2] = c;
1097 array[3] = d;
1098 array[4] = e;
1099 array[5] = f;
1100
1101 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001102 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
edisonn@google.come57c62d2013-08-07 18:04:15 +00001103 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001104
1105 // TODO(edisonn): Text positioning.
1106 pdfContext->fGraphicsState.fMatrixTm = matrix;
1107 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1108
1109 return kPartial_PdfResult;
1110}
1111
1112//— T* Move to the start of the next line. This operator has the same effect as the code
1113//0 Tl Td
1114//where Tl is the current leading parameter in the text state
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001115static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001116 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
1117 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001118
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001119 pdfContext->fObjectStack.push(zero);
1120 pdfContext->fObjectStack.push(tl);
1121
1122 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1123
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001124 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001125}
1126
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001127static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001128 if (pdfContext->fGraphicsState.fPathClosed) {
1129 pdfContext->fGraphicsState.fPath.reset();
1130 pdfContext->fGraphicsState.fPathClosed = false;
1131 }
1132
edisonn@google.com571c70b2013-07-10 17:09:50 +00001133 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1134 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001135
1136 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1137 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1138
1139 return kOK_PdfResult;
1140}
1141
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001142static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001143 if (pdfContext->fGraphicsState.fPathClosed) {
1144 pdfContext->fGraphicsState.fPath.reset();
1145 pdfContext->fGraphicsState.fPathClosed = false;
1146 }
1147
edisonn@google.com571c70b2013-07-10 17:09:50 +00001148 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1149 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001150
1151 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1152 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1153
1154 return kOK_PdfResult;
1155}
1156
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001157static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001158 if (pdfContext->fGraphicsState.fPathClosed) {
1159 pdfContext->fGraphicsState.fPath.reset();
1160 pdfContext->fGraphicsState.fPathClosed = false;
1161 }
1162
edisonn@google.com571c70b2013-07-10 17:09:50 +00001163 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1164 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1165 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1166 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1167 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1168 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001169
1170 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1171 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1172 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1173
1174 pdfContext->fGraphicsState.fCurPosX = x3;
1175 pdfContext->fGraphicsState.fCurPosY = y3;
1176
1177 return kOK_PdfResult;
1178}
1179
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001180static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001181 if (pdfContext->fGraphicsState.fPathClosed) {
1182 pdfContext->fGraphicsState.fPath.reset();
1183 pdfContext->fGraphicsState.fPathClosed = false;
1184 }
1185
edisonn@google.com571c70b2013-07-10 17:09:50 +00001186 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1187 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1188 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1189 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001190 double y1 = pdfContext->fGraphicsState.fCurPosY;
1191 double x1 = pdfContext->fGraphicsState.fCurPosX;
1192
1193 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1194 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1195 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1196
1197 pdfContext->fGraphicsState.fCurPosX = x3;
1198 pdfContext->fGraphicsState.fCurPosY = y3;
1199
1200 return kOK_PdfResult;
1201}
1202
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001203static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001204 if (pdfContext->fGraphicsState.fPathClosed) {
1205 pdfContext->fGraphicsState.fPath.reset();
1206 pdfContext->fGraphicsState.fPathClosed = false;
1207 }
1208
edisonn@google.com571c70b2013-07-10 17:09:50 +00001209 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1210 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001211 double y2 = pdfContext->fGraphicsState.fCurPosY;
1212 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001213 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1214 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001215
1216 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1217 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1218 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1219
1220 pdfContext->fGraphicsState.fCurPosX = x3;
1221 pdfContext->fGraphicsState.fCurPosY = y3;
1222
1223 return kOK_PdfResult;
1224}
1225
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001226static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001227 if (pdfContext->fGraphicsState.fPathClosed) {
1228 pdfContext->fGraphicsState.fPath.reset();
1229 pdfContext->fGraphicsState.fPathClosed = false;
1230 }
1231
edisonn@google.com571c70b2013-07-10 17:09:50 +00001232 double height = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1233 double width = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1234 double y = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1235 double x = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001236
1237 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1238 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1239
1240 pdfContext->fGraphicsState.fCurPosX = x;
1241 pdfContext->fGraphicsState.fCurPosY = y + height;
1242
1243 return kOK_PdfResult;
1244}
1245
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001246static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001247 pdfContext->fGraphicsState.fPath.close();
1248 return kOK_PdfResult;
1249}
1250
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001251static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001252 SkPath path = pdfContext->fGraphicsState.fPath;
1253
1254 if (close) {
1255 path.close();
1256 }
1257
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001258 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001259
1260 SkPaint paint;
1261
1262 SkPoint line[2];
1263 if (fill && !stroke && path.isLine(line)) {
1264 paint.setStyle(SkPaint::kStroke_Style);
1265
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001266 // TODO(edisonn): implement this with patterns
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001267 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1268 paint.setStrokeWidth(SkDoubleToScalar(0));
1269
1270 canvas->drawPath(path, paint);
1271 } else {
1272 if (fill) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001273 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0 &&
1274 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1275
1276 // TODO(edisonn): we can use a shader here, like imageshader to draw fast. ultimately,
1277 // if this is not possible, and we are in rasper mode, and the cells don't intersect, we could even have multiple cpus.
1278
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001279 PdfOp_q(pdfContext, canvas, NULL);
1280
1281 if (evenOdd) {
1282 path.setFillType(SkPath::kEvenOdd_FillType);
1283 }
1284 canvas->clipPath(path);
1285
1286 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(pdfContext->fGraphicsState.fNonStroking.fPattern) != kNone_SkPdfObjectType) {
1287 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState.fNonStroking.fPattern;
1288
1289 // TODO(edisonn): constants
1290 // TODO(edisonn): colored
1291 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001292 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
1293 // it will change the result iterating in reverse
1294 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
1295 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001296
1297 SkRect bounds = path.getBounds();
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001298
1299 // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001300 // TODO(edisonn): don't do that!
1301 bounds.sort();
1302
1303 SkScalar x;
1304 SkScalar y;
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001305
1306 y = bounds.top();
1307 int totalx = 0;
1308 int totaly = 0;
1309 while (y < bounds.bottom()) {
1310 x = bounds.left();
1311 totalx = 0;
1312
1313 while (x < bounds.right()) {
1314 doXObject(pdfContext, canvas, pattern);
1315
edisonn@google.com0f901902013-08-07 11:56:16 +00001316 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(xStep), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001317 totalx += xStep;
1318 x += SkIntToScalar(xStep);
1319 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001320 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(-totalx), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001321
edisonn@google.com0f901902013-08-07 11:56:16 +00001322 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(-yStep));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001323 totaly += yStep;
1324 y += SkIntToScalar(yStep);
1325 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001326 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(totaly));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001327 }
1328 }
1329
1330 // apply matrix
1331 // get xstep, y step, bbox ... for cliping, and bos of the path
1332
1333 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001334 } else {
1335 paint.setStyle(SkPaint::kFill_Style);
1336 if (evenOdd) {
1337 path.setFillType(SkPath::kEvenOdd_FillType);
1338 }
1339
1340 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1341
1342 canvas->drawPath(path, paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001343 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001344 }
1345
1346 if (stroke) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001347 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0) {
1348 // TODO(edisonn): implement Pattern for strokes
1349 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001350
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001351 paint.setColor(SK_ColorGREEN);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001352
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001353 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1354 canvas->drawPath(path, paint);
1355 } else {
1356 paint.setStyle(SkPaint::kStroke_Style);
1357
1358 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1359
1360 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1361 canvas->drawPath(path, paint);
1362 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001363 }
1364 }
1365
1366 pdfContext->fGraphicsState.fPath.reset();
1367 // todo zoom ... other stuff ?
1368
1369 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1370#ifndef PDF_DEBUG_NO_CLIPING
1371 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1372#endif
1373 }
1374
1375 //pdfContext->fGraphicsState.fClipPath.reset();
1376 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1377
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001378 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001379
1380}
1381
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001382static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001383 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1384}
1385
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001386static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001387 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1388}
1389
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001390static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001391 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1392}
1393
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001394static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001395 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1396}
1397
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001398static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001399 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1400}
1401
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001402static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001403 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1404}
1405
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001406static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001407 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1408}
1409
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001410static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001411 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1412}
1413
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001414static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001415 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1416}
1417
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001418static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001419 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001420 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1421#ifndef PDF_DEBUG_NO_CLIPING
1422 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1423#endif
1424 }
1425
1426 //pdfContext->fGraphicsState.fClipPath.reset();
1427 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1428
1429 pdfContext->fGraphicsState.fPathClosed = true;
1430
1431 return kOK_PdfResult;
1432}
1433
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001434static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001435 pdfContext->fGraphicsState.fTextBlock = true;
edisonn@google.come57c62d2013-08-07 18:04:15 +00001436 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
1437 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
1438 pdfContext->fGraphicsState.fMatrixTm = matrix;
1439 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001440
1441 return kPartial_PdfResult;
1442}
1443
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001444static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001445 if (!pdfContext->fGraphicsState.fTextBlock) {
1446 return kIgnoreError_PdfResult;
1447 }
1448 // 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 +00001449 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001450}
1451
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001452PdfResult skpdfGraphicsStateApplyFontCore(PdfContext* pdfContext, const SkPdfObject* fontName, double fontSize) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001453#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001454 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001455#endif
1456
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001457 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1458 // TODO(edisonn): try to recover and draw it any way?
1459 return kIgnoreError_PdfResult;
1460 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001461
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001462 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1463 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1464 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1465 // TODO(edisonn): try to recover and draw it any way?
1466 return kIgnoreError_PdfResult;
1467 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001468
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001469 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1470
1471 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1472
1473 if (skfont) {
1474 pdfContext->fGraphicsState.fSkFont = skfont;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001475 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001476 pdfContext->fGraphicsState.fCurFontSize = fontSize;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001477 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001478}
1479
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001480//font size Tf Set the text font, Tf
1481//, to font and the text font size, Tfs, to size. font is the name of a
1482//font resource in the Fontsubdictionary of the current resource dictionary; size is
1483//a number representing a scale factor. There is no initial value for either font or
1484//size; they must be specified explicitly using Tf before any text is shown.
1485static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1486 double fontSize = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1487 SkPdfObject* fontName = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1488 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1489}
1490
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001491static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001492 if (!pdfContext->fGraphicsState.fTextBlock) {
1493 // TODO(edisonn): try to recover and draw it any way?
1494 return kIgnoreError_PdfResult;
1495 }
1496
1497 PdfResult ret = DrawText(pdfContext,
1498 pdfContext->fObjectStack.top(),
1499 canvas);
1500 pdfContext->fObjectStack.pop();
1501
1502 return ret;
1503}
1504
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001505static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001506 if (!pdfContext->fGraphicsState.fTextBlock) {
1507 // TODO(edisonn): try to recover and draw it any way?
1508 return kIgnoreError_PdfResult;
1509 }
1510
1511 PdfOp_T_star(pdfContext, canvas, looper);
1512 // Do not pop, and push, just transfer the param to Tj
1513 return PdfOp_Tj(pdfContext, canvas, looper);
1514}
1515
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001516static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001517 if (!pdfContext->fGraphicsState.fTextBlock) {
1518 // TODO(edisonn): try to recover and draw it any way?
1519 return kIgnoreError_PdfResult;
1520 }
1521
1522 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1523 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1524 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1525
1526 pdfContext->fObjectStack.push(aw);
1527 PdfOp_Tw(pdfContext, canvas, looper);
1528
1529 pdfContext->fObjectStack.push(ac);
1530 PdfOp_Tc(pdfContext, canvas, looper);
1531
1532 pdfContext->fObjectStack.push(str);
1533 PdfOp_quote(pdfContext, canvas, looper);
1534
1535 return kPartial_PdfResult;
1536}
1537
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001538static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001539 if (!pdfContext->fGraphicsState.fTextBlock) {
1540 // TODO(edisonn): try to recover and draw it any way?
1541 return kIgnoreError_PdfResult;
1542 }
1543
edisonn@google.com571c70b2013-07-10 17:09:50 +00001544 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001545 pdfContext->fObjectStack.pop();
1546
edisonn@google.com571c70b2013-07-10 17:09:50 +00001547 if (!array->isArray()) {
1548 return kIgnoreError_PdfResult;
1549 }
1550
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001551 for( int i=0; i<static_cast<int>(array->size()); i++ )
1552 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001553 if( (*array)[i]->isAnyString()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001554 SkPdfObject* obj = (*array)[i];
1555 DrawText(pdfContext,
1556 obj,
1557 canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001558 } else if ((*array)[i]->isNumber()) {
1559 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001560 SkMatrix matrix;
1561 matrix.setAll(SkDoubleToScalar(1),
1562 SkDoubleToScalar(0),
1563 // TODO(edisonn): use writing mode, vertical/horizontal.
1564 SkDoubleToScalar(-dx), // amount is substracted!!!
1565 SkDoubleToScalar(0),
1566 SkDoubleToScalar(1),
1567 SkDoubleToScalar(0),
1568 SkDoubleToScalar(0),
1569 SkDoubleToScalar(0),
1570 SkDoubleToScalar(1));
1571
1572 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1573 }
1574 }
1575 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1576}
1577
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001578static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com4f898b72013-08-07 21:11:57 +00001579 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1580
1581 //Next, get the ColorSpace Dictionary from the Resource Dictionary:
1582 SkPdfDictionary* colorSpaceResource = pdfContext->fGraphicsState.fResources->ColorSpace(pdfContext->fPdfDoc);
1583
1584 SkPdfObject* colorSpace = pdfContext->fPdfDoc->resolveReference(colorSpaceResource->get(name));
1585
1586 if (colorSpace == NULL) {
1587 colorOperator->fColorSpace = name->strRef();
1588 } else {
1589#ifdef PDF_TRACE
1590 printf("CS = %s\n", colorSpace->toString(0, 0).c_str());
1591#endif // PDF_TRACE
1592 if (colorSpace->isName()) {
1593 colorOperator->fColorSpace = colorSpace->strRef();
1594 } else if (colorSpace->isArray()) {
1595 int cnt = colorSpace->size();
1596 if (cnt == 0) {
1597 return kIgnoreError_PdfResult;
1598 }
1599 SkPdfObject* type = colorSpace->objAtAIndex(0);
1600 type = pdfContext->fPdfDoc->resolveReference(type);
1601
1602 if (type->isName("ICCBased")) {
1603 if (cnt != 2) {
1604 return kIgnoreError_PdfResult;
1605 }
1606 SkPdfObject* prop = colorSpace->objAtAIndex(1);
1607 prop = pdfContext->fPdfDoc->resolveReference(prop);
1608#ifdef PDF_TRACE
1609 printf("ICCBased prop = %s\n", prop->toString(0, 0).c_str());
1610#endif // PDF_TRACE
1611 // TODO(edisonn): hack
1612 if (prop && prop->isDictionary() && prop->get("N") && prop->get("N")->isInteger() && prop->get("N")->intValue() == 3) {
1613 colorOperator->setColorSpace(&strings_DeviceRGB);
1614 return kPartial_PdfResult;
1615 }
1616 return kNYI_PdfResult;
1617 }
1618 }
1619 }
1620
1621 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001622}
1623
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001624static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001625 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1626}
1627
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001628static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001629 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1630}
1631
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001632static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001633 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001634// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001635
1636 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1637
1638 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001639 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001640 doubles = false;
1641 }
1642
1643#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001644 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001645#endif
1646
1647 for (int i = n - 1; i >= 0 ; i--) {
1648 if (doubles) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001649 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1650// } else {
1651// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001652 }
1653 }
1654
1655 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001656 // TODO(edisonn): do possible field values to enum at parsing time!
1657 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001658 if (colorOperator->fColorSpace.equals("DeviceRGB") || colorOperator->fColorSpace.equals("RGB")) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001659 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]), (U8CPU)(255*c[1]), (U8CPU)(255*c[2])));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001660 }
1661 return kPartial_PdfResult;
1662}
1663
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001664static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001665 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1666}
1667
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001668static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001669 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1670}
1671
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001672static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001673 if (pdfContext->fObjectStack.top()->isName()) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001674 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1675
1676 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001677 SkPdfDictionary* patternResources = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
edisonn@google.com276fed92013-08-01 21:20:47 +00001678
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001679 if (patternResources == NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001680#ifdef PDF_TRACE
1681 printf("ExtGState is NULL!\n");
1682#endif
1683 return kIgnoreError_PdfResult;
1684 }
1685
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001686 colorOperator->setPatternColorSpace(pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001687 }
1688
1689 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1690 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1691
1692 return kPartial_PdfResult;
1693}
1694
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001695static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001696 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1697}
1698
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001699static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001700 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1701}
1702
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001703static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001704 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001705 return kNYI_PdfResult;
1706}
1707
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001708static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001709 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1710}
1711
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001712static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001713 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1714}
1715
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001716static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001717 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1718 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1719 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001720
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001721 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001722 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001723 return kOK_PdfResult;
1724}
1725
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001726static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001727 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1728}
1729
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001730static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001731 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1732}
1733
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001734static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001735 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001736 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1737 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1738 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1739 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001740
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001741 colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001742 // TODO(edisonn): Set color.
1743 return kNYI_PdfResult;
1744}
1745
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001746static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001747 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1748}
1749
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001750static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001751 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1752}
1753
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001754static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001755 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1756 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1757
1758 return kOK_PdfResult;
1759}
1760
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001761static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001762 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1763
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001764 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1765 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1766
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001767 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001768}
1769
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001770static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001771 *looper = new PdfCompatibilitySectionLooper();
1772 return kOK_PdfResult;
1773}
1774
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001775static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001776#ifdef ASSERT_BAD_PDF_OPS
1777 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1778 // have the assert when testing good pdfs.
1779#endif
1780 return kIgnoreError_PdfResult;
1781}
1782
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001783static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001784 *looper = new PdfInlineImageLooper();
1785 return kOK_PdfResult;
1786}
1787
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001788static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001789#ifdef ASSERT_BAD_PDF_OPS
1790 SkASSERT(false); // must be processed in inline image looper, but let's
1791 // have the assert when testing good pdfs.
1792#endif
1793 return kIgnoreError_PdfResult;
1794}
1795
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001796static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001797#ifdef ASSERT_BAD_PDF_OPS
1798 SkASSERT(false); // must be processed in inline image looper, but let's
1799 // have the assert when testing good pdfs.
1800#endif
1801 return kIgnoreError_PdfResult;
1802}
1803
edisonn@google.com24cdf132013-07-30 16:06:12 +00001804
1805// TODO(edisonn): security review here, make sure all parameters are valid, and safe.
1806PdfResult skpdfGraphicsStateApply_ca(PdfContext* pdfContext, double ca) {
1807 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
1808 return kOK_PdfResult;
1809}
1810
1811PdfResult skpdfGraphicsStateApply_CA(PdfContext* pdfContext, double CA) {
1812 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
1813 return kOK_PdfResult;
1814}
1815
1816PdfResult skpdfGraphicsStateApplyLW(PdfContext* pdfContext, double lineWidth) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001817 pdfContext->fGraphicsState.fLineWidth = lineWidth;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001818 return kOK_PdfResult;
1819}
1820
1821PdfResult skpdfGraphicsStateApplyLC(PdfContext* pdfContext, int64_t lineCap) {
1822 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
1823 return kOK_PdfResult;
1824}
1825
1826PdfResult skpdfGraphicsStateApplyLJ(PdfContext* pdfContext, int64_t lineJoin) {
1827 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
1828 return kOK_PdfResult;
1829}
1830
1831PdfResult skpdfGraphicsStateApplyML(PdfContext* pdfContext, double miterLimit) {
1832 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
1833 return kOK_PdfResult;
1834}
1835
1836// TODO(edisonn): implement all rules, as of now 3) and 5) and 6) do not seem suported by skia, but I am not sure
1837/*
18381) [ ] 0 No dash; solid, unbroken lines
18392) [3] 0 3 units on, 3 units off, …
18403) [2] 1 1 on, 2 off, 2 on, 2 off, …
18414) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
18425) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
18436) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
1844 */
1845
1846PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* intervals, SkPdfObject* phase) {
1847 int cnt = intervals->size();
1848 if (cnt >= 256) {
1849 // TODO(edisonn): report error/warning, unsuported;
1850 // TODO(edisonn): alloc memory
1851 return kIgnoreError_PdfResult;
1852 }
1853 for (int i = 0; i < cnt; i++) {
1854 if (!intervals->objAtAIndex(i)->isNumber()) {
1855 // TODO(edisonn): report error/warning
1856 return kIgnoreError_PdfResult;
1857 }
1858 }
1859
edisonn@google.com24cdf132013-07-30 16:06:12 +00001860 double total = 0;
1861 for (int i = 0 ; i < cnt; i++) {
1862 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
1863 total += pdfContext->fGraphicsState.fDashArray[i];
1864 }
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001865 if (cnt & 1) {
1866 if (cnt == 1) {
1867 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
1868 cnt++;
1869 } else {
1870 // TODO(edisonn): report error/warning
1871 return kNYI_PdfResult;
1872 }
1873 }
1874 pdfContext->fGraphicsState.fDashArrayLength = cnt;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001875 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
1876 if (pdfContext->fGraphicsState.fDashPhase == 0) {
1877 // other rules, changes?
1878 pdfContext->fGraphicsState.fDashPhase = total;
1879 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001880
1881 return kOK_PdfResult;
1882}
1883
edisonn@google.com24cdf132013-07-30 16:06:12 +00001884PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* dash) {
1885 // TODO(edisonn): verify input
1886 if (!dash || dash->isArray() || dash->size() != 2 || !dash->objAtAIndex(0)->isArray() || !dash->objAtAIndex(1)->isNumber()) {
1887 // TODO(edisonn): report error/warning
1888 return kIgnoreError_PdfResult;
1889 }
1890 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0), dash->objAtAIndex(1));
1891}
1892
1893void skpdfGraphicsStateApplyFont(PdfContext* pdfContext, SkPdfArray* fontAndSize) {
1894 if (!fontAndSize || fontAndSize->isArray() || fontAndSize->size() != 2 || !fontAndSize->objAtAIndex(0)->isName() || !fontAndSize->objAtAIndex(1)->isNumber()) {
1895 // TODO(edisonn): report error/warning
1896 return;
1897 }
1898 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), fontAndSize->objAtAIndex(1)->numberValue());
1899}
1900
1901
1902//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1903static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1904 double lw = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1905 return skpdfGraphicsStateApplyLW(pdfContext, lw);
1906}
1907
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001908//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 +00001909static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001910 int64_t lc = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1911 return skpdfGraphicsStateApplyLC(pdfContext, lc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001912}
1913
1914//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 +00001915static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001916 double lj = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1917 return skpdfGraphicsStateApplyLJ(pdfContext, lj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001918}
1919
1920//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001921static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001922 double ml = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1923 return skpdfGraphicsStateApplyML(pdfContext, ml);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001924}
1925
1926//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1927//page 155).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001928static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001929 SkPdfObject* phase = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1930 SkPdfObject* array = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001931
edisonn@google.com24cdf132013-07-30 16:06:12 +00001932 if (!array->isArray()) {
1933 return kIgnoreError_PdfResult;
1934 }
1935
1936 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)array, phase);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001937}
1938
1939//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 +00001940static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001941 pdfContext->fObjectStack.pop();
1942
1943 return kNYI_PdfResult;
1944}
1945
1946//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1947//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1948//fies the output device’s default flatness tolerance.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001949static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001950 pdfContext->fObjectStack.pop();
1951
1952 return kNYI_PdfResult;
1953}
1954
edisonn@google.come878e722013-07-29 19:10:58 +00001955SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
1956
1957class InitBlendModes {
1958public:
1959 InitBlendModes() {
1960 // TODO(edisonn): use the python code generator?
1961 // TABLE 7.2 Standard separable blend modes
1962 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
1963 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
1964 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
1965 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
1966 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
1967 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
1968 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
1969 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
1970 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
1971 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
1972 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
1973 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
1974
1975 // TABLE 7.3 Standard nonseparable blend modes
1976 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
1977 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
1978 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
1979 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
1980 }
1981};
1982
1983InitBlendModes _gDummyInniter;
1984
1985SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
1986 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1987 if (gPdfBlendModes.find(blendMode, len, &mode)) {
1988 return mode;
1989 }
1990
1991 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1992}
1993
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001994void skpdfGraphicsStateApplyBM_name(PdfContext* pdfContext, const std::string& blendMode) {
edisonn@google.come878e722013-07-29 19:10:58 +00001995 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.length());
1996 if (mode <= SkXfermode::kLastMode) {
1997 pdfContext->fGraphicsState.fBlendModesLength = 1;
1998 pdfContext->fGraphicsState.fBlendModes[0] = mode;
1999 } else {
2000 // TODO(edisonn): report unknown blend mode
2001 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002002}
2003
2004void skpdfGraphicsStateApplyBM_array(PdfContext* pdfContext, SkPdfArray* blendModes) {
edisonn@google.come878e722013-07-29 19:10:58 +00002005 if (!blendModes || blendModes->isArray() || blendModes->size() == 0 || blendModes->size() > 256) {
2006 // TODO(edisonn): report error/warning
2007 return;
2008 }
2009 SkXfermode::Mode modes[256];
2010 int cnt = blendModes->size();
2011 for (int i = 0; i < cnt; i++) {
2012 SkPdfObject* name = blendModes->objAtAIndex(i);
2013 if (!name->isName()) {
2014 // TODO(edisonn): report error/warning
2015 return;
2016 }
2017 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
2018 if (mode > SkXfermode::kLastMode) {
2019 // TODO(edisonn): report error/warning
2020 return;
2021 }
2022 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002023
edisonn@google.come878e722013-07-29 19:10:58 +00002024 pdfContext->fGraphicsState.fBlendModesLength = cnt;
2025 for (int i = 0; i < cnt; i++) {
2026 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
2027 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002028}
2029
2030void skpdfGraphicsStateApplySMask_dict(PdfContext* pdfContext, SkPdfDictionary* sMask) {
2031 // TODO(edisonn): verify input
edisonn@google.come878e722013-07-29 19:10:58 +00002032 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002033 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
edisonn@google.come878e722013-07-29 19:10:58 +00002034 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
2035 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
2036 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
2037 } else {
2038 // TODO (edisonn): report error/warning
2039 }
2040}
2041
2042void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string& sMask) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002043 if (sMask == "None") {
2044 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002045 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002046 return;
2047 }
2048
edisonn@google.come878e722013-07-29 19:10:58 +00002049 //Next, get the ExtGState Dictionary from the Resource Dictionary:
2050 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2051
2052 if (extGStateDictionary == NULL) {
2053#ifdef PDF_TRACE
2054 printf("ExtGState is NULL!\n");
2055#endif
2056 // TODO (edisonn): report error/warning
2057 return;
2058 }
2059
2060 SkPdfObject* obj = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
2061 if (!obj || !obj->isDictionary()) {
2062 // TODO (edisonn): report error/warning
2063 return;
2064 }
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002065
2066 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002067 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002068
edisonn@google.come878e722013-07-29 19:10:58 +00002069 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002070}
2071
2072void skpdfGraphicsStateApplyAIS(PdfContext* pdfContext, bool alphaSource) {
2073 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2074}
2075
2076
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002077//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2078//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 +00002079static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002080 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002081
2082#ifdef PDF_TRACE
2083 std::string str;
2084#endif
2085
2086 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00002087 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002088
2089 if (extGStateDictionary == NULL) {
2090#ifdef PDF_TRACE
2091 printf("ExtGState is NULL!\n");
2092#endif
2093 return kIgnoreError_PdfResult;
2094 }
2095
edisonn@google.com571c70b2013-07-10 17:09:50 +00002096 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002097
edisonn@google.com571c70b2013-07-10 17:09:50 +00002098 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2099 return kIgnoreError_PdfResult;
2100 }
2101 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002102
2103 // TODO(edisonn): now load all those properties in graphic state.
2104 if (gs == NULL) {
2105 return kIgnoreError_PdfResult;
2106 }
2107
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002108 if (gs->has_LW()) {
2109 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2110 }
2111
2112 if (gs->has_LC()) {
2113 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2114 }
2115
2116 if (gs->has_LJ()) {
2117 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2118 }
2119
2120 if (gs->has_ML()) {
2121 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2122 }
2123
2124 if (gs->has_D()) {
2125 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2126 }
2127
2128 if (gs->has_Font()) {
2129 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2130 }
2131
2132 if (gs->has_BM()) {
2133 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2134 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2135 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2136 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2137 } else {
2138 // TODO(edisonn): report/warn
2139 }
2140 }
2141
2142 if (gs->has_SMask()) {
2143 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2144 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2145 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
2146 skpdfGraphicsStateApplySMask_dict(pdfContext, gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
2147 } else {
2148 // TODO(edisonn): report/warn
2149 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002150 }
2151
2152 if (gs->has_ca()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002153 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002154 }
2155
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002156 if (gs->has_CA()) {
2157 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2158 }
2159
2160 if (gs->has_AIS()) {
2161 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002162 }
2163
edisonn@google.com9a43c182013-08-01 20:06:42 +00002164 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002165}
2166
2167//charSpace Tc Set the character spacing, Tc
2168//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2169//Initial value: 0.
2170PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002171 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002172 pdfContext->fGraphicsState.fCharSpace = charSpace;
2173
2174 return kOK_PdfResult;
2175}
2176
2177//wordSpace Tw Set the word spacing, T
2178//w
2179//, to wordSpace, which is a number expressed in unscaled
2180//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2181//value: 0.
2182PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002183 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002184 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2185
2186 return kOK_PdfResult;
2187}
2188
2189//scale Tz Set the horizontal scaling, Th
2190//, to (scale ˜ 100). scale is a number specifying the
2191//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002192static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002193 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002194
2195 return kNYI_PdfResult;
2196}
2197
2198//render Tr Set the text rendering mode, T
2199//mode, to render, which is an integer. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002200static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002201 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002202
2203 return kNYI_PdfResult;
2204}
2205//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2206//units. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002207static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002208 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002209
2210 return kNYI_PdfResult;
2211}
2212
2213//wx wy d0
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002214static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002215 pdfContext->fObjectStack.pop();
2216 pdfContext->fObjectStack.pop();
2217
2218 return kNYI_PdfResult;
2219}
2220
2221//wx wy llx lly urx ury d1
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002222static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002223 pdfContext->fObjectStack.pop();
2224 pdfContext->fObjectStack.pop();
2225 pdfContext->fObjectStack.pop();
2226 pdfContext->fObjectStack.pop();
2227 pdfContext->fObjectStack.pop();
2228 pdfContext->fObjectStack.pop();
2229
2230 return kNYI_PdfResult;
2231}
2232
2233//name sh
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002234static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002235 pdfContext->fObjectStack.pop();
2236
2237 return kNYI_PdfResult;
2238}
2239
2240//name Do
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002241static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002242 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002243
edisonn@google.com571c70b2013-07-10 17:09:50 +00002244 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002245
2246 if (xObject == NULL) {
2247#ifdef PDF_TRACE
2248 printf("XObject is NULL!\n");
2249#endif
2250 return kIgnoreError_PdfResult;
2251 }
2252
edisonn@google.com571c70b2013-07-10 17:09:50 +00002253 SkPdfObject* value = xObject->get(name);
2254 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002255
2256#ifdef PDF_TRACE
2257// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002258// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002259#endif
2260
edisonn@google.com571c70b2013-07-10 17:09:50 +00002261 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002262}
2263
2264//tag MP Designate a marked-content point. tag is a name object indicating the role or
2265//significance of the point.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002266static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002267 pdfContext->fObjectStack.pop();
2268
2269 return kNYI_PdfResult;
2270}
2271
2272//tag properties DP Designate a marked-content point with an associated property list. tag is a
2273//name object indicating the role or significance of the point; properties is
2274//either an inline dictionary containing the property list or a name object
2275//associated with it in the Properties subdictionary of the current resource
2276//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002277static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002278 pdfContext->fObjectStack.pop();
2279 pdfContext->fObjectStack.pop();
2280
2281 return kNYI_PdfResult;
2282}
2283
2284//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2285//tag is a name object indicating the role or significance of the sequence.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002286static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002287 pdfContext->fObjectStack.pop();
2288
2289 return kNYI_PdfResult;
2290}
2291
2292//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2293//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2294//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 +00002295static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002296 pdfContext->fObjectStack.pop();
2297 pdfContext->fObjectStack.pop();
2298
2299 return kNYI_PdfResult;
2300}
2301
2302//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002303static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002304 return kNYI_PdfResult;
2305}
2306
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002307static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002308 static bool gInitialized = false;
2309 if (gInitialized) {
2310 return;
2311 }
2312
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002313 gPdfOps.set("q", PdfOp_q);
2314 gPdfOps.set("Q", PdfOp_Q);
2315 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002316
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002317 gPdfOps.set("TD", PdfOp_TD);
2318 gPdfOps.set("Td", PdfOp_Td);
2319 gPdfOps.set("Tm", PdfOp_Tm);
2320 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002321
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002322 gPdfOps.set("m", PdfOp_m);
2323 gPdfOps.set("l", PdfOp_l);
2324 gPdfOps.set("c", PdfOp_c);
2325 gPdfOps.set("v", PdfOp_v);
2326 gPdfOps.set("y", PdfOp_y);
2327 gPdfOps.set("h", PdfOp_h);
2328 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002329
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002330 gPdfOps.set("S", PdfOp_S);
2331 gPdfOps.set("s", PdfOp_s);
2332 gPdfOps.set("f", PdfOp_f);
2333 gPdfOps.set("F", PdfOp_F);
2334 gPdfOps.set("f*", PdfOp_f_star);
2335 gPdfOps.set("B", PdfOp_B);
2336 gPdfOps.set("B*", PdfOp_B_star);
2337 gPdfOps.set("b", PdfOp_b);
2338 gPdfOps.set("b*", PdfOp_b_star);
2339 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002340
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002341 gPdfOps.set("BT", PdfOp_BT);
2342 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002343
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002344 gPdfOps.set("Tj", PdfOp_Tj);
2345 gPdfOps.set("'", PdfOp_quote);
2346 gPdfOps.set("\"", PdfOp_doublequote);
2347 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002348
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002349 gPdfOps.set("CS", PdfOp_CS);
2350 gPdfOps.set("cs", PdfOp_cs);
2351 gPdfOps.set("SC", PdfOp_SC);
2352 gPdfOps.set("SCN", PdfOp_SCN);
2353 gPdfOps.set("sc", PdfOp_sc);
2354 gPdfOps.set("scn", PdfOp_scn);
2355 gPdfOps.set("G", PdfOp_G);
2356 gPdfOps.set("g", PdfOp_g);
2357 gPdfOps.set("RG", PdfOp_RG);
2358 gPdfOps.set("rg", PdfOp_rg);
2359 gPdfOps.set("K", PdfOp_K);
2360 gPdfOps.set("k", PdfOp_k);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002361
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002362 gPdfOps.set("W", PdfOp_W);
2363 gPdfOps.set("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002364
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002365 gPdfOps.set("BX", PdfOp_BX);
2366 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002367
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002368 gPdfOps.set("BI", PdfOp_BI);
2369 gPdfOps.set("ID", PdfOp_ID);
2370 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002371
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002372 gPdfOps.set("w", PdfOp_w);
2373 gPdfOps.set("J", PdfOp_J);
2374 gPdfOps.set("j", PdfOp_j);
2375 gPdfOps.set("M", PdfOp_M);
2376 gPdfOps.set("d", PdfOp_d);
2377 gPdfOps.set("ri", PdfOp_ri);
2378 gPdfOps.set("i", PdfOp_i);
2379 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002380
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002381 gPdfOps.set("Tc", PdfOp_Tc);
2382 gPdfOps.set("Tw", PdfOp_Tw);
2383 gPdfOps.set("Tz", PdfOp_Tz);
2384 gPdfOps.set("TL", PdfOp_TL);
2385 gPdfOps.set("Tf", PdfOp_Tf);
2386 gPdfOps.set("Tr", PdfOp_Tr);
2387 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002388
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002389 gPdfOps.set("d0", PdfOp_d0);
2390 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002391
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002392 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002393
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002394 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002395
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002396 gPdfOps.set("MP", PdfOp_MP);
2397 gPdfOps.set("DP", PdfOp_DP);
2398 gPdfOps.set("BMC", PdfOp_BMC);
2399 gPdfOps.set("BDC", PdfOp_BDC);
2400 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002401
2402 gInitialized = true;
2403}
2404
2405class InitPdfOps {
2406public:
2407 InitPdfOps() {
2408 initPdfOperatorRenderes();
2409 }
2410};
2411
2412InitPdfOps gInitPdfOps;
2413
2414void reportPdfRenderStats() {
2415 std::map<std::string, int>::iterator iter;
2416
2417 for (int i = 0 ; i < kCount_PdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002418 SkTDict<int>::Iter iter(gRenderStats[i]);
2419 const char* key;
2420 int value = 0;
2421 while ((key = iter.next(&value)) != NULL) {
2422 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002423 }
2424 }
2425}
2426
2427PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002428 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002429 {
2430 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002431 PdfOperatorRenderer pdfOperatorRenderer = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002432 if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) && pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002433 // caller, main work is done by pdfOperatorRenderer(...)
2434 PdfTokenLooper* childLooper = NULL;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002435 PdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
2436
2437 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002438 gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
2439 gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002440
2441 if (childLooper) {
2442 childLooper->setUp(this);
2443 childLooper->loop();
2444 delete childLooper;
2445 }
2446 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002447 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002448 gRenderStats[kUnsupported_PdfResult].find(token.fKeyword, token.fKeywordLength, &cnt);
2449 gRenderStats[kUnsupported_PdfResult].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002450 }
2451 }
2452 else if (token.fType == kObject_TokenType)
2453 {
2454 fPdfContext->fObjectStack.push( token.fObject );
2455 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002456 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002457 // TODO(edisonn): deine or use assert not reached
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002458 return kIgnoreError_PdfResult;
2459 }
2460 return kOK_PdfResult;
2461}
2462
2463void PdfMainLooper::loop() {
2464 PdfToken token;
2465 while (readToken(fTokenizer, &token)) {
2466 consumeToken(token);
2467 }
2468}
2469
2470PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002471 SkASSERT(false);
2472 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002473}
2474
2475void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002476 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002477}
2478
2479PdfResult PdfInlineImageLooper::done() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002480 return kNYI_PdfResult;
2481}
2482
2483PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2484 return fParent->consumeToken(token);
2485}
2486
2487void PdfCompatibilitySectionLooper::loop() {
2488 // TODO(edisonn): save stacks position, or create a new stack?
2489 // TODO(edisonn): what happens if we pop out more variables then when we started?
2490 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2491 // pop-ing too much will not affect outside the section.
2492 PdfToken token;
2493 while (readToken(fTokenizer, &token)) {
2494 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2495 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2496 looper->setUp(this);
2497 looper->loop();
2498 delete looper;
2499 } else {
2500 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
2501 fParent->consumeToken(token);
2502 }
2503 }
2504 // TODO(edisonn): restore stack.
2505}
2506
2507// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2508// TODO(edisonn): Add API for Forms viewing and editing
2509// e.g. SkBitmap getPage(int page);
2510// int formsCount();
2511// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2512// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2513// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2514// resources needed, so the preview is fast.
2515// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2516// references automatically.
2517
edisonn@google.com222382b2013-07-10 22:33:10 +00002518PdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00002519
edisonn@google.com444e25a2013-07-11 15:20:50 +00002520bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00002521 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002522 return false;
2523 }
2524
edisonn@google.com222382b2013-07-10 22:33:10 +00002525 if (page < 0 || page >= pages()) {
2526 return false;
2527 }
2528
edisonn@google.com222382b2013-07-10 22:33:10 +00002529 PdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002530
edisonn@google.com222382b2013-07-10 22:33:10 +00002531 pdfContext.fOriginalMatrix = SkMatrix::I();
2532 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2533
2534 gPdfContext = &pdfContext;
2535
2536 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00002537 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00002538 SkScalar w = dst.width();
2539 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00002540
edisonn@google.com444e25a2013-07-11 15:20:50 +00002541 SkScalar wp = fPdfDoc->MediaBox(page).width();
2542 SkScalar hp = fPdfDoc->MediaBox(page).height();
2543
2544 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 +00002545// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2546
2547 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2548 // TODO(edisonn): add flagg for no clipping.
2549 // Use larger image to make sure we do not draw anything outside of page
2550 // could be used in tests.
2551
2552#ifdef PDF_DEBUG_3X
2553 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)};
2554#else
2555 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2556#endif
2557 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2558 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2559
2560 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2561 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2562
2563 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2564 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2565
2566 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2567 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2568
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002569 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
edisonn@google.com0f901902013-08-07 11:56:16 +00002570 pdfContext.fGraphicsState.fContentStreamMatrix = pdfContext.fOriginalMatrix;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002571 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
2572 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
edisonn@google.com222382b2013-07-10 22:33:10 +00002573
edisonn@google.com222382b2013-07-10 22:33:10 +00002574#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00002575 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00002576#endif
2577
edisonn@google.com444e25a2013-07-11 15:20:50 +00002578 canvas->setMatrix(pdfContext.fOriginalMatrix);
2579
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002580 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2581
2582 // TODO(edisonn:) erase with white before draw?
edisonn@google.com222382b2013-07-10 22:33:10 +00002583// SkPaint paint;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002584// paint.setColor(SK_ColorWHITE);
edisonn@google.com222382b2013-07-10 22:33:10 +00002585// canvas->drawRect(rect, paint);
2586
edisonn@google.com222382b2013-07-10 22:33:10 +00002587
2588 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002589 return true;
2590}
edisonn@google.com222382b2013-07-10 22:33:10 +00002591
2592bool SkPdfRenderer::load(const SkString inputFileName) {
2593 unload();
2594
2595 // TODO(edisonn): create static function that could return NULL if there are errors
2596 fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00002597 if (fPdfDoc->pages() == 0) {
2598 delete fPdfDoc;
2599 fPdfDoc = NULL;
2600 }
edisonn@google.com222382b2013-07-10 22:33:10 +00002601
2602 return fPdfDoc != NULL;
2603}
2604
edisonn@google.com147adb12013-07-24 15:56:19 +00002605bool SkPdfRenderer::load(SkStream* stream) {
2606 unload();
2607
2608 // TODO(edisonn): create static function that could return NULL if there are errors
2609 fPdfDoc = new SkNativeParsedPDF(stream);
2610 if (fPdfDoc->pages() == 0) {
2611 delete fPdfDoc;
2612 fPdfDoc = NULL;
2613 }
2614
2615 return fPdfDoc != NULL;
2616}
2617
2618
edisonn@google.com222382b2013-07-10 22:33:10 +00002619int SkPdfRenderer::pages() const {
2620 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2621}
2622
2623void SkPdfRenderer::unload() {
2624 delete fPdfDoc;
2625 fPdfDoc = NULL;
2626}
2627
2628SkRect SkPdfRenderer::MediaBox(int page) const {
2629 SkASSERT(fPdfDoc);
2630 return fPdfDoc->MediaBox(page);
2631}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002632
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002633size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002634 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2635}
edisonn@google.com147adb12013-07-24 15:56:19 +00002636
2637bool SkPDFNativeRenderToBitmap(SkStream* stream,
2638 SkBitmap* output,
2639 int page,
2640 SkPdfContent content,
2641 double dpi) {
2642 SkASSERT(page >= 0);
2643 SkPdfRenderer renderer;
2644 renderer.load(stream);
2645 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
2646 return false;
2647 }
2648
2649 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
2650
2651 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2652 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2653
2654 rect = SkRect::MakeWH(width, height);
2655
2656 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
2657
2658 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*output)));
2659 SkCanvas canvas(device);
2660
2661 return renderer.renderPage(page, &canvas, rect);
2662}