blob: 1ace7ca5ae1bea06754a3ec6d8113624da774542 [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.com131d4ee2013-06-26 17:48:12 +000019
edisonn@google.com15b11182013-07-11 14:43:15 +000020#include "SkPdfBasics.h"
21#include "SkPdfNativeTokenizer.h"
22
edisonn@google.com131d4ee2013-06-26 17:48:12 +000023#include <iostream>
24#include <cstdio>
25#include <stack>
edisonn@google.com571c70b2013-07-10 17:09:50 +000026#include <set>
edisonn@google.com131d4ee2013-06-26 17:48:12 +000027
edisonn@google.com15b11182013-07-11 14:43:15 +000028extern "C" PdfContext* gPdfContext;
29extern "C" SkBitmap* gDumpBitmap;
30extern "C" SkCanvas* gDumpCanvas;
31
edisonn@google.com131d4ee2013-06-26 17:48:12 +000032__SK_FORCE_IMAGE_DECODER_LINKING;
33
34// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
35// keep for each object pos in file
36// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
37
38// TODO(edisonn): security - validate all the user input, all pdf!
39
edisonn@google.com6e49c342013-06-27 20:03:43 +000040// TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor changes in text positioning and font
41// this way, we look more at other features and layout in diffs
edisonn@google.com131d4ee2013-06-26 17:48:12 +000042
edisonn@google.com3aac1f92013-07-02 22:42:53 +000043// TODO(edisonn): move trace dump in the get functions, and mapper ones too so it ghappens automatically
44/*
45#ifdef PDF_TRACE
46 std::string str;
edisonn@google.com571c70b2013-07-10 17:09:50 +000047 pdfContext->fGraphicsState.fResources->native()->ToString(str);
edisonn@google.com3aac1f92013-07-02 22:42:53 +000048 printf("Print Tf Resources: %s\n", str.c_str());
49#endif
50 */
51
edisonn@google.com131d4ee2013-06-26 17:48:12 +000052#include "SkPdfHeaders_autogen.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +000053#include "SkPdfMapper_autogen.h"
edisonn@google.com222382b2013-07-10 22:33:10 +000054#include "SkPdfRenderer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000055
56#include "SkPdfBasics.h"
57#include "SkPdfUtils.h"
58
59#include "SkPdfFont.h"
60
edisonn@google.com131d4ee2013-06-26 17:48:12 +000061/*
62 * TODO(edisonn):
63 * - all font types and all ppdf font features
64 * - word spacing
65 * - load font for baidu.pdf
66 * - load font for youtube.pdf
67 * - parser for pdf from the definition already available in pdfspec_autogen.py
68 * - all docs from ~/work
edisonn@google.com571c70b2013-07-10 17:09:50 +000069 * - 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 +000070 * - load gs/ especially smask and already known prop (skp) ... in progress
71 * - wrapper on classes for customizations? e.g.
72 * SkPdfPageObjectVanila - has only the basic loaders/getters
73 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
74 * need to find a nice object model for all this with constructors and factories
75 * - deal with inheritable automatically ?
76 * - deal with specific type in spec directly, add all dictionary types to known types
77*/
78
79using namespace std;
edisonn@google.com131d4ee2013-06-26 17:48:12 +000080
edisonn@google.com222382b2013-07-10 22:33:10 +000081
82
83// TODO(edisonn): Document PdfTokenLooper and subclasses.
84class PdfTokenLooper {
85protected:
86 PdfTokenLooper* fParent;
87 SkPdfNativeTokenizer* fTokenizer;
88 PdfContext* fPdfContext;
89 SkCanvas* fCanvas;
90
91public:
92 PdfTokenLooper(PdfTokenLooper* parent,
93 SkPdfNativeTokenizer* tokenizer,
94 PdfContext* pdfContext,
95 SkCanvas* canvas)
96 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
97
98 virtual ~PdfTokenLooper() {}
99
100 virtual PdfResult consumeToken(PdfToken& token) = 0;
101 virtual void loop() = 0;
102
103 void setUp(PdfTokenLooper* parent) {
104 fParent = parent;
105 fTokenizer = parent->fTokenizer;
106 fPdfContext = parent->fPdfContext;
107 fCanvas = parent->fCanvas;
108 }
edisonn@google.com78b38b12013-07-15 18:20:58 +0000109
110 SkPdfNativeTokenizer* tokenizer() { return fTokenizer; }
edisonn@google.com222382b2013-07-10 22:33:10 +0000111};
112
113class PdfMainLooper : public PdfTokenLooper {
114public:
115 PdfMainLooper(PdfTokenLooper* parent,
116 SkPdfNativeTokenizer* tokenizer,
117 PdfContext* pdfContext,
118 SkCanvas* canvas)
119 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
120
121 virtual PdfResult consumeToken(PdfToken& token);
122 virtual void loop();
123};
124
125class PdfInlineImageLooper : public PdfTokenLooper {
126public:
127 PdfInlineImageLooper()
128 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
129
130 virtual PdfResult consumeToken(PdfToken& token);
131 virtual void loop();
132 PdfResult done();
133};
134
135class PdfCompatibilitySectionLooper : public PdfTokenLooper {
136public:
137 PdfCompatibilitySectionLooper()
138 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
139
140 virtual PdfResult consumeToken(PdfToken& token);
141 virtual void loop();
142};
143
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000144// Utilities
145static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
146 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
147
148 bitmap->allocPixels();
149 bitmap->eraseColor(color);
150}
151
152// TODO(edisonn): synonyms? DeviceRGB and RGB ...
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000153static int GetColorSpaceComponents(const std::string& colorSpace) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000154 if (colorSpace == "DeviceCMYK") {
155 return 4;
156 } else if (colorSpace == "DeviceGray" ||
157 colorSpace == "CalGray" ||
158 colorSpace == "Indexed") {
159 return 1;
160 } else if (colorSpace == "DeviceRGB" ||
161 colorSpace == "CalRGB" ||
162 colorSpace == "Lab") {
163 return 3;
164 } else {
165 return 0;
166 }
167}
168
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000169SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000170 SkMatrix matrix;
171 matrix.setAll(SkDoubleToScalar(array[0]),
172 SkDoubleToScalar(array[2]),
173 SkDoubleToScalar(array[4]),
174 SkDoubleToScalar(array[1]),
175 SkDoubleToScalar(array[3]),
176 SkDoubleToScalar(array[5]),
177 SkDoubleToScalar(0),
178 SkDoubleToScalar(0),
179 SkDoubleToScalar(1));
180
181 return matrix;
182}
183
184SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
185 double array[6];
186
187 // TODO(edisonn): security issue, ret if size() != 6
188 for (int i = 0; i < 6; i++) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000189 const SkPdfObject* elem = pdfArray->operator [](i);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000190 if (elem == NULL || !elem->isNumber()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000191 return SkMatrix::I(); // TODO(edisonn): report issue
192 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000193 array[i] = elem->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000194 }
195
196 return SkMatrixFromPdfMatrix(array);
197}
198
edisonn@google.com222382b2013-07-10 22:33:10 +0000199
200extern "C" SkNativeParsedPDF* gDoc;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000201SkBitmap* gDumpBitmap = NULL;
202SkCanvas* gDumpCanvas = NULL;
203char gLastKeyword[100] = "";
204int gLastOpKeyword = -1;
205char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
206int gReadOp = 0;
207
208
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000209#ifdef PDF_TRACE_DIFF_IN_PNG
210static bool hasVisualEffect(const char* pdfOp) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000211 return true;
212 if (*pdfOp == '\0') return false;
213
214 char markedPdfOp[100] = ",";
215 strcat(markedPdfOp, pdfOp);
216 strcat(markedPdfOp, ",");
217
218 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
219}
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000220#endif // PDF_TRACE_DIFF_IN_PNG
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000221
edisonn@google.com222382b2013-07-10 22:33:10 +0000222
223
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000224// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000225static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000226 bool ret = fTokenizer->readToken(token);
227
228 gReadOp++;
229
230#ifdef PDF_TRACE_DIFF_IN_PNG
231 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
232 // the numbar and name of last operation, so the file name will reflect op that changed.
233 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
234 gDumpCanvas->flush();
235
236 SkBitmap bitmap;
237 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
238
239 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
240
241 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
242 SkCanvas canvas(device);
243
244 // draw context stuff here
245 SkPaint blueBorder;
246 blueBorder.setColor(SK_ColorBLUE);
247 blueBorder.setStyle(SkPaint::kStroke_Style);
248 blueBorder.setTextSize(SkDoubleToScalar(20));
249
250 SkString str;
251
252 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
253 if (clipStack) {
254 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
255 const SkClipStack::Element* elem;
256 double y = 0;
257 int total = 0;
258 while (elem = iter.next()) {
259 total++;
260 y += 30;
261
262 switch (elem->getType()) {
263 case SkClipStack::Element::kRect_Type:
264 canvas.drawRect(elem->getRect(), blueBorder);
265 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
266 break;
267 case SkClipStack::Element::kPath_Type:
268 canvas.drawPath(elem->getPath(), blueBorder);
269 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
270 break;
271 case SkClipStack::Element::kEmpty_Type:
272 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
273 break;
274 default:
275 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
276 break;
277 }
278 }
279
280 y += 30;
281 str.printf("Number of clips in stack: %i", total);
282 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
283 }
284
285 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
286 SkPath clipPath;
287 if (clipRegion.getBoundaryPath(&clipPath)) {
288 SkPaint redBorder;
289 redBorder.setColor(SK_ColorRED);
290 redBorder.setStyle(SkPaint::kStroke_Style);
291 canvas.drawPath(clipPath, redBorder);
292 }
293
294 canvas.flush();
295
296 SkString out;
297
298 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
299 // ... and other properties, to be able to debug th code easily
300
301 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
302 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
303 }
304
305 if (token->fType == kKeyword_TokenType) {
306 strcpy(gLastKeyword, token->fKeyword);
307 gLastOpKeyword = gReadOp;
308 } else {
309 strcpy(gLastKeyword, "");
310 }
311#endif
312
313 return ret;
314}
315
316
317
318typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
319
320map<std::string, PdfOperatorRenderer> gPdfOps;
321
322map<std::string, int> gRenderStats[kCount_PdfResult];
323
edisonn@google.com571c70b2013-07-10 17:09:50 +0000324const char* gRenderStatsNames[kCount_PdfResult] = {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000325 "Success",
326 "Partially implemented",
327 "Not yet implemented",
328 "Ignore Error",
329 "Error",
330 "Unsupported/Unknown"
331};
332
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000333static PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000334 const SkPdfObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000335 SkCanvas* canvas)
336{
337
338 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
339 if (skfont == NULL) {
340 skfont = SkPdfFont::Default();
341 }
342
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000343
edisonn@google.com571c70b2013-07-10 17:09:50 +0000344 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000345 // TODO(edisonn): report warning
346 return kIgnoreError_PdfResult;
347 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000348 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000349
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000350 SkUnencodedText binary(str);
351
352 SkDecodedText decoded;
353
354 if (skfont->encoding() == NULL) {
355 // TODO(edisonn): report warning
356 return kNYI_PdfResult;
357 }
358
359 skfont->encoding()->decodeText(binary, &decoded);
360
361 SkPaint paint;
362 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
363 // Or maybe just not call setTextSize at all?
364 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
365 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
366 }
367
368// if (fCurFont && fCurFont->GetFontScale() != 0) {
369// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
370// }
371
372 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
373
374 canvas->save();
375
376#if 1
377 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
378
379 SkPoint point1;
380 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
381
382 SkMatrix mirror;
383 mirror.setTranslate(0, -point1.y());
384 // TODO(edisonn): fix rotated text, and skewed too
385 mirror.postScale(SK_Scalar1, -SK_Scalar1);
386 // TODO(edisonn): post rotate, skew
387 mirror.postTranslate(0, point1.y());
388
389 matrix.postConcat(mirror);
390
391 canvas->setMatrix(matrix);
392
393 SkTraceMatrix(matrix, "mirrored");
394#endif
395
edisonn@google.com6e49c342013-06-27 20:03:43 +0000396 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000397 canvas->restore();
398
399 return kPartial_PdfResult;
400}
401
402// TODO(edisonn): create header files with declarations!
403PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
404PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
405PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
406PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
407
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000408// TODO(edisonn): perf!!!
409
410static SkColorTable* getGrayColortable() {
411 static SkColorTable* grayColortable = NULL;
412 if (grayColortable == NULL) {
413 SkPMColor* colors = new SkPMColor[256];
414 for (int i = 0 ; i < 256; i++) {
415 colors[i] = SkPreMultiplyARGB(255, i, i, i);
416 }
417 grayColortable = new SkColorTable(colors, 256);
418 }
419 return grayColortable;
420}
421
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000422static SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, size_t uncompressedStreamLength,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000423 int width, int height, int bytesPerLine,
424 int bpc, const std::string& colorSpace,
425 bool transparencyMask) {
426 SkBitmap bitmap;
427
edisonn@google.com571c70b2013-07-10 17:09:50 +0000428 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000429//#define MAX_COMPONENTS 10
430
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000431 // TODO(edisonn): assume start of lines are aligned at 32 bits?
432 // Is there a faster way to load the uncompressed stream into a bitmap?
433
434 // minimal support for now
435 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
436 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
437
438 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000439 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000440 for (int w = 0 ; w < width; w++) {
441 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
442 uncompressedStream[3 * w + 1],
443 uncompressedStream[3 * w + 2]);
444 i++;
445 }
446 uncompressedStream += bytesPerLine;
447 }
448
449 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
450 bitmap.setPixels(uncompressedStreamArgb);
451 }
452 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
453 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
454
455 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000456 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000457 for (int w = 0 ; w < width; w++) {
458 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
459 uncompressedStream[w];
460 i++;
461 }
462 uncompressedStream += bytesPerLine;
463 }
464
465 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
466 width, height);
467 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
468 }
469
470 // TODO(edisonn): Report Warning, NYI, or error
471 return bitmap;
472}
473
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000474// utils
475
476// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
477// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
478// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
479// skia format, through a table
480
481// this functions returns the image, it does not look at the smask.
482
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000483static SkBitmap getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000484 if (image == NULL || !image->hasStream()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000485 // TODO(edisonn): report warning to be used in testing.
486 return SkBitmap();
487 }
488
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000489 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc);
490 int64_t width = image->Width(pdfContext->fPdfDoc);
491 int64_t height = image->Height(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000492 std::string colorSpace = "DeviceRGB";
493
494 // TODO(edisonn): color space can be an array too!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000495 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
496 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000497 }
498
499/*
500 bool imageMask = image->imageMask();
501
502 if (imageMask) {
503 if (bpc != 0 && bpc != 1) {
504 // TODO(edisonn): report warning to be used in testing.
505 return SkBitmap();
506 }
507 bpc = 1;
508 }
509*/
510
edisonn@google.com571c70b2013-07-10 17:09:50 +0000511 unsigned char* uncompressedStream = NULL;
512 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000513
edisonn@google.com571c70b2013-07-10 17:09:50 +0000514 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000515
edisonn@google.com571c70b2013-07-10 17:09:50 +0000516 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength, pdfContext->fPdfDoc->allocator()) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000517 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000518 // TODO(edisonn): report warning to be used in testing.
519 return SkBitmap();
520 }
521
edisonn@google.com571c70b2013-07-10 17:09:50 +0000522 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
523
524 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
525 streamDict->getFilterAsName(NULL) == "DCTDecode") ||
526 (streamDict->isFilterAArray(NULL) &&
527 streamDict->getFilterAsArray(NULL)->size() > 0 &&
528 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
529 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() == "DCTDecode"))) {
530 SkBitmap bitmap;
531 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, &bitmap);
532 return bitmap;
533 }
534
535
536
537 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
538// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
539// obj.GetDictionary().GetKey(PdfName("Filter")));
540// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
541// value = resolveReferenceObject(pdfContext->fPdfDoc,
542// &value->GetArray()[0]);
543// }
544// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
545// SkStream stream = SkStream::
546// SkImageDecoder::Factory()
547// }
548
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000549 int bytesPerLine = uncompressedStreamLength / height;
550#ifdef PDF_TRACE
551 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000552 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000553 }
554#endif
555
556 SkBitmap bitmap = transferImageStreamToBitmap(
557 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000558 (int)width, (int)height, bytesPerLine,
559 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000560 transparencyMask);
561
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000562 return bitmap;
563}
564
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000565static SkBitmap getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000566 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000567
568 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000569 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000570 }
571
572 // TODO(edisonn): implement GS SMask. Default to empty right now.
573 return pdfContext->fGraphicsState.fSMask;
574}
575
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000576static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000577 if (skpdfimage == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000578 return kIgnoreError_PdfResult;
579 }
580
581 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
582 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
583
584 canvas->save();
585 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000586
587#if 1
588 SkScalar z = SkIntToScalar(0);
589 SkScalar one = SkIntToScalar(1);
590
591 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
592 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
593 SkMatrix flip;
594 SkAssertResult(flip.setPolyToPoly(from, to, 4));
595 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fMatrix;
596 solveImageFlip.preConcat(flip);
597 canvas->setMatrix(solveImageFlip);
598#endif
599
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000600 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
601
602 if (sMask.empty()) {
603 canvas->drawBitmapRect(image, dst, NULL);
604 } else {
605 canvas->saveLayer(&dst, NULL);
606 canvas->drawBitmapRect(image, dst, NULL);
607 SkPaint xfer;
608 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
609 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
610 canvas->drawBitmapRect(sMask, dst, &xfer);
611 canvas->restore();
612 }
613
614 canvas->restore();
615
616 return kPartial_PdfResult;
617}
618
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000619
620
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000621
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000622static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000623 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000624 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000625 }
626
627 PdfOp_q(pdfContext, canvas, NULL);
628 canvas->save();
629
630
edisonn@google.com571c70b2013-07-10 17:09:50 +0000631 if (skobj->Resources(pdfContext->fPdfDoc)) {
632 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000633 }
634
635 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
636
edisonn@google.com571c70b2013-07-10 17:09:50 +0000637 if (skobj->has_Matrix()) {
638 pdfContext->fGraphicsState.fMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000639 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
640 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
641 // TODO(edisonn) reset matrixTm and matricTlm also?
642 }
643
644 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
645
646 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
647
edisonn@google.com571c70b2013-07-10 17:09:50 +0000648 if (skobj->has_BBox()) {
649 canvas->clipRect(skobj->BBox(pdfContext->fPdfDoc), SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000650 }
651
652 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
653 // For this PdfContentsTokenizer needs to be extended.
654
edisonn@google.com571c70b2013-07-10 17:09:50 +0000655 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000656
edisonn@google.com571c70b2013-07-10 17:09:50 +0000657 SkPdfNativeTokenizer* tokenizer = pdfContext->fPdfDoc->tokenizerOfStream(stream);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000658 if (tokenizer != NULL) {
659 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
660 looper.loop();
661 delete tokenizer;
662 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000663
664 // TODO(edisonn): should we restore the variable stack at the same state?
665 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
666 canvas->restore();
667 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000668 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000669}
670
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000671//static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
672// return kNYI_PdfResult;
673//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000674
edisonn@google.com571c70b2013-07-10 17:09:50 +0000675PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
676 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000677 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000678 }
679
680 PdfOp_q(pdfContext, canvas, NULL);
681 canvas->save();
682
683 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
684 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
685
686 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm;
687 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
688
689 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
690
691 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
692
693 SkRect rm = bBox;
694 pdfContext->fGraphicsState.fMatrix.mapRect(&rm);
695
696 SkTraceRect(rm, "bbox mapped");
697
698 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
699
700 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
701 // For this PdfContentsTokenizer needs to be extended.
702
edisonn@google.com571c70b2013-07-10 17:09:50 +0000703 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000704
edisonn@google.com571c70b2013-07-10 17:09:50 +0000705 SkPdfNativeTokenizer* tokenizer = pdfContext->fPdfDoc->tokenizerOfStream(stream);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000706 if (tokenizer != NULL) {
707 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
708 looper.loop();
709 delete tokenizer;
710 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000711
712 // TODO(edisonn): should we restore the variable stack at the same state?
713 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
714 canvas->restore();
715 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000716
717 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000718}
719
720
edisonn@google.com571c70b2013-07-10 17:09:50 +0000721// TODO(edisonn): make sure the pointer is unique
722std::set<const SkPdfObject*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000723
724class CheckRecursiveRendering {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000725 const SkPdfObject* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000726public:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000727 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000728 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000729 }
730
731 ~CheckRecursiveRendering() {
732 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000733 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000734 }
735
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000736 static bool IsInRendering(const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000737 return gInRendering.find(obj) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000738 }
739};
740
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000741static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000742 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000743 // Oops, corrupt PDF!
744 return kIgnoreError_PdfResult;
745 }
746
edisonn@google.com571c70b2013-07-10 17:09:50 +0000747 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000748
edisonn@google.com571c70b2013-07-10 17:09:50 +0000749 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000750 {
751 case kImageDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000752 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000753 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000754 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000755 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
756 //return doXObject_PS(skxobj.asPS());
edisonn@google.com571c70b2013-07-10 17:09:50 +0000757 default:
758 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000759 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000760}
761
762PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
763 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
764 canvas->save();
765 return kOK_PdfResult;
766}
767
768PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
769 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
770 pdfContext->fStateStack.pop();
771 canvas->restore();
772 return kOK_PdfResult;
773}
774
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000775static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000776 double array[6];
777 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000778 array[5 - i] = pdfContext->fObjectStack.top()->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000779 pdfContext->fObjectStack.pop();
780 }
781
782 // a b
783 // c d
784 // e f
785
786 // 0 1
787 // 2 3
788 // 4 5
789
790 // sx ky
791 // kx sy
792 // tx ty
793 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
794
795 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
796
797#ifdef PDF_TRACE
798 printf("cm ");
799 for (int i = 0 ; i < 6 ; i++) {
800 printf("%f ", array[i]);
801 }
802 printf("\n");
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000803 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000804#endif
805
806 return kOK_PdfResult;
807}
808
809//leading TL Set the text leading, Tl
810//, to leading, which is a number expressed in unscaled text
811//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000812static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000813 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000814
815 pdfContext->fGraphicsState.fTextLeading = ty;
816
817 return kOK_PdfResult;
818}
819
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000820static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000821 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
822 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000823
824 double array[6] = {1, 0, 0, 1, tx, ty};
825 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
826
827 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
828 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
829
830 return kPartial_PdfResult;
831}
832
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000833static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000834 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
835 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000836
edisonn@google.com571c70b2013-07-10 17:09:50 +0000837 // TODO(edisonn): Create factory methods or constructors so native is hidden
838 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000839 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000840
841 PdfOp_TL(pdfContext, canvas, looper);
842
edisonn@google.com571c70b2013-07-10 17:09:50 +0000843 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000844 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000845
edisonn@google.com571c70b2013-07-10 17:09:50 +0000846 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000847 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000848
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000849 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
850
851 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
852 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000853
854 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000855}
856
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000857static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000858 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
859 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
860 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
861 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
862 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
863 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000864
865 double array[6];
866 array[0] = a;
867 array[1] = b;
868 array[2] = c;
869 array[3] = d;
870 array[4] = e;
871 array[5] = f;
872
873 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
874 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
875
876 // TODO(edisonn): Text positioning.
877 pdfContext->fGraphicsState.fMatrixTm = matrix;
878 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
879
880 return kPartial_PdfResult;
881}
882
883//— T* Move to the start of the next line. This operator has the same effect as the code
884//0 Tl Td
885//where Tl is the current leading parameter in the text state
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000886static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000887 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
888 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000889
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000890 pdfContext->fObjectStack.push(zero);
891 pdfContext->fObjectStack.push(tl);
892
893 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
894
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000895 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000896}
897
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000898static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000899 if (pdfContext->fGraphicsState.fPathClosed) {
900 pdfContext->fGraphicsState.fPath.reset();
901 pdfContext->fGraphicsState.fPathClosed = false;
902 }
903
edisonn@google.com571c70b2013-07-10 17:09:50 +0000904 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
905 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000906
907 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
908 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
909
910 return kOK_PdfResult;
911}
912
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000913static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000914 if (pdfContext->fGraphicsState.fPathClosed) {
915 pdfContext->fGraphicsState.fPath.reset();
916 pdfContext->fGraphicsState.fPathClosed = false;
917 }
918
edisonn@google.com571c70b2013-07-10 17:09:50 +0000919 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
920 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000921
922 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
923 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
924
925 return kOK_PdfResult;
926}
927
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000928static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000929 if (pdfContext->fGraphicsState.fPathClosed) {
930 pdfContext->fGraphicsState.fPath.reset();
931 pdfContext->fGraphicsState.fPathClosed = false;
932 }
933
edisonn@google.com571c70b2013-07-10 17:09:50 +0000934 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
935 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
936 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
937 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
938 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
939 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000940
941 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
942 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
943 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
944
945 pdfContext->fGraphicsState.fCurPosX = x3;
946 pdfContext->fGraphicsState.fCurPosY = y3;
947
948 return kOK_PdfResult;
949}
950
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000951static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000952 if (pdfContext->fGraphicsState.fPathClosed) {
953 pdfContext->fGraphicsState.fPath.reset();
954 pdfContext->fGraphicsState.fPathClosed = false;
955 }
956
edisonn@google.com571c70b2013-07-10 17:09:50 +0000957 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
958 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
959 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
960 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000961 double y1 = pdfContext->fGraphicsState.fCurPosY;
962 double x1 = pdfContext->fGraphicsState.fCurPosX;
963
964 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
965 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
966 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
967
968 pdfContext->fGraphicsState.fCurPosX = x3;
969 pdfContext->fGraphicsState.fCurPosY = y3;
970
971 return kOK_PdfResult;
972}
973
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000974static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000975 if (pdfContext->fGraphicsState.fPathClosed) {
976 pdfContext->fGraphicsState.fPath.reset();
977 pdfContext->fGraphicsState.fPathClosed = false;
978 }
979
edisonn@google.com571c70b2013-07-10 17:09:50 +0000980 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
981 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000982 double y2 = pdfContext->fGraphicsState.fCurPosY;
983 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000984 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
985 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000986
987 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
988 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
989 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
990
991 pdfContext->fGraphicsState.fCurPosX = x3;
992 pdfContext->fGraphicsState.fCurPosY = y3;
993
994 return kOK_PdfResult;
995}
996
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000997static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000998 if (pdfContext->fGraphicsState.fPathClosed) {
999 pdfContext->fGraphicsState.fPath.reset();
1000 pdfContext->fGraphicsState.fPathClosed = false;
1001 }
1002
edisonn@google.com571c70b2013-07-10 17:09:50 +00001003 double height = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1004 double width = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1005 double y = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1006 double x = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001007
1008 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1009 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1010
1011 pdfContext->fGraphicsState.fCurPosX = x;
1012 pdfContext->fGraphicsState.fCurPosY = y + height;
1013
1014 return kOK_PdfResult;
1015}
1016
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001017static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001018 pdfContext->fGraphicsState.fPath.close();
1019 return kOK_PdfResult;
1020}
1021
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001022static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001023 SkPath path = pdfContext->fGraphicsState.fPath;
1024
1025 if (close) {
1026 path.close();
1027 }
1028
1029 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1030
1031 SkPaint paint;
1032
1033 SkPoint line[2];
1034 if (fill && !stroke && path.isLine(line)) {
1035 paint.setStyle(SkPaint::kStroke_Style);
1036
1037 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1038 paint.setStrokeWidth(SkDoubleToScalar(0));
1039
1040 canvas->drawPath(path, paint);
1041 } else {
1042 if (fill) {
1043 paint.setStyle(SkPaint::kFill_Style);
1044 if (evenOdd) {
1045 path.setFillType(SkPath::kEvenOdd_FillType);
1046 }
1047
1048 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1049
1050 canvas->drawPath(path, paint);
1051 }
1052
1053 if (stroke) {
1054 paint.setStyle(SkPaint::kStroke_Style);
1055
1056 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1057
1058 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1059 canvas->drawPath(path, paint);
1060 }
1061 }
1062
1063 pdfContext->fGraphicsState.fPath.reset();
1064 // todo zoom ... other stuff ?
1065
1066 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1067#ifndef PDF_DEBUG_NO_CLIPING
1068 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1069#endif
1070 }
1071
1072 //pdfContext->fGraphicsState.fClipPath.reset();
1073 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1074
1075 return kPartial_PdfResult;
1076
1077}
1078
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001079static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001080 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1081}
1082
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001083static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001084 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1085}
1086
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001087static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001088 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1089}
1090
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001091static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001092 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1093}
1094
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001095static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001096 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1097}
1098
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001099static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001100 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1101}
1102
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001103static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001104 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1105}
1106
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001107static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001108 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1109}
1110
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001111static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001112 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1113}
1114
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001115static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001116 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1117 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1118#ifndef PDF_DEBUG_NO_CLIPING
1119 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1120#endif
1121 }
1122
1123 //pdfContext->fGraphicsState.fClipPath.reset();
1124 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1125
1126 pdfContext->fGraphicsState.fPathClosed = true;
1127
1128 return kOK_PdfResult;
1129}
1130
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001131static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001132 pdfContext->fGraphicsState.fTextBlock = true;
1133 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1134 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1135
1136 return kPartial_PdfResult;
1137}
1138
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001139static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001140 if (!pdfContext->fGraphicsState.fTextBlock) {
1141 return kIgnoreError_PdfResult;
1142 }
1143 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1144 return kPartial_PdfResult;
1145}
1146
1147//font size Tf Set the text font, Tf
1148//, to font and the text font size, Tfs, to size. font is the name of a
1149//font resource in the Fontsubdictionary of the current resource dictionary; size is
1150//a number representing a scale factor. There is no initial value for either font or
1151//size; they must be specified explicitly using Tf before any text is shown.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001152static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001153 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1154 const char* fontName = pdfContext->fObjectStack.top()->nameValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001155
1156#ifdef PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +00001157 printf("font name: %s\n", fontName);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001158#endif
1159
edisonn@google.com571c70b2013-07-10 17:09:50 +00001160 if (pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1161 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1162 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1163 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1164 // TODO(edisonn): try to recover and draw it any way?
1165 return kIgnoreError_PdfResult;
1166 }
1167 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1168
1169 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1170
1171 if (skfont) {
1172 pdfContext->fGraphicsState.fSkFont = skfont;
1173 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001174 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001175 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001176}
1177
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001178static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001179 if (!pdfContext->fGraphicsState.fTextBlock) {
1180 // TODO(edisonn): try to recover and draw it any way?
1181 return kIgnoreError_PdfResult;
1182 }
1183
1184 PdfResult ret = DrawText(pdfContext,
1185 pdfContext->fObjectStack.top(),
1186 canvas);
1187 pdfContext->fObjectStack.pop();
1188
1189 return ret;
1190}
1191
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001192static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001193 if (!pdfContext->fGraphicsState.fTextBlock) {
1194 // TODO(edisonn): try to recover and draw it any way?
1195 return kIgnoreError_PdfResult;
1196 }
1197
1198 PdfOp_T_star(pdfContext, canvas, looper);
1199 // Do not pop, and push, just transfer the param to Tj
1200 return PdfOp_Tj(pdfContext, canvas, looper);
1201}
1202
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001203static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001204 if (!pdfContext->fGraphicsState.fTextBlock) {
1205 // TODO(edisonn): try to recover and draw it any way?
1206 return kIgnoreError_PdfResult;
1207 }
1208
1209 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1210 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1211 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1212
1213 pdfContext->fObjectStack.push(aw);
1214 PdfOp_Tw(pdfContext, canvas, looper);
1215
1216 pdfContext->fObjectStack.push(ac);
1217 PdfOp_Tc(pdfContext, canvas, looper);
1218
1219 pdfContext->fObjectStack.push(str);
1220 PdfOp_quote(pdfContext, canvas, looper);
1221
1222 return kPartial_PdfResult;
1223}
1224
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001225static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001226 if (!pdfContext->fGraphicsState.fTextBlock) {
1227 // TODO(edisonn): try to recover and draw it any way?
1228 return kIgnoreError_PdfResult;
1229 }
1230
edisonn@google.com571c70b2013-07-10 17:09:50 +00001231 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001232 pdfContext->fObjectStack.pop();
1233
edisonn@google.com571c70b2013-07-10 17:09:50 +00001234 if (!array->isArray()) {
1235 return kIgnoreError_PdfResult;
1236 }
1237
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001238 for( int i=0; i<static_cast<int>(array->size()); i++ )
1239 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001240 if( (*array)[i]->isAnyString()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001241 SkPdfObject* obj = (*array)[i];
1242 DrawText(pdfContext,
1243 obj,
1244 canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001245 } else if ((*array)[i]->isNumber()) {
1246 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001247 SkMatrix matrix;
1248 matrix.setAll(SkDoubleToScalar(1),
1249 SkDoubleToScalar(0),
1250 // TODO(edisonn): use writing mode, vertical/horizontal.
1251 SkDoubleToScalar(-dx), // amount is substracted!!!
1252 SkDoubleToScalar(0),
1253 SkDoubleToScalar(1),
1254 SkDoubleToScalar(0),
1255 SkDoubleToScalar(0),
1256 SkDoubleToScalar(0),
1257 SkDoubleToScalar(1));
1258
1259 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1260 }
1261 }
1262 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1263}
1264
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001265static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001266 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->nameValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001267 return kOK_PdfResult;
1268}
1269
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001270static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001271 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1272}
1273
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001274static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001275 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1276}
1277
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001278static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001279 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001280// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001281
1282 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1283
1284 bool doubles = true;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001285 if (strcmp(colorOperator->fColorSpace, "Indexed") == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001286 doubles = false;
1287 }
1288
1289#ifdef PDF_TRACE
edisonn@google.com571c70b2013-07-10 17:09:50 +00001290 printf("color space = %s, N = %i\n", colorOperator->fColorSpace, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001291#endif
1292
1293 for (int i = n - 1; i >= 0 ; i--) {
1294 if (doubles) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001295 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1296// } else {
1297// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001298 }
1299 }
1300
1301 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001302 // TODO(edisonn): do possible field values to enum at parsing time!
1303 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
1304 if (strcmp(colorOperator->fColorSpace, "DeviceRGB") == 0 || strcmp(colorOperator->fColorSpace, "RGB") == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001305 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
1306 }
1307 return kPartial_PdfResult;
1308}
1309
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001310static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001311 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1312}
1313
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001314static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001315 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1316}
1317
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001318static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001319 //SkPdfString* name;
1320 if (pdfContext->fObjectStack.top()->isName()) {
1321 // TODO(edisonn): get name, pass it
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001322 pdfContext->fObjectStack.pop();
1323 }
1324
1325 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1326 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1327
1328 return kPartial_PdfResult;
1329}
1330
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001331static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001332 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1333}
1334
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001335static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001336 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1337}
1338
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001339static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001340 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001341 return kNYI_PdfResult;
1342}
1343
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001344static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001345 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1346}
1347
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001348static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001349 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1350}
1351
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001352static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001353 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1354 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1355 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001356
1357 colorOperator->fColorSpace = "DeviceRGB";
1358 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
1359 return kOK_PdfResult;
1360}
1361
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001362static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001363 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1364}
1365
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001366static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001367 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1368}
1369
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001370static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001371 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001372 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1373 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1374 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1375 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001376
1377 colorOperator->fColorSpace = "DeviceCMYK";
1378 // TODO(edisonn): Set color.
1379 return kNYI_PdfResult;
1380}
1381
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001382static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001383 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1384}
1385
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001386static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001387 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1388}
1389
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001390static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001391 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1392 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1393
1394 return kOK_PdfResult;
1395}
1396
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001397static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001398 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1399
1400#ifdef PDF_TRACE
1401 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
1402 printf("CLIP IS RECT\n");
1403 }
1404#endif
1405
1406 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
1407 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1408 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1409
1410 return kPartial_PdfResult;
1411}
1412
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001413static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001414 *looper = new PdfCompatibilitySectionLooper();
1415 return kOK_PdfResult;
1416}
1417
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001418static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001419#ifdef ASSERT_BAD_PDF_OPS
1420 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1421 // have the assert when testing good pdfs.
1422#endif
1423 return kIgnoreError_PdfResult;
1424}
1425
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001426static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001427 *looper = new PdfInlineImageLooper();
1428 return kOK_PdfResult;
1429}
1430
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001431static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001432#ifdef ASSERT_BAD_PDF_OPS
1433 SkASSERT(false); // must be processed in inline image looper, but let's
1434 // have the assert when testing good pdfs.
1435#endif
1436 return kIgnoreError_PdfResult;
1437}
1438
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001439static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001440#ifdef ASSERT_BAD_PDF_OPS
1441 SkASSERT(false); // must be processed in inline image looper, but let's
1442 // have the assert when testing good pdfs.
1443#endif
1444 return kIgnoreError_PdfResult;
1445}
1446
1447//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001448static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001449 double lineWidth = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001450 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1451
1452 return kOK_PdfResult;
1453}
1454
1455//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 +00001456static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001457 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001458 //double lineCap = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001459
1460 return kNYI_PdfResult;
1461}
1462
1463//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 +00001464static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001465 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001466 //double lineJoin = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001467
1468 return kNYI_PdfResult;
1469}
1470
1471//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001472static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001473 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001474 //double miterLimit = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001475
1476 return kNYI_PdfResult;
1477}
1478
1479//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1480//page 155).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001481static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001482 pdfContext->fObjectStack.pop();
1483 pdfContext->fObjectStack.pop();
1484
1485 return kNYI_PdfResult;
1486}
1487
1488//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 +00001489static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001490 pdfContext->fObjectStack.pop();
1491
1492 return kNYI_PdfResult;
1493}
1494
1495//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1496//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1497//fies the output device’s default flatness tolerance.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001498static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001499 pdfContext->fObjectStack.pop();
1500
1501 return kNYI_PdfResult;
1502}
1503
1504//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
1505//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 +00001506static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001507 const char* name = pdfContext->fObjectStack.top()->nameValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001508
1509#ifdef PDF_TRACE
1510 std::string str;
1511#endif
1512
1513 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001514 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001515
1516 if (extGStateDictionary == NULL) {
1517#ifdef PDF_TRACE
1518 printf("ExtGState is NULL!\n");
1519#endif
1520 return kIgnoreError_PdfResult;
1521 }
1522
edisonn@google.com571c70b2013-07-10 17:09:50 +00001523 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001524
edisonn@google.com571c70b2013-07-10 17:09:50 +00001525 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
1526 return kIgnoreError_PdfResult;
1527 }
1528 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001529
1530 // TODO(edisonn): now load all those properties in graphic state.
1531 if (gs == NULL) {
1532 return kIgnoreError_PdfResult;
1533 }
1534
1535 if (gs->has_CA()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001536 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001537 }
1538
1539 if (gs->has_ca()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001540 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001541 }
1542
1543 if (gs->has_LW()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001544 pdfContext->fGraphicsState.fLineWidth = gs->LW(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001545 }
1546
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001547 return kNYI_PdfResult;
1548}
1549
1550//charSpace Tc Set the character spacing, Tc
1551//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
1552//Initial value: 0.
1553PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001554 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001555 pdfContext->fGraphicsState.fCharSpace = charSpace;
1556
1557 return kOK_PdfResult;
1558}
1559
1560//wordSpace Tw Set the word spacing, T
1561//w
1562//, to wordSpace, which is a number expressed in unscaled
1563//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
1564//value: 0.
1565PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001566 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001567 pdfContext->fGraphicsState.fWordSpace = wordSpace;
1568
1569 return kOK_PdfResult;
1570}
1571
1572//scale Tz Set the horizontal scaling, Th
1573//, to (scale ˜ 100). scale is a number specifying the
1574//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001575static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001576 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001577
1578 return kNYI_PdfResult;
1579}
1580
1581//render Tr Set the text rendering mode, T
1582//mode, to render, which is an integer. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001583static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001584 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001585
1586 return kNYI_PdfResult;
1587}
1588//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
1589//units. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001590static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001591 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001592
1593 return kNYI_PdfResult;
1594}
1595
1596//wx wy d0
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001597static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001598 pdfContext->fObjectStack.pop();
1599 pdfContext->fObjectStack.pop();
1600
1601 return kNYI_PdfResult;
1602}
1603
1604//wx wy llx lly urx ury d1
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001605static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001606 pdfContext->fObjectStack.pop();
1607 pdfContext->fObjectStack.pop();
1608 pdfContext->fObjectStack.pop();
1609 pdfContext->fObjectStack.pop();
1610 pdfContext->fObjectStack.pop();
1611 pdfContext->fObjectStack.pop();
1612
1613 return kNYI_PdfResult;
1614}
1615
1616//name sh
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001617static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001618 pdfContext->fObjectStack.pop();
1619
1620 return kNYI_PdfResult;
1621}
1622
1623//name Do
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001624static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001625 const char* name = pdfContext->fObjectStack.top()->nameValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001626
edisonn@google.com571c70b2013-07-10 17:09:50 +00001627 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001628
1629 if (xObject == NULL) {
1630#ifdef PDF_TRACE
1631 printf("XObject is NULL!\n");
1632#endif
1633 return kIgnoreError_PdfResult;
1634 }
1635
edisonn@google.com571c70b2013-07-10 17:09:50 +00001636 SkPdfObject* value = xObject->get(name);
1637 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001638
1639#ifdef PDF_TRACE
1640// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001641// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001642#endif
1643
edisonn@google.com571c70b2013-07-10 17:09:50 +00001644 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001645}
1646
1647//tag MP Designate a marked-content point. tag is a name object indicating the role or
1648//significance of the point.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001649static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001650 pdfContext->fObjectStack.pop();
1651
1652 return kNYI_PdfResult;
1653}
1654
1655//tag properties DP Designate a marked-content point with an associated property list. tag is a
1656//name object indicating the role or significance of the point; properties is
1657//either an inline dictionary containing the property list or a name object
1658//associated with it in the Properties subdictionary of the current resource
1659//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001660static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001661 pdfContext->fObjectStack.pop();
1662 pdfContext->fObjectStack.pop();
1663
1664 return kNYI_PdfResult;
1665}
1666
1667//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
1668//tag is a name object indicating the role or significance of the sequence.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001669static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001670 pdfContext->fObjectStack.pop();
1671
1672 return kNYI_PdfResult;
1673}
1674
1675//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
1676//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
1677//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 +00001678static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001679 pdfContext->fObjectStack.pop();
1680 pdfContext->fObjectStack.pop();
1681
1682 return kNYI_PdfResult;
1683}
1684
1685//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001686static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001687 return kNYI_PdfResult;
1688}
1689
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001690static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001691 static bool gInitialized = false;
1692 if (gInitialized) {
1693 return;
1694 }
1695
1696 gPdfOps["q"] = PdfOp_q;
1697 gPdfOps["Q"] = PdfOp_Q;
1698 gPdfOps["cm"] = PdfOp_cm;
1699
1700 gPdfOps["TD"] = PdfOp_TD;
1701 gPdfOps["Td"] = PdfOp_Td;
1702 gPdfOps["Tm"] = PdfOp_Tm;
1703 gPdfOps["T*"] = PdfOp_T_star;
1704
1705 gPdfOps["m"] = PdfOp_m;
1706 gPdfOps["l"] = PdfOp_l;
1707 gPdfOps["c"] = PdfOp_c;
1708 gPdfOps["v"] = PdfOp_v;
1709 gPdfOps["y"] = PdfOp_y;
1710 gPdfOps["h"] = PdfOp_h;
1711 gPdfOps["re"] = PdfOp_re;
1712
1713 gPdfOps["S"] = PdfOp_S;
1714 gPdfOps["s"] = PdfOp_s;
1715 gPdfOps["f"] = PdfOp_f;
1716 gPdfOps["F"] = PdfOp_F;
1717 gPdfOps["f*"] = PdfOp_f_star;
1718 gPdfOps["B"] = PdfOp_B;
1719 gPdfOps["B*"] = PdfOp_B_star;
1720 gPdfOps["b"] = PdfOp_b;
1721 gPdfOps["b*"] = PdfOp_b_star;
1722 gPdfOps["n"] = PdfOp_n;
1723
1724 gPdfOps["BT"] = PdfOp_BT;
1725 gPdfOps["ET"] = PdfOp_ET;
1726
1727 gPdfOps["Tj"] = PdfOp_Tj;
1728 gPdfOps["'"] = PdfOp_quote;
1729 gPdfOps["\""] = PdfOp_doublequote;
1730 gPdfOps["TJ"] = PdfOp_TJ;
1731
1732 gPdfOps["CS"] = PdfOp_CS;
1733 gPdfOps["cs"] = PdfOp_cs;
1734 gPdfOps["SC"] = PdfOp_SC;
1735 gPdfOps["SCN"] = PdfOp_SCN;
1736 gPdfOps["sc"] = PdfOp_sc;
1737 gPdfOps["scn"] = PdfOp_scn;
1738 gPdfOps["G"] = PdfOp_G;
1739 gPdfOps["g"] = PdfOp_g;
1740 gPdfOps["RG"] = PdfOp_RG;
1741 gPdfOps["rg"] = PdfOp_rg;
1742 gPdfOps["K"] = PdfOp_K;
1743 gPdfOps["k"] = PdfOp_k;
1744
1745 gPdfOps["W"] = PdfOp_W;
1746 gPdfOps["W*"] = PdfOp_W_star;
1747
1748 gPdfOps["BX"] = PdfOp_BX;
1749 gPdfOps["EX"] = PdfOp_EX;
1750
1751 gPdfOps["BI"] = PdfOp_BI;
1752 gPdfOps["ID"] = PdfOp_ID;
1753 gPdfOps["EI"] = PdfOp_EI;
1754
1755 gPdfOps["w"] = PdfOp_w;
1756 gPdfOps["J"] = PdfOp_J;
1757 gPdfOps["j"] = PdfOp_j;
1758 gPdfOps["M"] = PdfOp_M;
1759 gPdfOps["d"] = PdfOp_d;
1760 gPdfOps["ri"] = PdfOp_ri;
1761 gPdfOps["i"] = PdfOp_i;
1762 gPdfOps["gs"] = PdfOp_gs;
1763
1764 gPdfOps["Tc"] = PdfOp_Tc;
1765 gPdfOps["Tw"] = PdfOp_Tw;
1766 gPdfOps["Tz"] = PdfOp_Tz;
1767 gPdfOps["TL"] = PdfOp_TL;
1768 gPdfOps["Tf"] = PdfOp_Tf;
1769 gPdfOps["Tr"] = PdfOp_Tr;
1770 gPdfOps["Ts"] = PdfOp_Ts;
1771
1772 gPdfOps["d0"] = PdfOp_d0;
1773 gPdfOps["d1"] = PdfOp_d1;
1774
1775 gPdfOps["sh"] = PdfOp_sh;
1776
1777 gPdfOps["Do"] = PdfOp_Do;
1778
1779 gPdfOps["MP"] = PdfOp_MP;
1780 gPdfOps["DP"] = PdfOp_DP;
1781 gPdfOps["BMC"] = PdfOp_BMC;
1782 gPdfOps["BDC"] = PdfOp_BDC;
1783 gPdfOps["EMC"] = PdfOp_EMC;
1784
1785 gInitialized = true;
1786}
1787
1788class InitPdfOps {
1789public:
1790 InitPdfOps() {
1791 initPdfOperatorRenderes();
1792 }
1793};
1794
1795InitPdfOps gInitPdfOps;
1796
1797void reportPdfRenderStats() {
1798 std::map<std::string, int>::iterator iter;
1799
1800 for (int i = 0 ; i < kCount_PdfResult; i++) {
1801 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++iter) {
1802 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_str(), iter->second);
1803 }
1804 }
1805}
1806
1807PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001808 char keyword[256];
1809
1810 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001811 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001812 strncpy(keyword, token.fKeyword, token.fKeywordLength);
1813 keyword[token.fKeywordLength] = '\0';
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001814 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com571c70b2013-07-10 17:09:50 +00001815 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[keyword];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001816 if (pdfOperatorRenderer) {
1817 // caller, main work is done by pdfOperatorRenderer(...)
1818 PdfTokenLooper* childLooper = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001819 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)][keyword]++;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001820
1821 if (childLooper) {
1822 childLooper->setUp(this);
1823 childLooper->loop();
1824 delete childLooper;
1825 }
1826 } else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001827 gRenderStats[kUnsupported_PdfResult][keyword]++;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001828 }
1829 }
1830 else if (token.fType == kObject_TokenType)
1831 {
1832 fPdfContext->fObjectStack.push( token.fObject );
1833 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001834 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001835 // TODO(edisonn): deine or use assert not reached
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001836 return kIgnoreError_PdfResult;
1837 }
1838 return kOK_PdfResult;
1839}
1840
1841void PdfMainLooper::loop() {
1842 PdfToken token;
1843 while (readToken(fTokenizer, &token)) {
1844 consumeToken(token);
1845 }
1846}
1847
1848PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00001849 SkASSERT(false);
1850 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001851}
1852
1853void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00001854 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001855}
1856
1857PdfResult PdfInlineImageLooper::done() {
1858
1859 // TODO(edisonn): long to short names
1860 // TODO(edisonn): set properties in a map
1861 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
1862 // the stream.
1863
1864 SkBitmap bitmap;
1865 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
1866
1867 // TODO(edisonn): matrix use.
1868 // Draw dummy red square, to show the prezence of the inline image.
1869 fCanvas->drawBitmap(bitmap,
1870 SkDoubleToScalar(0),
1871 SkDoubleToScalar(0),
1872 NULL);
1873 return kNYI_PdfResult;
1874}
1875
1876PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
1877 return fParent->consumeToken(token);
1878}
1879
1880void PdfCompatibilitySectionLooper::loop() {
1881 // TODO(edisonn): save stacks position, or create a new stack?
1882 // TODO(edisonn): what happens if we pop out more variables then when we started?
1883 // restore them? fail? We could create a new operands stack for every new BX/EX section,
1884 // pop-ing too much will not affect outside the section.
1885 PdfToken token;
1886 while (readToken(fTokenizer, &token)) {
1887 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
1888 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
1889 looper->setUp(this);
1890 looper->loop();
1891 delete looper;
1892 } else {
1893 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
1894 fParent->consumeToken(token);
1895 }
1896 }
1897 // TODO(edisonn): restore stack.
1898}
1899
1900// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
1901// TODO(edisonn): Add API for Forms viewing and editing
1902// e.g. SkBitmap getPage(int page);
1903// int formsCount();
1904// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
1905// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
1906// if we load the first page, and we zoom to fit to screen horizontally, then load only those
1907// resources needed, so the preview is fast.
1908// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
1909// references automatically.
1910
edisonn@google.com222382b2013-07-10 22:33:10 +00001911PdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001912
edisonn@google.com444e25a2013-07-11 15:20:50 +00001913bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00001914 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001915 return false;
1916 }
1917
edisonn@google.com222382b2013-07-10 22:33:10 +00001918 if (page < 0 || page >= pages()) {
1919 return false;
1920 }
1921
1922 SkPdfNativeTokenizer* tokenizer = fPdfDoc->tokenizerOfPage(page);
1923
1924 PdfContext pdfContext(fPdfDoc);
1925 pdfContext.fOriginalMatrix = SkMatrix::I();
1926 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
1927
1928 gPdfContext = &pdfContext;
1929
1930 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00001931 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00001932 SkScalar w = dst.width();
1933 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00001934
edisonn@google.com444e25a2013-07-11 15:20:50 +00001935 SkScalar wp = fPdfDoc->MediaBox(page).width();
1936 SkScalar hp = fPdfDoc->MediaBox(page).height();
1937
1938 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 +00001939// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
1940
1941 // TODO(edisonn): add flag for this app to create sourunding buffer zone
1942 // TODO(edisonn): add flagg for no clipping.
1943 // Use larger image to make sure we do not draw anything outside of page
1944 // could be used in tests.
1945
1946#ifdef PDF_DEBUG_3X
1947 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)};
1948#else
1949 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
1950#endif
1951 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
1952 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
1953
1954 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
1955 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
1956
1957 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
1958 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
1959
1960 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
1961 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
1962
1963
1964 pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
1965 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
1966 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
1967
edisonn@google.com222382b2013-07-10 22:33:10 +00001968#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00001969 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00001970#endif
1971
edisonn@google.com444e25a2013-07-11 15:20:50 +00001972 canvas->setMatrix(pdfContext.fOriginalMatrix);
1973
edisonn@google.com222382b2013-07-10 22:33:10 +00001974// erase with red before?
1975// SkPaint paint;
1976// paint.setColor(SK_ColorRED);
1977// canvas->drawRect(rect, paint);
1978
1979 PdfMainLooper looper(NULL, tokenizer, &pdfContext, canvas);
1980 looper.loop();
1981
1982 delete tokenizer;
1983
1984 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001985 return true;
1986}
edisonn@google.com222382b2013-07-10 22:33:10 +00001987
1988bool SkPdfRenderer::load(const SkString inputFileName) {
1989 unload();
1990
1991 // TODO(edisonn): create static function that could return NULL if there are errors
1992 fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00001993 if (fPdfDoc->pages() == 0) {
1994 delete fPdfDoc;
1995 fPdfDoc = NULL;
1996 }
edisonn@google.com222382b2013-07-10 22:33:10 +00001997
1998 return fPdfDoc != NULL;
1999}
2000
2001int SkPdfRenderer::pages() const {
2002 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2003}
2004
2005void SkPdfRenderer::unload() {
2006 delete fPdfDoc;
2007 fPdfDoc = NULL;
2008}
2009
2010SkRect SkPdfRenderer::MediaBox(int page) const {
2011 SkASSERT(fPdfDoc);
2012 return fPdfDoc->MediaBox(page);
2013}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002014
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002015size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002016 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2017}