blob: 68501749ef02eda5997a29ca077e00cc1e1b8710 [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 canvas->save();
729
730
edisonn@google.com571c70b2013-07-10 17:09:50 +0000731 if (skobj->Resources(pdfContext->fPdfDoc)) {
732 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000733 }
734
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000735 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000736
edisonn@google.com571c70b2013-07-10 17:09:50 +0000737 if (skobj->has_Matrix()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000738 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000739 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
740 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
741 pdfContext->fGraphicsState.fMatrixTm = matrix;
742 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000743 // TODO(edisonn) reset matrixTm and matricTlm also?
744 }
745
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000746 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com0f901902013-08-07 11:56:16 +0000747 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000748
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000749 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000750
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000751 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
752 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000753
754 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
755 // For this PdfContentsTokenizer needs to be extended.
756
edisonn@google.come878e722013-07-29 19:10:58 +0000757 // This is a group?
758 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000759 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
760 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
edisonn@google.come878e722013-07-29 19:10:58 +0000761 }
762
edisonn@google.com571c70b2013-07-10 17:09:50 +0000763 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000764
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000765 SkPdfNativeTokenizer* tokenizer =
766 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000767 if (tokenizer != NULL) {
768 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
769 looper.loop();
770 delete tokenizer;
771 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000772
773 // TODO(edisonn): should we restore the variable stack at the same state?
774 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000775
776 if (skobj->has_Group()) {
777 canvas->restore();
778 }
779
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000780 canvas->restore();
781 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000782 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000783}
784
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000785
786// TODO(edisonn): Extract a class like ObjWithStream
787static PdfResult doXObject_Pattern(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1PatternDictionary* skobj) {
788 if (!skobj || !skobj->hasStream()) {
789 return kIgnoreError_PdfResult;
790 }
791
792 if (!skobj->has_BBox()) {
793 return kIgnoreError_PdfResult;
794 }
795
796 PdfOp_q(pdfContext, canvas, NULL);
797
798 canvas->save();
799
800
801 if (skobj->Resources(pdfContext->fPdfDoc)) {
802 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
803 }
804
edisonn@google.com0f901902013-08-07 11:56:16 +0000805 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000806
807 if (skobj->has_Matrix()) {
edisonn@google.com0f901902013-08-07 11:56:16 +0000808 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000809 }
810
edisonn@google.com0f901902013-08-07 11:56:16 +0000811 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix");
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000812
edisonn@google.com0f901902013-08-07 11:56:16 +0000813 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix);
814 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix;
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000815
816 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
817 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
818
819 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
820 // For this PdfContentsTokenizer needs to be extended.
821
822 SkPdfStream* stream = (SkPdfStream*)skobj;
823
824 SkPdfNativeTokenizer* tokenizer =
825 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
826 if (tokenizer != NULL) {
827 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
828 looper.loop();
829 delete tokenizer;
830 }
831
832 // TODO(edisonn): should we restore the variable stack at the same state?
833 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
834
835 canvas->restore();
836 PdfOp_Q(pdfContext, canvas, NULL);
837 return kPartial_PdfResult;
838}
839
840
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000841//static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
842// return kNYI_PdfResult;
843//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000844
edisonn@google.com571c70b2013-07-10 17:09:50 +0000845PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
846 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000847 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000848 }
849
850 PdfOp_q(pdfContext, canvas, NULL);
851 canvas->save();
852
853 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
854 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
edisonn@google.come57c62d2013-08-07 18:04:15 +0000855 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000856
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000857 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
edisonn@google.come57c62d2013-08-07 18:04:15 +0000858 pdfContext->fGraphicsState.fCTM.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000859
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000860 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000861
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000862 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000863
864 SkRect rm = bBox;
edisonn@google.coma0cefa12013-07-28 18:34:14 +0000865 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000866
867 SkTraceRect(rm, "bbox mapped");
868
869 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
870
871 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
872 // For this PdfContentsTokenizer needs to be extended.
873
edisonn@google.com571c70b2013-07-10 17:09:50 +0000874 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000875
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000876 SkPdfNativeTokenizer* tokenizer =
877 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000878 if (tokenizer != NULL) {
879 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
880 looper.loop();
881 delete tokenizer;
882 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000883
884 // TODO(edisonn): should we restore the variable stack at the same state?
885 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
886 canvas->restore();
887 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000888
889 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000890}
891
892
edisonn@google.com571c70b2013-07-10 17:09:50 +0000893// TODO(edisonn): make sure the pointer is unique
894std::set<const SkPdfObject*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000895
896class CheckRecursiveRendering {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000897 const SkPdfObject* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000898public:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000899 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000900 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000901 }
902
903 ~CheckRecursiveRendering() {
904 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000905 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000906 }
907
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000908 static bool IsInRendering(const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000909 return gInRendering.find(obj) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000910 }
911};
912
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000913static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000914 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000915 // Oops, corrupt PDF!
916 return kIgnoreError_PdfResult;
917 }
918
edisonn@google.com571c70b2013-07-10 17:09:50 +0000919 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000920
edisonn@google.com571c70b2013-07-10 17:09:50 +0000921 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000922 {
923 case kImageDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000924 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000925 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000926 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000927 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
928 //return doXObject_PS(skxobj.asPS());
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000929 default: {
930 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != kNone_SkPdfObjectType) {
931 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
932 return doXObject_Pattern(pdfContext, canvas, pattern);
933 }
934 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000935 }
edisonn@google.come2e01ff2013-08-02 20:24:48 +0000936 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000937}
938
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000939static PdfResult doPage(PdfContext* pdfContext, SkCanvas* canvas, SkPdfPageObjectDictionary* skobj) {
940 if (!skobj) {
941 return kIgnoreError_PdfResult;
942 }
943
944 if (!skobj->isContentsAStream(pdfContext->fPdfDoc)) {
945 return kNYI_PdfResult;
946 }
947
948 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
949
950 if (!stream) {
951 return kIgnoreError_PdfResult;
952 }
953
954 if (CheckRecursiveRendering::IsInRendering(skobj)) {
955 // Oops, corrupt PDF!
956 return kIgnoreError_PdfResult;
957 }
958 CheckRecursiveRendering checkRecursion(skobj);
959
960
961 PdfOp_q(pdfContext, canvas, NULL);
962
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000963
964 if (skobj->Resources(pdfContext->fPdfDoc)) {
965 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
966 }
967
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000968 // TODO(edisonn): MediaBox can be inherited!!!!
969 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000970 if (skobj->has_Group()) {
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000971 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
972 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
973 } else {
974 canvas->save();
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000975 }
976
edisonn@google.comf111a4b2013-07-31 18:22:36 +0000977
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000978 SkPdfNativeTokenizer* tokenizer =
979 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
980 if (tokenizer != NULL) {
981 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
982 looper.loop();
983 delete tokenizer;
984 }
985
986 // TODO(edisonn): should we restore the variable stack at the same state?
987 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
988 canvas->restore();
989 PdfOp_Q(pdfContext, canvas, NULL);
990 return kPartial_PdfResult;
991}
992
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000993PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
994 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
995 canvas->save();
996 return kOK_PdfResult;
997}
998
999PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1000 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1001 pdfContext->fStateStack.pop();
1002 canvas->restore();
1003 return kOK_PdfResult;
1004}
1005
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001006static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001007 double array[6];
1008 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001009 array[5 - i] = pdfContext->fObjectStack.top()->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001010 pdfContext->fObjectStack.pop();
1011 }
1012
1013 // a b
1014 // c d
1015 // e f
1016
1017 // 0 1
1018 // 2 3
1019 // 4 5
1020
1021 // sx ky
1022 // kx sy
1023 // tx ty
1024 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1025
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001026 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001027
1028#ifdef PDF_TRACE
1029 printf("cm ");
1030 for (int i = 0 ; i < 6 ; i++) {
1031 printf("%f ", array[i]);
1032 }
1033 printf("\n");
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001034 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001035#endif
1036
1037 return kOK_PdfResult;
1038}
1039
1040//leading TL Set the text leading, Tl
1041//, to leading, which is a number expressed in unscaled text
1042//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001043static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001044 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001045
1046 pdfContext->fGraphicsState.fTextLeading = ty;
1047
1048 return kOK_PdfResult;
1049}
1050
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001051static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001052#ifdef PDF_TRACE
1053 printf("stack size = %i\n", (int)pdfContext->fObjectStack.size());
1054#endif
edisonn@google.com571c70b2013-07-10 17:09:50 +00001055 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00001056 SkPdfObject* obj = pdfContext->fObjectStack.top();
1057 obj = obj;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001058 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001059
edisonn@google.come57c62d2013-08-07 18:04:15 +00001060 double array[6] = {1, 0, 0, 1, tx, -ty};
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001061 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1062
1063 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1064 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1065
1066 return kPartial_PdfResult;
1067}
1068
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001069static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001070 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1071 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001072
edisonn@google.com571c70b2013-07-10 17:09:50 +00001073 // TODO(edisonn): Create factory methods or constructors so native is hidden
1074 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001075 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001076
1077 PdfOp_TL(pdfContext, canvas, looper);
1078
edisonn@google.com571c70b2013-07-10 17:09:50 +00001079 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001080 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001081
edisonn@google.com571c70b2013-07-10 17:09:50 +00001082 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001083 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001084
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001085 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1086
1087 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
1088 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001089
1090 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001091}
1092
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001093static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001094 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1095 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1096 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1097 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1098 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1099 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001100
1101 double array[6];
1102 array[0] = a;
1103 array[1] = b;
1104 array[2] = c;
1105 array[3] = d;
1106 array[4] = e;
1107 array[5] = f;
1108
1109 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001110 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
edisonn@google.come57c62d2013-08-07 18:04:15 +00001111 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001112
1113 // TODO(edisonn): Text positioning.
1114 pdfContext->fGraphicsState.fMatrixTm = matrix;
1115 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1116
1117 return kPartial_PdfResult;
1118}
1119
1120//— T* Move to the start of the next line. This operator has the same effect as the code
1121//0 Tl Td
1122//where Tl is the current leading parameter in the text state
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001123static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001124 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
1125 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001126
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001127 pdfContext->fObjectStack.push(zero);
1128 pdfContext->fObjectStack.push(tl);
1129
1130 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
1131
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001132 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001133}
1134
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001135static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001136 if (pdfContext->fGraphicsState.fPathClosed) {
1137 pdfContext->fGraphicsState.fPath.reset();
1138 pdfContext->fGraphicsState.fPathClosed = false;
1139 }
1140
edisonn@google.com571c70b2013-07-10 17:09:50 +00001141 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1142 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001143
1144 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1145 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1146
1147 return kOK_PdfResult;
1148}
1149
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001150static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001151 if (pdfContext->fGraphicsState.fPathClosed) {
1152 pdfContext->fGraphicsState.fPath.reset();
1153 pdfContext->fGraphicsState.fPathClosed = false;
1154 }
1155
edisonn@google.com571c70b2013-07-10 17:09:50 +00001156 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1157 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001158
1159 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1160 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1161
1162 return kOK_PdfResult;
1163}
1164
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001165static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001166 if (pdfContext->fGraphicsState.fPathClosed) {
1167 pdfContext->fGraphicsState.fPath.reset();
1168 pdfContext->fGraphicsState.fPathClosed = false;
1169 }
1170
edisonn@google.com571c70b2013-07-10 17:09:50 +00001171 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1172 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1173 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1174 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1175 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1176 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001177
1178 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1179 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1180 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1181
1182 pdfContext->fGraphicsState.fCurPosX = x3;
1183 pdfContext->fGraphicsState.fCurPosY = y3;
1184
1185 return kOK_PdfResult;
1186}
1187
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001188static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001189 if (pdfContext->fGraphicsState.fPathClosed) {
1190 pdfContext->fGraphicsState.fPath.reset();
1191 pdfContext->fGraphicsState.fPathClosed = false;
1192 }
1193
edisonn@google.com571c70b2013-07-10 17:09:50 +00001194 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1195 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1196 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1197 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001198 double y1 = pdfContext->fGraphicsState.fCurPosY;
1199 double x1 = pdfContext->fGraphicsState.fCurPosX;
1200
1201 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1202 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1203 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1204
1205 pdfContext->fGraphicsState.fCurPosX = x3;
1206 pdfContext->fGraphicsState.fCurPosY = y3;
1207
1208 return kOK_PdfResult;
1209}
1210
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001211static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001212 if (pdfContext->fGraphicsState.fPathClosed) {
1213 pdfContext->fGraphicsState.fPath.reset();
1214 pdfContext->fGraphicsState.fPathClosed = false;
1215 }
1216
edisonn@google.com571c70b2013-07-10 17:09:50 +00001217 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1218 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001219 double y2 = pdfContext->fGraphicsState.fCurPosY;
1220 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001221 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1222 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001223
1224 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1225 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1226 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1227
1228 pdfContext->fGraphicsState.fCurPosX = x3;
1229 pdfContext->fGraphicsState.fCurPosY = y3;
1230
1231 return kOK_PdfResult;
1232}
1233
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001234static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001235 if (pdfContext->fGraphicsState.fPathClosed) {
1236 pdfContext->fGraphicsState.fPath.reset();
1237 pdfContext->fGraphicsState.fPathClosed = false;
1238 }
1239
edisonn@google.com571c70b2013-07-10 17:09:50 +00001240 double height = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1241 double width = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1242 double y = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1243 double x = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001244
1245 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1246 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1247
1248 pdfContext->fGraphicsState.fCurPosX = x;
1249 pdfContext->fGraphicsState.fCurPosY = y + height;
1250
1251 return kOK_PdfResult;
1252}
1253
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001254static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001255 pdfContext->fGraphicsState.fPath.close();
1256 return kOK_PdfResult;
1257}
1258
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001259static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001260 SkPath path = pdfContext->fGraphicsState.fPath;
1261
1262 if (close) {
1263 path.close();
1264 }
1265
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001266 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001267
1268 SkPaint paint;
1269
1270 SkPoint line[2];
1271 if (fill && !stroke && path.isLine(line)) {
1272 paint.setStyle(SkPaint::kStroke_Style);
1273
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001274 // TODO(edisonn): implement this with patterns
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001275 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1276 paint.setStrokeWidth(SkDoubleToScalar(0));
1277
1278 canvas->drawPath(path, paint);
1279 } else {
1280 if (fill) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001281 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0 &&
1282 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1283
1284 // TODO(edisonn): we can use a shader here, like imageshader to draw fast. ultimately,
1285 // if this is not possible, and we are in rasper mode, and the cells don't intersect, we could even have multiple cpus.
1286
1287 canvas->save();
1288 PdfOp_q(pdfContext, canvas, NULL);
1289
1290 if (evenOdd) {
1291 path.setFillType(SkPath::kEvenOdd_FillType);
1292 }
1293 canvas->clipPath(path);
1294
1295 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(pdfContext->fGraphicsState.fNonStroking.fPattern) != kNone_SkPdfObjectType) {
1296 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState.fNonStroking.fPattern;
1297
1298 // TODO(edisonn): constants
1299 // TODO(edisonn): colored
1300 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001301 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
1302 // it will change the result iterating in reverse
1303 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
1304 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001305
1306 SkRect bounds = path.getBounds();
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001307
1308 // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse
edisonn@google.comb0145ce2013-08-05 16:23:23 +00001309 // TODO(edisonn): don't do that!
1310 bounds.sort();
1311
1312 SkScalar x;
1313 SkScalar y;
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001314
1315 y = bounds.top();
1316 int totalx = 0;
1317 int totaly = 0;
1318 while (y < bounds.bottom()) {
1319 x = bounds.left();
1320 totalx = 0;
1321
1322 while (x < bounds.right()) {
1323 doXObject(pdfContext, canvas, pattern);
1324
edisonn@google.com0f901902013-08-07 11:56:16 +00001325 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(xStep), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001326 totalx += xStep;
1327 x += SkIntToScalar(xStep);
1328 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001329 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(-totalx), SkIntToScalar(0));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001330
edisonn@google.com0f901902013-08-07 11:56:16 +00001331 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(-yStep));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001332 totaly += yStep;
1333 y += SkIntToScalar(yStep);
1334 }
edisonn@google.com0f901902013-08-07 11:56:16 +00001335 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(SkIntToScalar(0), SkIntToScalar(totaly));
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001336 }
1337 }
1338
1339 // apply matrix
1340 // get xstep, y step, bbox ... for cliping, and bos of the path
1341
1342 PdfOp_Q(pdfContext, canvas, NULL);
1343 canvas->restore();
1344 } else {
1345 paint.setStyle(SkPaint::kFill_Style);
1346 if (evenOdd) {
1347 path.setFillType(SkPath::kEvenOdd_FillType);
1348 }
1349
1350 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1351
1352 canvas->drawPath(path, paint);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001353 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001354 }
1355
1356 if (stroke) {
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001357 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0) {
1358 // TODO(edisonn): implement Pattern for strokes
1359 paint.setStyle(SkPaint::kStroke_Style);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001360
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001361 paint.setColor(SK_ColorGREEN);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001362
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001363 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1364 canvas->drawPath(path, paint);
1365 } else {
1366 paint.setStyle(SkPaint::kStroke_Style);
1367
1368 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1369
1370 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1371 canvas->drawPath(path, paint);
1372 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001373 }
1374 }
1375
1376 pdfContext->fGraphicsState.fPath.reset();
1377 // todo zoom ... other stuff ?
1378
1379 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1380#ifndef PDF_DEBUG_NO_CLIPING
1381 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1382#endif
1383 }
1384
1385 //pdfContext->fGraphicsState.fClipPath.reset();
1386 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1387
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001388 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001389
1390}
1391
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001392static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001393 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1394}
1395
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001396static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001397 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1398}
1399
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001400static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001401 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1402}
1403
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001404static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001405 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1406}
1407
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001408static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001409 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1410}
1411
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001412static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001413 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1414}
1415
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001416static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001417 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1418}
1419
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001420static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001421 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1422}
1423
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001424static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001425 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1426}
1427
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001428static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001429 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001430 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1431#ifndef PDF_DEBUG_NO_CLIPING
1432 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1433#endif
1434 }
1435
1436 //pdfContext->fGraphicsState.fClipPath.reset();
1437 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1438
1439 pdfContext->fGraphicsState.fPathClosed = true;
1440
1441 return kOK_PdfResult;
1442}
1443
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001444static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001445 pdfContext->fGraphicsState.fTextBlock = true;
edisonn@google.come57c62d2013-08-07 18:04:15 +00001446 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
1447 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
1448 pdfContext->fGraphicsState.fMatrixTm = matrix;
1449 pdfContext->fGraphicsState.fMatrixTlm = matrix;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001450
1451 return kPartial_PdfResult;
1452}
1453
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001454static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001455 if (!pdfContext->fGraphicsState.fTextBlock) {
1456 return kIgnoreError_PdfResult;
1457 }
1458 // 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 +00001459 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001460}
1461
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001462PdfResult skpdfGraphicsStateApplyFontCore(PdfContext* pdfContext, const SkPdfObject* fontName, double fontSize) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001463#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001464 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001465#endif
1466
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001467 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1468 // TODO(edisonn): try to recover and draw it any way?
1469 return kIgnoreError_PdfResult;
1470 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001471
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001472 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1473 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1474 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1475 // TODO(edisonn): try to recover and draw it any way?
1476 return kIgnoreError_PdfResult;
1477 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001478
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001479 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1480
1481 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1482
1483 if (skfont) {
1484 pdfContext->fGraphicsState.fSkFont = skfont;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001485 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001486 pdfContext->fGraphicsState.fCurFontSize = fontSize;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001487 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001488}
1489
edisonn@google.coma0cefa12013-07-28 18:34:14 +00001490//font size Tf Set the text font, Tf
1491//, to font and the text font size, Tfs, to size. font is the name of a
1492//font resource in the Fontsubdictionary of the current resource dictionary; size is
1493//a number representing a scale factor. There is no initial value for either font or
1494//size; they must be specified explicitly using Tf before any text is shown.
1495static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1496 double fontSize = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1497 SkPdfObject* fontName = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1498 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1499}
1500
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001501static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001502 if (!pdfContext->fGraphicsState.fTextBlock) {
1503 // TODO(edisonn): try to recover and draw it any way?
1504 return kIgnoreError_PdfResult;
1505 }
1506
1507 PdfResult ret = DrawText(pdfContext,
1508 pdfContext->fObjectStack.top(),
1509 canvas);
1510 pdfContext->fObjectStack.pop();
1511
1512 return ret;
1513}
1514
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001515static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001516 if (!pdfContext->fGraphicsState.fTextBlock) {
1517 // TODO(edisonn): try to recover and draw it any way?
1518 return kIgnoreError_PdfResult;
1519 }
1520
1521 PdfOp_T_star(pdfContext, canvas, looper);
1522 // Do not pop, and push, just transfer the param to Tj
1523 return PdfOp_Tj(pdfContext, canvas, looper);
1524}
1525
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001526static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001527 if (!pdfContext->fGraphicsState.fTextBlock) {
1528 // TODO(edisonn): try to recover and draw it any way?
1529 return kIgnoreError_PdfResult;
1530 }
1531
1532 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1533 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1534 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1535
1536 pdfContext->fObjectStack.push(aw);
1537 PdfOp_Tw(pdfContext, canvas, looper);
1538
1539 pdfContext->fObjectStack.push(ac);
1540 PdfOp_Tc(pdfContext, canvas, looper);
1541
1542 pdfContext->fObjectStack.push(str);
1543 PdfOp_quote(pdfContext, canvas, looper);
1544
1545 return kPartial_PdfResult;
1546}
1547
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001548static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001549 if (!pdfContext->fGraphicsState.fTextBlock) {
1550 // TODO(edisonn): try to recover and draw it any way?
1551 return kIgnoreError_PdfResult;
1552 }
1553
edisonn@google.com571c70b2013-07-10 17:09:50 +00001554 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001555 pdfContext->fObjectStack.pop();
1556
edisonn@google.com571c70b2013-07-10 17:09:50 +00001557 if (!array->isArray()) {
1558 return kIgnoreError_PdfResult;
1559 }
1560
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001561 for( int i=0; i<static_cast<int>(array->size()); i++ )
1562 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001563 if( (*array)[i]->isAnyString()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001564 SkPdfObject* obj = (*array)[i];
1565 DrawText(pdfContext,
1566 obj,
1567 canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001568 } else if ((*array)[i]->isNumber()) {
1569 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001570 SkMatrix matrix;
1571 matrix.setAll(SkDoubleToScalar(1),
1572 SkDoubleToScalar(0),
1573 // TODO(edisonn): use writing mode, vertical/horizontal.
1574 SkDoubleToScalar(-dx), // amount is substracted!!!
1575 SkDoubleToScalar(0),
1576 SkDoubleToScalar(1),
1577 SkDoubleToScalar(0),
1578 SkDoubleToScalar(0),
1579 SkDoubleToScalar(0),
1580 SkDoubleToScalar(1));
1581
1582 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1583 }
1584 }
1585 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1586}
1587
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001588static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com4f898b72013-08-07 21:11:57 +00001589 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1590
1591 //Next, get the ColorSpace Dictionary from the Resource Dictionary:
1592 SkPdfDictionary* colorSpaceResource = pdfContext->fGraphicsState.fResources->ColorSpace(pdfContext->fPdfDoc);
1593
1594 SkPdfObject* colorSpace = pdfContext->fPdfDoc->resolveReference(colorSpaceResource->get(name));
1595
1596 if (colorSpace == NULL) {
1597 colorOperator->fColorSpace = name->strRef();
1598 } else {
1599#ifdef PDF_TRACE
1600 printf("CS = %s\n", colorSpace->toString(0, 0).c_str());
1601#endif // PDF_TRACE
1602 if (colorSpace->isName()) {
1603 colorOperator->fColorSpace = colorSpace->strRef();
1604 } else if (colorSpace->isArray()) {
1605 int cnt = colorSpace->size();
1606 if (cnt == 0) {
1607 return kIgnoreError_PdfResult;
1608 }
1609 SkPdfObject* type = colorSpace->objAtAIndex(0);
1610 type = pdfContext->fPdfDoc->resolveReference(type);
1611
1612 if (type->isName("ICCBased")) {
1613 if (cnt != 2) {
1614 return kIgnoreError_PdfResult;
1615 }
1616 SkPdfObject* prop = colorSpace->objAtAIndex(1);
1617 prop = pdfContext->fPdfDoc->resolveReference(prop);
1618#ifdef PDF_TRACE
1619 printf("ICCBased prop = %s\n", prop->toString(0, 0).c_str());
1620#endif // PDF_TRACE
1621 // TODO(edisonn): hack
1622 if (prop && prop->isDictionary() && prop->get("N") && prop->get("N")->isInteger() && prop->get("N")->intValue() == 3) {
1623 colorOperator->setColorSpace(&strings_DeviceRGB);
1624 return kPartial_PdfResult;
1625 }
1626 return kNYI_PdfResult;
1627 }
1628 }
1629 }
1630
1631 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001632}
1633
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001634static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001635 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1636}
1637
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001638static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001639 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1640}
1641
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001642static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001643 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001644// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001645
1646 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1647
1648 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001649 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001650 doubles = false;
1651 }
1652
1653#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001654 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001655#endif
1656
1657 for (int i = n - 1; i >= 0 ; i--) {
1658 if (doubles) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001659 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1660// } else {
1661// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001662 }
1663 }
1664
1665 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001666 // TODO(edisonn): do possible field values to enum at parsing time!
1667 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001668 if (colorOperator->fColorSpace.equals("DeviceRGB") || colorOperator->fColorSpace.equals("RGB")) {
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001669 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]), (U8CPU)(255*c[1]), (U8CPU)(255*c[2])));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001670 }
1671 return kPartial_PdfResult;
1672}
1673
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001674static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001675 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1676}
1677
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001678static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001679 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1680}
1681
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001682static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001683 if (pdfContext->fObjectStack.top()->isName()) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001684 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1685
1686 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001687 SkPdfDictionary* patternResources = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
edisonn@google.com276fed92013-08-01 21:20:47 +00001688
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001689 if (patternResources == NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +00001690#ifdef PDF_TRACE
1691 printf("ExtGState is NULL!\n");
1692#endif
1693 return kIgnoreError_PdfResult;
1694 }
1695
edisonn@google.come2e01ff2013-08-02 20:24:48 +00001696 colorOperator->setPatternColorSpace(pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001697 }
1698
1699 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1700 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1701
1702 return kPartial_PdfResult;
1703}
1704
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001705static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001706 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1707}
1708
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001709static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001710 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1711}
1712
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001713static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001714 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001715 return kNYI_PdfResult;
1716}
1717
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001718static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001719 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1720}
1721
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001722static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001723 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1724}
1725
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001726static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001727 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1728 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1729 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001730
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001731 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001732 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001733 return kOK_PdfResult;
1734}
1735
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001736static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001737 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1738}
1739
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001740static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001741 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1742}
1743
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001744static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001745 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001746 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1747 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1748 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1749 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001750
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001751 colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001752 // TODO(edisonn): Set color.
1753 return kNYI_PdfResult;
1754}
1755
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001756static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001757 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1758}
1759
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001760static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001761 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1762}
1763
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001764static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001765 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1766 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1767
1768 return kOK_PdfResult;
1769}
1770
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001771static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001772 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1773
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001774 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1775 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1776
edisonn@google.com96ba3aa2013-07-28 20:04:35 +00001777 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001778}
1779
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001780static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001781 *looper = new PdfCompatibilitySectionLooper();
1782 return kOK_PdfResult;
1783}
1784
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001785static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001786#ifdef ASSERT_BAD_PDF_OPS
1787 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1788 // have the assert when testing good pdfs.
1789#endif
1790 return kIgnoreError_PdfResult;
1791}
1792
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001793static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001794 *looper = new PdfInlineImageLooper();
1795 return kOK_PdfResult;
1796}
1797
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001798static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001799#ifdef ASSERT_BAD_PDF_OPS
1800 SkASSERT(false); // must be processed in inline image looper, but let's
1801 // have the assert when testing good pdfs.
1802#endif
1803 return kIgnoreError_PdfResult;
1804}
1805
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001806static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001807#ifdef ASSERT_BAD_PDF_OPS
1808 SkASSERT(false); // must be processed in inline image looper, but let's
1809 // have the assert when testing good pdfs.
1810#endif
1811 return kIgnoreError_PdfResult;
1812}
1813
edisonn@google.com24cdf132013-07-30 16:06:12 +00001814
1815// TODO(edisonn): security review here, make sure all parameters are valid, and safe.
1816PdfResult skpdfGraphicsStateApply_ca(PdfContext* pdfContext, double ca) {
1817 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
1818 return kOK_PdfResult;
1819}
1820
1821PdfResult skpdfGraphicsStateApply_CA(PdfContext* pdfContext, double CA) {
1822 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
1823 return kOK_PdfResult;
1824}
1825
1826PdfResult skpdfGraphicsStateApplyLW(PdfContext* pdfContext, double lineWidth) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001827 pdfContext->fGraphicsState.fLineWidth = lineWidth;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001828 return kOK_PdfResult;
1829}
1830
1831PdfResult skpdfGraphicsStateApplyLC(PdfContext* pdfContext, int64_t lineCap) {
1832 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
1833 return kOK_PdfResult;
1834}
1835
1836PdfResult skpdfGraphicsStateApplyLJ(PdfContext* pdfContext, int64_t lineJoin) {
1837 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
1838 return kOK_PdfResult;
1839}
1840
1841PdfResult skpdfGraphicsStateApplyML(PdfContext* pdfContext, double miterLimit) {
1842 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
1843 return kOK_PdfResult;
1844}
1845
1846// TODO(edisonn): implement all rules, as of now 3) and 5) and 6) do not seem suported by skia, but I am not sure
1847/*
18481) [ ] 0 No dash; solid, unbroken lines
18492) [3] 0 3 units on, 3 units off, …
18503) [2] 1 1 on, 2 off, 2 on, 2 off, …
18514) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
18525) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
18536) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
1854 */
1855
1856PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* intervals, SkPdfObject* phase) {
1857 int cnt = intervals->size();
1858 if (cnt >= 256) {
1859 // TODO(edisonn): report error/warning, unsuported;
1860 // TODO(edisonn): alloc memory
1861 return kIgnoreError_PdfResult;
1862 }
1863 for (int i = 0; i < cnt; i++) {
1864 if (!intervals->objAtAIndex(i)->isNumber()) {
1865 // TODO(edisonn): report error/warning
1866 return kIgnoreError_PdfResult;
1867 }
1868 }
1869
edisonn@google.com24cdf132013-07-30 16:06:12 +00001870 double total = 0;
1871 for (int i = 0 ; i < cnt; i++) {
1872 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
1873 total += pdfContext->fGraphicsState.fDashArray[i];
1874 }
edisonn@google.comf111a4b2013-07-31 18:22:36 +00001875 if (cnt & 1) {
1876 if (cnt == 1) {
1877 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
1878 cnt++;
1879 } else {
1880 // TODO(edisonn): report error/warning
1881 return kNYI_PdfResult;
1882 }
1883 }
1884 pdfContext->fGraphicsState.fDashArrayLength = cnt;
edisonn@google.com24cdf132013-07-30 16:06:12 +00001885 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
1886 if (pdfContext->fGraphicsState.fDashPhase == 0) {
1887 // other rules, changes?
1888 pdfContext->fGraphicsState.fDashPhase = total;
1889 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001890
1891 return kOK_PdfResult;
1892}
1893
edisonn@google.com24cdf132013-07-30 16:06:12 +00001894PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* dash) {
1895 // TODO(edisonn): verify input
1896 if (!dash || dash->isArray() || dash->size() != 2 || !dash->objAtAIndex(0)->isArray() || !dash->objAtAIndex(1)->isNumber()) {
1897 // TODO(edisonn): report error/warning
1898 return kIgnoreError_PdfResult;
1899 }
1900 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0), dash->objAtAIndex(1));
1901}
1902
1903void skpdfGraphicsStateApplyFont(PdfContext* pdfContext, SkPdfArray* fontAndSize) {
1904 if (!fontAndSize || fontAndSize->isArray() || fontAndSize->size() != 2 || !fontAndSize->objAtAIndex(0)->isName() || !fontAndSize->objAtAIndex(1)->isNumber()) {
1905 // TODO(edisonn): report error/warning
1906 return;
1907 }
1908 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), fontAndSize->objAtAIndex(1)->numberValue());
1909}
1910
1911
1912//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1913static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1914 double lw = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1915 return skpdfGraphicsStateApplyLW(pdfContext, lw);
1916}
1917
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001918//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 +00001919static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001920 int64_t lc = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1921 return skpdfGraphicsStateApplyLC(pdfContext, lc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001922}
1923
1924//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 +00001925static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001926 double lj = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1927 return skpdfGraphicsStateApplyLJ(pdfContext, lj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001928}
1929
1930//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001931static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001932 double ml = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1933 return skpdfGraphicsStateApplyML(pdfContext, ml);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001934}
1935
1936//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1937//page 155).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001938static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com24cdf132013-07-30 16:06:12 +00001939 SkPdfObject* phase = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1940 SkPdfObject* array = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001941
edisonn@google.com24cdf132013-07-30 16:06:12 +00001942 if (!array->isArray()) {
1943 return kIgnoreError_PdfResult;
1944 }
1945
1946 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)array, phase);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001947}
1948
1949//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 +00001950static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001951 pdfContext->fObjectStack.pop();
1952
1953 return kNYI_PdfResult;
1954}
1955
1956//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1957//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1958//fies the output device’s default flatness tolerance.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001959static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001960 pdfContext->fObjectStack.pop();
1961
1962 return kNYI_PdfResult;
1963}
1964
edisonn@google.come878e722013-07-29 19:10:58 +00001965SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
1966
1967class InitBlendModes {
1968public:
1969 InitBlendModes() {
1970 // TODO(edisonn): use the python code generator?
1971 // TABLE 7.2 Standard separable blend modes
1972 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
1973 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
1974 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
1975 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
1976 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
1977 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
1978 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
1979 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
1980 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
1981 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
1982 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
1983 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
1984
1985 // TABLE 7.3 Standard nonseparable blend modes
1986 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
1987 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
1988 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
1989 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
1990 }
1991};
1992
1993InitBlendModes _gDummyInniter;
1994
1995SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
1996 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
1997 if (gPdfBlendModes.find(blendMode, len, &mode)) {
1998 return mode;
1999 }
2000
2001 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2002}
2003
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002004void skpdfGraphicsStateApplyBM_name(PdfContext* pdfContext, const std::string& blendMode) {
edisonn@google.come878e722013-07-29 19:10:58 +00002005 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.length());
2006 if (mode <= SkXfermode::kLastMode) {
2007 pdfContext->fGraphicsState.fBlendModesLength = 1;
2008 pdfContext->fGraphicsState.fBlendModes[0] = mode;
2009 } else {
2010 // TODO(edisonn): report unknown blend mode
2011 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002012}
2013
2014void skpdfGraphicsStateApplyBM_array(PdfContext* pdfContext, SkPdfArray* blendModes) {
edisonn@google.come878e722013-07-29 19:10:58 +00002015 if (!blendModes || blendModes->isArray() || blendModes->size() == 0 || blendModes->size() > 256) {
2016 // TODO(edisonn): report error/warning
2017 return;
2018 }
2019 SkXfermode::Mode modes[256];
2020 int cnt = blendModes->size();
2021 for (int i = 0; i < cnt; i++) {
2022 SkPdfObject* name = blendModes->objAtAIndex(i);
2023 if (!name->isName()) {
2024 // TODO(edisonn): report error/warning
2025 return;
2026 }
2027 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
2028 if (mode > SkXfermode::kLastMode) {
2029 // TODO(edisonn): report error/warning
2030 return;
2031 }
2032 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002033
edisonn@google.come878e722013-07-29 19:10:58 +00002034 pdfContext->fGraphicsState.fBlendModesLength = cnt;
2035 for (int i = 0; i < cnt; i++) {
2036 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
2037 }
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002038}
2039
2040void skpdfGraphicsStateApplySMask_dict(PdfContext* pdfContext, SkPdfDictionary* sMask) {
2041 // TODO(edisonn): verify input
edisonn@google.come878e722013-07-29 19:10:58 +00002042 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002043 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
edisonn@google.come878e722013-07-29 19:10:58 +00002044 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
2045 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
2046 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
2047 } else {
2048 // TODO (edisonn): report error/warning
2049 }
2050}
2051
2052void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string& sMask) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002053 if (sMask == "None") {
2054 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002055 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002056 return;
2057 }
2058
edisonn@google.come878e722013-07-29 19:10:58 +00002059 //Next, get the ExtGState Dictionary from the Resource Dictionary:
2060 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2061
2062 if (extGStateDictionary == NULL) {
2063#ifdef PDF_TRACE
2064 printf("ExtGState is NULL!\n");
2065#endif
2066 // TODO (edisonn): report error/warning
2067 return;
2068 }
2069
2070 SkPdfObject* obj = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
2071 if (!obj || !obj->isDictionary()) {
2072 // TODO (edisonn): report error/warning
2073 return;
2074 }
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002075
2076 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
edisonn@google.comb0145ce2013-08-05 16:23:23 +00002077 pdfContext->fGraphicsState.fSMask = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002078
edisonn@google.come878e722013-07-29 19:10:58 +00002079 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002080}
2081
2082void skpdfGraphicsStateApplyAIS(PdfContext* pdfContext, bool alphaSource) {
2083 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2084}
2085
2086
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002087//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2088//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 +00002089static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002090 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002091
2092#ifdef PDF_TRACE
2093 std::string str;
2094#endif
2095
2096 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00002097 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002098
2099 if (extGStateDictionary == NULL) {
2100#ifdef PDF_TRACE
2101 printf("ExtGState is NULL!\n");
2102#endif
2103 return kIgnoreError_PdfResult;
2104 }
2105
edisonn@google.com571c70b2013-07-10 17:09:50 +00002106 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002107
edisonn@google.com571c70b2013-07-10 17:09:50 +00002108 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2109 return kIgnoreError_PdfResult;
2110 }
2111 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002112
2113 // TODO(edisonn): now load all those properties in graphic state.
2114 if (gs == NULL) {
2115 return kIgnoreError_PdfResult;
2116 }
2117
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002118 if (gs->has_LW()) {
2119 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2120 }
2121
2122 if (gs->has_LC()) {
2123 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2124 }
2125
2126 if (gs->has_LJ()) {
2127 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2128 }
2129
2130 if (gs->has_ML()) {
2131 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2132 }
2133
2134 if (gs->has_D()) {
2135 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2136 }
2137
2138 if (gs->has_Font()) {
2139 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2140 }
2141
2142 if (gs->has_BM()) {
2143 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2144 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2145 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2146 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2147 } else {
2148 // TODO(edisonn): report/warn
2149 }
2150 }
2151
2152 if (gs->has_SMask()) {
2153 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2154 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2155 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
2156 skpdfGraphicsStateApplySMask_dict(pdfContext, gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
2157 } else {
2158 // TODO(edisonn): report/warn
2159 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002160 }
2161
2162 if (gs->has_ca()) {
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002163 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002164 }
2165
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002166 if (gs->has_CA()) {
2167 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2168 }
2169
2170 if (gs->has_AIS()) {
2171 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002172 }
2173
edisonn@google.com9a43c182013-08-01 20:06:42 +00002174 return kOK_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002175}
2176
2177//charSpace Tc Set the character spacing, Tc
2178//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
2179//Initial value: 0.
2180PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002181 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002182 pdfContext->fGraphicsState.fCharSpace = charSpace;
2183
2184 return kOK_PdfResult;
2185}
2186
2187//wordSpace Tw Set the word spacing, T
2188//w
2189//, to wordSpace, which is a number expressed in unscaled
2190//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2191//value: 0.
2192PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002193 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002194 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2195
2196 return kOK_PdfResult;
2197}
2198
2199//scale Tz Set the horizontal scaling, Th
2200//, to (scale ˜ 100). scale is a number specifying the
2201//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002202static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002203 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002204
2205 return kNYI_PdfResult;
2206}
2207
2208//render Tr Set the text rendering mode, T
2209//mode, to render, which is an integer. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002210static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002211 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002212
2213 return kNYI_PdfResult;
2214}
2215//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2216//units. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002217static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002218 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002219
2220 return kNYI_PdfResult;
2221}
2222
2223//wx wy d0
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002224static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002225 pdfContext->fObjectStack.pop();
2226 pdfContext->fObjectStack.pop();
2227
2228 return kNYI_PdfResult;
2229}
2230
2231//wx wy llx lly urx ury d1
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002232static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002233 pdfContext->fObjectStack.pop();
2234 pdfContext->fObjectStack.pop();
2235 pdfContext->fObjectStack.pop();
2236 pdfContext->fObjectStack.pop();
2237 pdfContext->fObjectStack.pop();
2238 pdfContext->fObjectStack.pop();
2239
2240 return kNYI_PdfResult;
2241}
2242
2243//name sh
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002244static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002245 pdfContext->fObjectStack.pop();
2246
2247 return kNYI_PdfResult;
2248}
2249
2250//name Do
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002251static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002252 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002253
edisonn@google.com571c70b2013-07-10 17:09:50 +00002254 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002255
2256 if (xObject == NULL) {
2257#ifdef PDF_TRACE
2258 printf("XObject is NULL!\n");
2259#endif
2260 return kIgnoreError_PdfResult;
2261 }
2262
edisonn@google.com571c70b2013-07-10 17:09:50 +00002263 SkPdfObject* value = xObject->get(name);
2264 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002265
2266#ifdef PDF_TRACE
2267// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00002268// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002269#endif
2270
edisonn@google.com571c70b2013-07-10 17:09:50 +00002271 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002272}
2273
2274//tag MP Designate a marked-content point. tag is a name object indicating the role or
2275//significance of the point.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002276static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002277 pdfContext->fObjectStack.pop();
2278
2279 return kNYI_PdfResult;
2280}
2281
2282//tag properties DP Designate a marked-content point with an associated property list. tag is a
2283//name object indicating the role or significance of the point; properties is
2284//either an inline dictionary containing the property list or a name object
2285//associated with it in the Properties subdictionary of the current resource
2286//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002287static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002288 pdfContext->fObjectStack.pop();
2289 pdfContext->fObjectStack.pop();
2290
2291 return kNYI_PdfResult;
2292}
2293
2294//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2295//tag is a name object indicating the role or significance of the sequence.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002296static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002297 pdfContext->fObjectStack.pop();
2298
2299 return kNYI_PdfResult;
2300}
2301
2302//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2303//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
2304//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 +00002305static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002306 pdfContext->fObjectStack.pop();
2307 pdfContext->fObjectStack.pop();
2308
2309 return kNYI_PdfResult;
2310}
2311
2312//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002313static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002314 return kNYI_PdfResult;
2315}
2316
edisonn@google.coma3356fc2013-07-10 18:20:06 +00002317static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002318 static bool gInitialized = false;
2319 if (gInitialized) {
2320 return;
2321 }
2322
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002323 gPdfOps.set("q", PdfOp_q);
2324 gPdfOps.set("Q", PdfOp_Q);
2325 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002326
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002327 gPdfOps.set("TD", PdfOp_TD);
2328 gPdfOps.set("Td", PdfOp_Td);
2329 gPdfOps.set("Tm", PdfOp_Tm);
2330 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002331
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002332 gPdfOps.set("m", PdfOp_m);
2333 gPdfOps.set("l", PdfOp_l);
2334 gPdfOps.set("c", PdfOp_c);
2335 gPdfOps.set("v", PdfOp_v);
2336 gPdfOps.set("y", PdfOp_y);
2337 gPdfOps.set("h", PdfOp_h);
2338 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002339
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002340 gPdfOps.set("S", PdfOp_S);
2341 gPdfOps.set("s", PdfOp_s);
2342 gPdfOps.set("f", PdfOp_f);
2343 gPdfOps.set("F", PdfOp_F);
2344 gPdfOps.set("f*", PdfOp_f_star);
2345 gPdfOps.set("B", PdfOp_B);
2346 gPdfOps.set("B*", PdfOp_B_star);
2347 gPdfOps.set("b", PdfOp_b);
2348 gPdfOps.set("b*", PdfOp_b_star);
2349 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002350
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002351 gPdfOps.set("BT", PdfOp_BT);
2352 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002353
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002354 gPdfOps.set("Tj", PdfOp_Tj);
2355 gPdfOps.set("'", PdfOp_quote);
2356 gPdfOps.set("\"", PdfOp_doublequote);
2357 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002358
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002359 gPdfOps.set("CS", PdfOp_CS);
2360 gPdfOps.set("cs", PdfOp_cs);
2361 gPdfOps.set("SC", PdfOp_SC);
2362 gPdfOps.set("SCN", PdfOp_SCN);
2363 gPdfOps.set("sc", PdfOp_sc);
2364 gPdfOps.set("scn", PdfOp_scn);
2365 gPdfOps.set("G", PdfOp_G);
2366 gPdfOps.set("g", PdfOp_g);
2367 gPdfOps.set("RG", PdfOp_RG);
2368 gPdfOps.set("rg", PdfOp_rg);
2369 gPdfOps.set("K", PdfOp_K);
2370 gPdfOps.set("k", PdfOp_k);
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("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002374
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002375 gPdfOps.set("BX", PdfOp_BX);
2376 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002377
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002378 gPdfOps.set("BI", PdfOp_BI);
2379 gPdfOps.set("ID", PdfOp_ID);
2380 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002381
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002382 gPdfOps.set("w", PdfOp_w);
2383 gPdfOps.set("J", PdfOp_J);
2384 gPdfOps.set("j", PdfOp_j);
2385 gPdfOps.set("M", PdfOp_M);
2386 gPdfOps.set("d", PdfOp_d);
2387 gPdfOps.set("ri", PdfOp_ri);
2388 gPdfOps.set("i", PdfOp_i);
2389 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002390
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002391 gPdfOps.set("Tc", PdfOp_Tc);
2392 gPdfOps.set("Tw", PdfOp_Tw);
2393 gPdfOps.set("Tz", PdfOp_Tz);
2394 gPdfOps.set("TL", PdfOp_TL);
2395 gPdfOps.set("Tf", PdfOp_Tf);
2396 gPdfOps.set("Tr", PdfOp_Tr);
2397 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002398
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002399 gPdfOps.set("d0", PdfOp_d0);
2400 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002401
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002402 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002403
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002404 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002405
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002406 gPdfOps.set("MP", PdfOp_MP);
2407 gPdfOps.set("DP", PdfOp_DP);
2408 gPdfOps.set("BMC", PdfOp_BMC);
2409 gPdfOps.set("BDC", PdfOp_BDC);
2410 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002411
2412 gInitialized = true;
2413}
2414
2415class InitPdfOps {
2416public:
2417 InitPdfOps() {
2418 initPdfOperatorRenderes();
2419 }
2420};
2421
2422InitPdfOps gInitPdfOps;
2423
2424void reportPdfRenderStats() {
2425 std::map<std::string, int>::iterator iter;
2426
2427 for (int i = 0 ; i < kCount_PdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002428 SkTDict<int>::Iter iter(gRenderStats[i]);
2429 const char* key;
2430 int value = 0;
2431 while ((key = iter.next(&value)) != NULL) {
2432 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002433 }
2434 }
2435}
2436
2437PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002438 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002439 {
2440 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002441 PdfOperatorRenderer pdfOperatorRenderer = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002442 if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) && pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002443 // caller, main work is done by pdfOperatorRenderer(...)
2444 PdfTokenLooper* childLooper = NULL;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002445 PdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
2446
2447 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002448 gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
2449 gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002450
2451 if (childLooper) {
2452 childLooper->setUp(this);
2453 childLooper->loop();
2454 delete childLooper;
2455 }
2456 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002457 int cnt = 0;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +00002458 gRenderStats[kUnsupported_PdfResult].find(token.fKeyword, token.fKeywordLength, &cnt);
2459 gRenderStats[kUnsupported_PdfResult].set(token.fKeyword, token.fKeywordLength, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002460 }
2461 }
2462 else if (token.fType == kObject_TokenType)
2463 {
2464 fPdfContext->fObjectStack.push( token.fObject );
2465 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002466 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00002467 // TODO(edisonn): deine or use assert not reached
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002468 return kIgnoreError_PdfResult;
2469 }
2470 return kOK_PdfResult;
2471}
2472
2473void PdfMainLooper::loop() {
2474 PdfToken token;
2475 while (readToken(fTokenizer, &token)) {
2476 consumeToken(token);
2477 }
2478}
2479
2480PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002481 SkASSERT(false);
2482 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002483}
2484
2485void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00002486 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002487}
2488
2489PdfResult PdfInlineImageLooper::done() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002490 return kNYI_PdfResult;
2491}
2492
2493PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2494 return fParent->consumeToken(token);
2495}
2496
2497void PdfCompatibilitySectionLooper::loop() {
2498 // TODO(edisonn): save stacks position, or create a new stack?
2499 // TODO(edisonn): what happens if we pop out more variables then when we started?
2500 // restore them? fail? We could create a new operands stack for every new BX/EX section,
2501 // pop-ing too much will not affect outside the section.
2502 PdfToken token;
2503 while (readToken(fTokenizer, &token)) {
2504 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2505 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2506 looper->setUp(this);
2507 looper->loop();
2508 delete looper;
2509 } else {
2510 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
2511 fParent->consumeToken(token);
2512 }
2513 }
2514 // TODO(edisonn): restore stack.
2515}
2516
2517// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2518// TODO(edisonn): Add API for Forms viewing and editing
2519// e.g. SkBitmap getPage(int page);
2520// int formsCount();
2521// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2522// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
2523// if we load the first page, and we zoom to fit to screen horizontally, then load only those
2524// resources needed, so the preview is fast.
2525// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
2526// references automatically.
2527
edisonn@google.com222382b2013-07-10 22:33:10 +00002528PdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00002529
edisonn@google.com444e25a2013-07-11 15:20:50 +00002530bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00002531 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002532 return false;
2533 }
2534
edisonn@google.com222382b2013-07-10 22:33:10 +00002535 if (page < 0 || page >= pages()) {
2536 return false;
2537 }
2538
edisonn@google.com222382b2013-07-10 22:33:10 +00002539 PdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00002540
edisonn@google.com222382b2013-07-10 22:33:10 +00002541 pdfContext.fOriginalMatrix = SkMatrix::I();
2542 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2543
2544 gPdfContext = &pdfContext;
2545
2546 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00002547 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00002548 SkScalar w = dst.width();
2549 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00002550
edisonn@google.com444e25a2013-07-11 15:20:50 +00002551 SkScalar wp = fPdfDoc->MediaBox(page).width();
2552 SkScalar hp = fPdfDoc->MediaBox(page).height();
2553
2554 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 +00002555// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2556
2557 // TODO(edisonn): add flag for this app to create sourunding buffer zone
2558 // TODO(edisonn): add flagg for no clipping.
2559 // Use larger image to make sure we do not draw anything outside of page
2560 // could be used in tests.
2561
2562#ifdef PDF_DEBUG_3X
2563 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)};
2564#else
2565 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2566#endif
2567 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
2568 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2569
2570 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
2571 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2572
2573 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
2574 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2575
2576 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2577 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2578
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002579 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
edisonn@google.com0f901902013-08-07 11:56:16 +00002580 pdfContext.fGraphicsState.fContentStreamMatrix = pdfContext.fOriginalMatrix;
edisonn@google.coma0cefa12013-07-28 18:34:14 +00002581 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
2582 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
edisonn@google.com222382b2013-07-10 22:33:10 +00002583
edisonn@google.com222382b2013-07-10 22:33:10 +00002584#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00002585 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00002586#endif
2587
edisonn@google.com444e25a2013-07-11 15:20:50 +00002588 canvas->setMatrix(pdfContext.fOriginalMatrix);
2589
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002590 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2591
2592 // TODO(edisonn:) erase with white before draw?
edisonn@google.com222382b2013-07-10 22:33:10 +00002593// SkPaint paint;
edisonn@google.com88fc03d2013-07-30 13:34:10 +00002594// paint.setColor(SK_ColorWHITE);
edisonn@google.com222382b2013-07-10 22:33:10 +00002595// canvas->drawRect(rect, paint);
2596
edisonn@google.com222382b2013-07-10 22:33:10 +00002597
2598 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002599 return true;
2600}
edisonn@google.com222382b2013-07-10 22:33:10 +00002601
2602bool SkPdfRenderer::load(const SkString inputFileName) {
2603 unload();
2604
2605 // TODO(edisonn): create static function that could return NULL if there are errors
2606 fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00002607 if (fPdfDoc->pages() == 0) {
2608 delete fPdfDoc;
2609 fPdfDoc = NULL;
2610 }
edisonn@google.com222382b2013-07-10 22:33:10 +00002611
2612 return fPdfDoc != NULL;
2613}
2614
edisonn@google.com147adb12013-07-24 15:56:19 +00002615bool SkPdfRenderer::load(SkStream* stream) {
2616 unload();
2617
2618 // TODO(edisonn): create static function that could return NULL if there are errors
2619 fPdfDoc = new SkNativeParsedPDF(stream);
2620 if (fPdfDoc->pages() == 0) {
2621 delete fPdfDoc;
2622 fPdfDoc = NULL;
2623 }
2624
2625 return fPdfDoc != NULL;
2626}
2627
2628
edisonn@google.com222382b2013-07-10 22:33:10 +00002629int SkPdfRenderer::pages() const {
2630 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2631}
2632
2633void SkPdfRenderer::unload() {
2634 delete fPdfDoc;
2635 fPdfDoc = NULL;
2636}
2637
2638SkRect SkPdfRenderer::MediaBox(int page) const {
2639 SkASSERT(fPdfDoc);
2640 return fPdfDoc->MediaBox(page);
2641}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002642
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002643size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002644 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2645}
edisonn@google.com147adb12013-07-24 15:56:19 +00002646
2647bool SkPDFNativeRenderToBitmap(SkStream* stream,
2648 SkBitmap* output,
2649 int page,
2650 SkPdfContent content,
2651 double dpi) {
2652 SkASSERT(page >= 0);
2653 SkPdfRenderer renderer;
2654 renderer.load(stream);
2655 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
2656 return false;
2657 }
2658
2659 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
2660
2661 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2662 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2663
2664 rect = SkRect::MakeWH(width, height);
2665
2666 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
2667
2668 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*output)));
2669 SkCanvas canvas(device);
2670
2671 return renderer.renderPage(page, &canvas, rect);
2672}