blob: c51c2390f85061372e87ea2743781eec38e83f7f [file] [log] [blame]
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkCanvas.h"
9#include "SkDevice.h"
10#include "SkForceLinking.h"
11#include "SkGraphics.h"
12#include "SkImageDecoder.h"
13#include "SkImageEncoder.h"
14#include "SkOSFile.h"
15#include "SkPicture.h"
16#include "SkStream.h"
17#include "SkTypeface.h"
18#include "SkTArray.h"
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000019#include "SkTDict.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000020
edisonn@google.com15b11182013-07-11 14:43:15 +000021#include "SkPdfBasics.h"
22#include "SkPdfNativeTokenizer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000023#include <cstdio>
24#include <stack>
edisonn@google.com571c70b2013-07-10 17:09:50 +000025#include <set>
edisonn@google.com131d4ee2013-06-26 17:48:12 +000026
edisonn@google.com15b11182013-07-11 14:43:15 +000027extern "C" PdfContext* gPdfContext;
28extern "C" SkBitmap* gDumpBitmap;
29extern "C" SkCanvas* gDumpCanvas;
30
edisonn@google.com131d4ee2013-06-26 17:48:12 +000031__SK_FORCE_IMAGE_DECODER_LINKING;
32
33// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
34// keep for each object pos in file
35// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
36
37// TODO(edisonn): security - validate all the user input, all pdf!
38
edisonn@google.com6e49c342013-06-27 20:03:43 +000039// TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor changes in text positioning and font
40// this way, we look more at other features and layout in diffs
edisonn@google.com131d4ee2013-06-26 17:48:12 +000041
edisonn@google.com3aac1f92013-07-02 22:42:53 +000042// TODO(edisonn): move trace dump in the get functions, and mapper ones too so it ghappens automatically
43/*
44#ifdef PDF_TRACE
45 std::string str;
edisonn@google.com571c70b2013-07-10 17:09:50 +000046 pdfContext->fGraphicsState.fResources->native()->ToString(str);
edisonn@google.com3aac1f92013-07-02 22:42:53 +000047 printf("Print Tf Resources: %s\n", str.c_str());
48#endif
49 */
50
edisonn@google.com131d4ee2013-06-26 17:48:12 +000051#include "SkPdfHeaders_autogen.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +000052#include "SkPdfMapper_autogen.h"
edisonn@google.com222382b2013-07-10 22:33:10 +000053#include "SkPdfRenderer.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000054
55#include "SkPdfBasics.h"
56#include "SkPdfUtils.h"
57
58#include "SkPdfFont.h"
59
edisonn@google.com131d4ee2013-06-26 17:48:12 +000060/*
61 * TODO(edisonn):
62 * - all font types and all ppdf font features
63 * - word spacing
64 * - load font for baidu.pdf
65 * - load font for youtube.pdf
66 * - parser for pdf from the definition already available in pdfspec_autogen.py
67 * - all docs from ~/work
edisonn@google.com571c70b2013-07-10 17:09:50 +000068 * - encapsulate native in the pdf api so the skpdf does not know anything about native ... in progress
edisonn@google.com131d4ee2013-06-26 17:48:12 +000069 * - load gs/ especially smask and already known prop (skp) ... in progress
70 * - wrapper on classes for customizations? e.g.
71 * SkPdfPageObjectVanila - has only the basic loaders/getters
72 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
73 * need to find a nice object model for all this with constructors and factories
74 * - deal with inheritable automatically ?
75 * - deal with specific type in spec directly, add all dictionary types to known types
76*/
77
78using namespace std;
edisonn@google.com131d4ee2013-06-26 17:48:12 +000079
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000080NotOwnedString strings_DeviceRGB;
81NotOwnedString strings_DeviceCMYK;
edisonn@google.com222382b2013-07-10 22:33:10 +000082
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000083class StringsInit {
84public:
85 StringsInit() {
86 NotOwnedString::init(&strings_DeviceRGB, "DeviceRGB");
87 NotOwnedString::init(&strings_DeviceCMYK, "DeviceCMYK");
88 }
89};
90
91StringsInit gStringsInit;
edisonn@google.com222382b2013-07-10 22:33:10 +000092
93// TODO(edisonn): Document PdfTokenLooper and subclasses.
94class PdfTokenLooper {
95protected:
96 PdfTokenLooper* fParent;
97 SkPdfNativeTokenizer* fTokenizer;
98 PdfContext* fPdfContext;
99 SkCanvas* fCanvas;
100
101public:
102 PdfTokenLooper(PdfTokenLooper* parent,
103 SkPdfNativeTokenizer* tokenizer,
104 PdfContext* pdfContext,
105 SkCanvas* canvas)
106 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
107
108 virtual ~PdfTokenLooper() {}
109
110 virtual PdfResult consumeToken(PdfToken& token) = 0;
111 virtual void loop() = 0;
112
113 void setUp(PdfTokenLooper* parent) {
114 fParent = parent;
115 fTokenizer = parent->fTokenizer;
116 fPdfContext = parent->fPdfContext;
117 fCanvas = parent->fCanvas;
118 }
edisonn@google.com78b38b12013-07-15 18:20:58 +0000119
120 SkPdfNativeTokenizer* tokenizer() { return fTokenizer; }
edisonn@google.com222382b2013-07-10 22:33:10 +0000121};
122
123class PdfMainLooper : public PdfTokenLooper {
124public:
125 PdfMainLooper(PdfTokenLooper* parent,
126 SkPdfNativeTokenizer* tokenizer,
127 PdfContext* pdfContext,
128 SkCanvas* canvas)
129 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
130
131 virtual PdfResult consumeToken(PdfToken& token);
132 virtual void loop();
133};
134
135class PdfInlineImageLooper : public PdfTokenLooper {
136public:
137 PdfInlineImageLooper()
138 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
139
140 virtual PdfResult consumeToken(PdfToken& token);
141 virtual void loop();
142 PdfResult done();
143};
144
145class PdfCompatibilitySectionLooper : public PdfTokenLooper {
146public:
147 PdfCompatibilitySectionLooper()
148 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
149
150 virtual PdfResult consumeToken(PdfToken& token);
151 virtual void loop();
152};
153
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000154// Utilities
155static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
156 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
157
158 bitmap->allocPixels();
159 bitmap->eraseColor(color);
160}
161
162// TODO(edisonn): synonyms? DeviceRGB and RGB ...
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000163static int GetColorSpaceComponents(NotOwnedString& colorSpace) {
164 if (colorSpace.equals("DeviceCMYK")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000165 return 4;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000166 } else if (colorSpace.equals("DeviceGray") ||
167 colorSpace.equals("CalGray") ||
168 colorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000169 return 1;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000170 } else if (colorSpace.equals("DeviceRGB") ||
171 colorSpace.equals("CalRGB") ||
172 colorSpace.equals("Lab")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000173 return 3;
174 } else {
175 return 0;
176 }
177}
178
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000179SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000180 SkMatrix matrix;
181 matrix.setAll(SkDoubleToScalar(array[0]),
182 SkDoubleToScalar(array[2]),
183 SkDoubleToScalar(array[4]),
184 SkDoubleToScalar(array[1]),
185 SkDoubleToScalar(array[3]),
186 SkDoubleToScalar(array[5]),
187 SkDoubleToScalar(0),
188 SkDoubleToScalar(0),
189 SkDoubleToScalar(1));
190
191 return matrix;
192}
193
194SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
195 double array[6];
196
197 // TODO(edisonn): security issue, ret if size() != 6
198 for (int i = 0; i < 6; i++) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000199 const SkPdfObject* elem = pdfArray->operator [](i);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000200 if (elem == NULL || !elem->isNumber()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000201 return SkMatrix::I(); // TODO(edisonn): report issue
202 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000203 array[i] = elem->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000204 }
205
206 return SkMatrixFromPdfMatrix(array);
207}
208
edisonn@google.com222382b2013-07-10 22:33:10 +0000209
210extern "C" SkNativeParsedPDF* gDoc;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000211SkBitmap* gDumpBitmap = NULL;
212SkCanvas* gDumpCanvas = NULL;
213char gLastKeyword[100] = "";
214int gLastOpKeyword = -1;
215char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
216int gReadOp = 0;
217
218
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000219#ifdef PDF_TRACE_DIFF_IN_PNG
220static bool hasVisualEffect(const char* pdfOp) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000221 return true;
222 if (*pdfOp == '\0') return false;
223
224 char markedPdfOp[100] = ",";
225 strcat(markedPdfOp, pdfOp);
226 strcat(markedPdfOp, ",");
227
228 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
229}
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000230#endif // PDF_TRACE_DIFF_IN_PNG
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000231
edisonn@google.com222382b2013-07-10 22:33:10 +0000232
233
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000234// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000235static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000236 bool ret = fTokenizer->readToken(token);
237
238 gReadOp++;
239
240#ifdef PDF_TRACE_DIFF_IN_PNG
241 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
242 // the numbar and name of last operation, so the file name will reflect op that changed.
243 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
244 gDumpCanvas->flush();
245
246 SkBitmap bitmap;
247 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
248
249 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
250
251 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
252 SkCanvas canvas(device);
253
254 // draw context stuff here
255 SkPaint blueBorder;
256 blueBorder.setColor(SK_ColorBLUE);
257 blueBorder.setStyle(SkPaint::kStroke_Style);
258 blueBorder.setTextSize(SkDoubleToScalar(20));
259
260 SkString str;
261
262 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
263 if (clipStack) {
264 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
265 const SkClipStack::Element* elem;
266 double y = 0;
267 int total = 0;
268 while (elem = iter.next()) {
269 total++;
270 y += 30;
271
272 switch (elem->getType()) {
273 case SkClipStack::Element::kRect_Type:
274 canvas.drawRect(elem->getRect(), blueBorder);
275 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
276 break;
277 case SkClipStack::Element::kPath_Type:
278 canvas.drawPath(elem->getPath(), blueBorder);
279 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
280 break;
281 case SkClipStack::Element::kEmpty_Type:
282 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
283 break;
284 default:
285 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
286 break;
287 }
288 }
289
290 y += 30;
291 str.printf("Number of clips in stack: %i", total);
292 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
293 }
294
295 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
296 SkPath clipPath;
297 if (clipRegion.getBoundaryPath(&clipPath)) {
298 SkPaint redBorder;
299 redBorder.setColor(SK_ColorRED);
300 redBorder.setStyle(SkPaint::kStroke_Style);
301 canvas.drawPath(clipPath, redBorder);
302 }
303
304 canvas.flush();
305
306 SkString out;
307
308 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
309 // ... and other properties, to be able to debug th code easily
310
311 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
312 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
313 }
314
315 if (token->fType == kKeyword_TokenType) {
316 strcpy(gLastKeyword, token->fKeyword);
317 gLastOpKeyword = gReadOp;
318 } else {
319 strcpy(gLastKeyword, "");
320 }
321#endif
322
323 return ret;
324}
325
326
327
328typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
329
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000330SkTDict<PdfOperatorRenderer> gPdfOps(100);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000331
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000332
333template <typename T> class SkTDictWithDefaultConstructor : public SkTDict<T> {
334public:
335 SkTDictWithDefaultConstructor() : SkTDict<T>(10) {}
336};
337
338SkTDictWithDefaultConstructor<int> gRenderStats[kCount_PdfResult];
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000339
edisonn@google.com571c70b2013-07-10 17:09:50 +0000340const char* gRenderStatsNames[kCount_PdfResult] = {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000341 "Success",
342 "Partially implemented",
343 "Not yet implemented",
344 "Ignore Error",
345 "Error",
346 "Unsupported/Unknown"
347};
348
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000349static PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000350 const SkPdfObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000351 SkCanvas* canvas)
352{
353
354 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
355 if (skfont == NULL) {
356 skfont = SkPdfFont::Default();
357 }
358
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000359
edisonn@google.com571c70b2013-07-10 17:09:50 +0000360 if (_str == NULL || !_str->isAnyString()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000361 // TODO(edisonn): report warning
362 return kIgnoreError_PdfResult;
363 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000364 const SkPdfString* str = (const SkPdfString*)_str;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000365
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000366 SkUnencodedText binary(str);
367
368 SkDecodedText decoded;
369
370 if (skfont->encoding() == NULL) {
371 // TODO(edisonn): report warning
372 return kNYI_PdfResult;
373 }
374
375 skfont->encoding()->decodeText(binary, &decoded);
376
377 SkPaint paint;
378 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
379 // Or maybe just not call setTextSize at all?
380 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
381 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
382 }
383
384// if (fCurFont && fCurFont->GetFontScale() != 0) {
385// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
386// }
387
388 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
389
390 canvas->save();
391
392#if 1
393 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
394
395 SkPoint point1;
396 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
397
398 SkMatrix mirror;
399 mirror.setTranslate(0, -point1.y());
400 // TODO(edisonn): fix rotated text, and skewed too
401 mirror.postScale(SK_Scalar1, -SK_Scalar1);
402 // TODO(edisonn): post rotate, skew
403 mirror.postTranslate(0, point1.y());
404
405 matrix.postConcat(mirror);
406
407 canvas->setMatrix(matrix);
408
409 SkTraceMatrix(matrix, "mirrored");
410#endif
411
edisonn@google.com6e49c342013-06-27 20:03:43 +0000412 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000413 canvas->restore();
414
415 return kPartial_PdfResult;
416}
417
418// TODO(edisonn): create header files with declarations!
419PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
420PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
421PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
422PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
423
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000424// TODO(edisonn): perf!!!
425
426static SkColorTable* getGrayColortable() {
427 static SkColorTable* grayColortable = NULL;
428 if (grayColortable == NULL) {
429 SkPMColor* colors = new SkPMColor[256];
430 for (int i = 0 ; i < 256; i++) {
431 colors[i] = SkPreMultiplyARGB(255, i, i, i);
432 }
433 grayColortable = new SkColorTable(colors, 256);
434 }
435 return grayColortable;
436}
437
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000438static SkBitmap transferImageStreamToBitmap(const unsigned char* uncompressedStream, size_t uncompressedStreamLength,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000439 int width, int height, int bytesPerLine,
440 int bpc, const std::string& colorSpace,
441 bool transparencyMask) {
442 SkBitmap bitmap;
443
edisonn@google.com571c70b2013-07-10 17:09:50 +0000444 //int components = GetColorSpaceComponents(colorSpace);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000445//#define MAX_COMPONENTS 10
446
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000447 // TODO(edisonn): assume start of lines are aligned at 32 bits?
448 // Is there a faster way to load the uncompressed stream into a bitmap?
449
450 // minimal support for now
451 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
452 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
453
454 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000455 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000456 for (int w = 0 ; w < width; w++) {
457 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
458 uncompressedStream[3 * w + 1],
459 uncompressedStream[3 * w + 2]);
460 i++;
461 }
462 uncompressedStream += bytesPerLine;
463 }
464
465 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
466 bitmap.setPixels(uncompressedStreamArgb);
467 }
468 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
469 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
470
471 for (int h = 0 ; h < height; h++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000472 long i = width * (h);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000473 for (int w = 0 ; w < width; w++) {
474 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
475 uncompressedStream[w];
476 i++;
477 }
478 uncompressedStream += bytesPerLine;
479 }
480
481 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
482 width, height);
483 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
484 }
485
486 // TODO(edisonn): Report Warning, NYI, or error
487 return bitmap;
488}
489
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000490// utils
491
492// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
493// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
494// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
495// skia format, through a table
496
497// this functions returns the image, it does not look at the smask.
498
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000499static SkBitmap getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000500 if (image == NULL || !image->hasStream()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000501 // TODO(edisonn): report warning to be used in testing.
502 return SkBitmap();
503 }
504
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000505 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc);
506 int64_t width = image->Width(pdfContext->fPdfDoc);
507 int64_t height = image->Height(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000508 std::string colorSpace = "DeviceRGB";
509
510 // TODO(edisonn): color space can be an array too!
edisonn@google.com571c70b2013-07-10 17:09:50 +0000511 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
512 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000513 }
514
515/*
516 bool imageMask = image->imageMask();
517
518 if (imageMask) {
519 if (bpc != 0 && bpc != 1) {
520 // TODO(edisonn): report warning to be used in testing.
521 return SkBitmap();
522 }
523 bpc = 1;
524 }
525*/
526
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000527 const unsigned char* uncompressedStream = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000528 size_t uncompressedStreamLength = 0;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000529
edisonn@google.com571c70b2013-07-10 17:09:50 +0000530 SkPdfStream* stream = (SkPdfStream*)image;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000531
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000532 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000533 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000534 // TODO(edisonn): report warning to be used in testing.
535 return SkBitmap();
536 }
537
edisonn@google.com571c70b2013-07-10 17:09:50 +0000538 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
539
540 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) &&
541 streamDict->getFilterAsName(NULL) == "DCTDecode") ||
542 (streamDict->isFilterAArray(NULL) &&
543 streamDict->getFilterAsArray(NULL)->size() > 0 &&
544 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
545 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() == "DCTDecode"))) {
546 SkBitmap bitmap;
547 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, &bitmap);
548 return bitmap;
549 }
550
551
552
553 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
554// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
555// obj.GetDictionary().GetKey(PdfName("Filter")));
556// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
557// value = resolveReferenceObject(pdfContext->fPdfDoc,
558// &value->GetArray()[0]);
559// }
560// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
561// SkStream stream = SkStream::
562// SkImageDecoder::Factory()
563// }
564
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000565 int bytesPerLine = uncompressedStreamLength / height;
566#ifdef PDF_TRACE
567 if (uncompressedStreamLength % height != 0) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000568 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000569 }
570#endif
571
572 SkBitmap bitmap = transferImageStreamToBitmap(
573 (unsigned char*)uncompressedStream, uncompressedStreamLength,
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000574 (int)width, (int)height, bytesPerLine,
575 (int)bpc, colorSpace,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000576 transparencyMask);
577
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000578 return bitmap;
579}
580
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000581static SkBitmap getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000582 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000583
584 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000585 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000586 }
587
588 // TODO(edisonn): implement GS SMask. Default to empty right now.
589 return pdfContext->fGraphicsState.fSMask;
590}
591
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000592static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000593 if (skpdfimage == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000594 return kIgnoreError_PdfResult;
595 }
596
597 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
598 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
599
600 canvas->save();
601 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000602
603#if 1
604 SkScalar z = SkIntToScalar(0);
605 SkScalar one = SkIntToScalar(1);
606
607 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make(one, one), SkPoint::Make(z, one)};
608 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
609 SkMatrix flip;
610 SkAssertResult(flip.setPolyToPoly(from, to, 4));
611 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fMatrix;
612 solveImageFlip.preConcat(flip);
613 canvas->setMatrix(solveImageFlip);
614#endif
615
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000616 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
617
618 if (sMask.empty()) {
619 canvas->drawBitmapRect(image, dst, NULL);
620 } else {
621 canvas->saveLayer(&dst, NULL);
622 canvas->drawBitmapRect(image, dst, NULL);
623 SkPaint xfer;
624 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
625 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
626 canvas->drawBitmapRect(sMask, dst, &xfer);
627 canvas->restore();
628 }
629
630 canvas->restore();
631
632 return kPartial_PdfResult;
633}
634
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000635
636
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000637
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000638static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000639 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000640 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000641 }
642
643 PdfOp_q(pdfContext, canvas, NULL);
644 canvas->save();
645
646
edisonn@google.com571c70b2013-07-10 17:09:50 +0000647 if (skobj->Resources(pdfContext->fPdfDoc)) {
648 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000649 }
650
651 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
652
edisonn@google.com571c70b2013-07-10 17:09:50 +0000653 if (skobj->has_Matrix()) {
654 pdfContext->fGraphicsState.fMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000655 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
656 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
657 // TODO(edisonn) reset matrixTm and matricTlm also?
658 }
659
660 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
661
662 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
663
edisonn@google.com571c70b2013-07-10 17:09:50 +0000664 if (skobj->has_BBox()) {
665 canvas->clipRect(skobj->BBox(pdfContext->fPdfDoc), SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000666 }
667
668 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
669 // For this PdfContentsTokenizer needs to be extended.
670
edisonn@google.com571c70b2013-07-10 17:09:50 +0000671 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000672
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000673 SkPdfNativeTokenizer* tokenizer =
674 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000675 if (tokenizer != NULL) {
676 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
677 looper.loop();
678 delete tokenizer;
679 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000680
681 // TODO(edisonn): should we restore the variable stack at the same state?
682 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
683 canvas->restore();
684 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000685 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000686}
687
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000688//static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
689// return kNYI_PdfResult;
690//}
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000691
edisonn@google.com571c70b2013-07-10 17:09:50 +0000692PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
693 if (!skobj || !skobj->hasStream()) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000694 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000695 }
696
697 PdfOp_q(pdfContext, canvas, NULL);
698 canvas->save();
699
700 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
701 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
702
703 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm;
704 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
705
706 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
707
708 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
709
710 SkRect rm = bBox;
711 pdfContext->fGraphicsState.fMatrix.mapRect(&rm);
712
713 SkTraceRect(rm, "bbox mapped");
714
715 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
716
717 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
718 // For this PdfContentsTokenizer needs to be extended.
719
edisonn@google.com571c70b2013-07-10 17:09:50 +0000720 SkPdfStream* stream = (SkPdfStream*)skobj;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000721
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000722 SkPdfNativeTokenizer* tokenizer =
723 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000724 if (tokenizer != NULL) {
725 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
726 looper.loop();
727 delete tokenizer;
728 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000729
730 // TODO(edisonn): should we restore the variable stack at the same state?
731 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
732 canvas->restore();
733 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000734
735 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000736}
737
738
edisonn@google.com571c70b2013-07-10 17:09:50 +0000739// TODO(edisonn): make sure the pointer is unique
740std::set<const SkPdfObject*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000741
742class CheckRecursiveRendering {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000743 const SkPdfObject* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000744public:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000745 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000746 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000747 }
748
749 ~CheckRecursiveRendering() {
750 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000751 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000752 }
753
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000754 static bool IsInRendering(const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000755 return gInRendering.find(obj) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000756 }
757};
758
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000759static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000760 if (CheckRecursiveRendering::IsInRendering(obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000761 // Oops, corrupt PDF!
762 return kIgnoreError_PdfResult;
763 }
764
edisonn@google.com571c70b2013-07-10 17:09:50 +0000765 CheckRecursiveRendering checkRecursion(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000766
edisonn@google.com571c70b2013-07-10 17:09:50 +0000767 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000768 {
769 case kImageDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000770 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000771 case kType1FormDictionary_SkPdfObjectType:
edisonn@google.com571c70b2013-07-10 17:09:50 +0000772 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000773 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
774 //return doXObject_PS(skxobj.asPS());
edisonn@google.com571c70b2013-07-10 17:09:50 +0000775 default:
776 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000777 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000778}
779
780PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
781 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
782 canvas->save();
783 return kOK_PdfResult;
784}
785
786PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
787 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
788 pdfContext->fStateStack.pop();
789 canvas->restore();
790 return kOK_PdfResult;
791}
792
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000793static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000794 double array[6];
795 for (int i = 0 ; i < 6 ; i++) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000796 array[5 - i] = pdfContext->fObjectStack.top()->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000797 pdfContext->fObjectStack.pop();
798 }
799
800 // a b
801 // c d
802 // e f
803
804 // 0 1
805 // 2 3
806 // 4 5
807
808 // sx ky
809 // kx sy
810 // tx ty
811 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
812
813 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
814
815#ifdef PDF_TRACE
816 printf("cm ");
817 for (int i = 0 ; i < 6 ; i++) {
818 printf("%f ", array[i]);
819 }
820 printf("\n");
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000821 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000822#endif
823
824 return kOK_PdfResult;
825}
826
827//leading TL Set the text leading, Tl
828//, to leading, which is a number expressed in unscaled text
829//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000830static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000831 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000832
833 pdfContext->fGraphicsState.fTextLeading = ty;
834
835 return kOK_PdfResult;
836}
837
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000838static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000839 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
840 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000841
842 double array[6] = {1, 0, 0, 1, tx, ty};
843 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
844
845 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
846 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
847
848 return kPartial_PdfResult;
849}
850
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000851static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000852 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
853 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000854
edisonn@google.com571c70b2013-07-10 17:09:50 +0000855 // TODO(edisonn): Create factory methods or constructors so native is hidden
856 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000857 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000858
859 PdfOp_TL(pdfContext, canvas, looper);
860
edisonn@google.com571c70b2013-07-10 17:09:50 +0000861 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000862 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000863
edisonn@google.com571c70b2013-07-10 17:09:50 +0000864 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000865 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000866
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000867 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
868
869 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
870 // and the cleanup can happen while the user looks at the image
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000871
872 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000873}
874
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000875static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000876 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
877 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
878 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
879 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
880 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
881 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000882
883 double array[6];
884 array[0] = a;
885 array[1] = b;
886 array[2] = c;
887 array[3] = d;
888 array[4] = e;
889 array[5] = f;
890
891 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
892 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
893
894 // TODO(edisonn): Text positioning.
895 pdfContext->fGraphicsState.fMatrixTm = matrix;
896 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
897
898 return kPartial_PdfResult;
899}
900
901//— T* Move to the start of the next line. This operator has the same effect as the code
902//0 Tl Td
903//where Tl is the current leading parameter in the text state
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000904static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000905 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
906 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000907
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000908 pdfContext->fObjectStack.push(zero);
909 pdfContext->fObjectStack.push(tl);
910
911 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
912
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000913 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000914}
915
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000916static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000917 if (pdfContext->fGraphicsState.fPathClosed) {
918 pdfContext->fGraphicsState.fPath.reset();
919 pdfContext->fGraphicsState.fPathClosed = false;
920 }
921
edisonn@google.com571c70b2013-07-10 17:09:50 +0000922 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
923 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000924
925 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
926 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
927
928 return kOK_PdfResult;
929}
930
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000931static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000932 if (pdfContext->fGraphicsState.fPathClosed) {
933 pdfContext->fGraphicsState.fPath.reset();
934 pdfContext->fGraphicsState.fPathClosed = false;
935 }
936
edisonn@google.com571c70b2013-07-10 17:09:50 +0000937 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
938 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000939
940 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
941 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
942
943 return kOK_PdfResult;
944}
945
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000946static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000947 if (pdfContext->fGraphicsState.fPathClosed) {
948 pdfContext->fGraphicsState.fPath.reset();
949 pdfContext->fGraphicsState.fPathClosed = false;
950 }
951
edisonn@google.com571c70b2013-07-10 17:09:50 +0000952 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
953 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
954 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
955 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
956 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
957 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000958
959 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
960 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
961 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
962
963 pdfContext->fGraphicsState.fCurPosX = x3;
964 pdfContext->fGraphicsState.fCurPosY = y3;
965
966 return kOK_PdfResult;
967}
968
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000969static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000970 if (pdfContext->fGraphicsState.fPathClosed) {
971 pdfContext->fGraphicsState.fPath.reset();
972 pdfContext->fGraphicsState.fPathClosed = false;
973 }
974
edisonn@google.com571c70b2013-07-10 17:09:50 +0000975 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
976 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
977 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
978 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000979 double y1 = pdfContext->fGraphicsState.fCurPosY;
980 double x1 = pdfContext->fGraphicsState.fCurPosX;
981
982 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
983 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
984 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
985
986 pdfContext->fGraphicsState.fCurPosX = x3;
987 pdfContext->fGraphicsState.fCurPosY = y3;
988
989 return kOK_PdfResult;
990}
991
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000992static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000993 if (pdfContext->fGraphicsState.fPathClosed) {
994 pdfContext->fGraphicsState.fPath.reset();
995 pdfContext->fGraphicsState.fPathClosed = false;
996 }
997
edisonn@google.com571c70b2013-07-10 17:09:50 +0000998 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
999 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001000 double y2 = pdfContext->fGraphicsState.fCurPosY;
1001 double x2 = pdfContext->fGraphicsState.fCurPosX;
edisonn@google.com571c70b2013-07-10 17:09:50 +00001002 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1003 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001004
1005 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1006 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1007 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1008
1009 pdfContext->fGraphicsState.fCurPosX = x3;
1010 pdfContext->fGraphicsState.fCurPosY = y3;
1011
1012 return kOK_PdfResult;
1013}
1014
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001015static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001016 if (pdfContext->fGraphicsState.fPathClosed) {
1017 pdfContext->fGraphicsState.fPath.reset();
1018 pdfContext->fGraphicsState.fPathClosed = false;
1019 }
1020
edisonn@google.com571c70b2013-07-10 17:09:50 +00001021 double height = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1022 double width = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1023 double y = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1024 double x = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001025
1026 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1027 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1028
1029 pdfContext->fGraphicsState.fCurPosX = x;
1030 pdfContext->fGraphicsState.fCurPosY = y + height;
1031
1032 return kOK_PdfResult;
1033}
1034
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001035static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001036 pdfContext->fGraphicsState.fPath.close();
1037 return kOK_PdfResult;
1038}
1039
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001040static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001041 SkPath path = pdfContext->fGraphicsState.fPath;
1042
1043 if (close) {
1044 path.close();
1045 }
1046
1047 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1048
1049 SkPaint paint;
1050
1051 SkPoint line[2];
1052 if (fill && !stroke && path.isLine(line)) {
1053 paint.setStyle(SkPaint::kStroke_Style);
1054
1055 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1056 paint.setStrokeWidth(SkDoubleToScalar(0));
1057
1058 canvas->drawPath(path, paint);
1059 } else {
1060 if (fill) {
1061 paint.setStyle(SkPaint::kFill_Style);
1062 if (evenOdd) {
1063 path.setFillType(SkPath::kEvenOdd_FillType);
1064 }
1065
1066 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1067
1068 canvas->drawPath(path, paint);
1069 }
1070
1071 if (stroke) {
1072 paint.setStyle(SkPaint::kStroke_Style);
1073
1074 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1075
1076 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1077 canvas->drawPath(path, paint);
1078 }
1079 }
1080
1081 pdfContext->fGraphicsState.fPath.reset();
1082 // todo zoom ... other stuff ?
1083
1084 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1085#ifndef PDF_DEBUG_NO_CLIPING
1086 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1087#endif
1088 }
1089
1090 //pdfContext->fGraphicsState.fClipPath.reset();
1091 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1092
1093 return kPartial_PdfResult;
1094
1095}
1096
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001097static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001098 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1099}
1100
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001101static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001102 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1103}
1104
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001105static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001106 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1107}
1108
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001109static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001110 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1111}
1112
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001113static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001114 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1115}
1116
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001117static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001118 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1119}
1120
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001121static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001122 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1123}
1124
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001125static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001126 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1127}
1128
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001129static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001130 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1131}
1132
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001133static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001134 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1135 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1136#ifndef PDF_DEBUG_NO_CLIPING
1137 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1138#endif
1139 }
1140
1141 //pdfContext->fGraphicsState.fClipPath.reset();
1142 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1143
1144 pdfContext->fGraphicsState.fPathClosed = true;
1145
1146 return kOK_PdfResult;
1147}
1148
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001149static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001150 pdfContext->fGraphicsState.fTextBlock = true;
1151 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1152 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1153
1154 return kPartial_PdfResult;
1155}
1156
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001157static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001158 if (!pdfContext->fGraphicsState.fTextBlock) {
1159 return kIgnoreError_PdfResult;
1160 }
1161 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1162 return kPartial_PdfResult;
1163}
1164
1165//font size Tf Set the text font, Tf
1166//, to font and the text font size, Tfs, to size. font is the name of a
1167//font resource in the Fontsubdictionary of the current resource dictionary; size is
1168//a number representing a scale factor. There is no initial value for either font or
1169//size; they must be specified explicitly using Tf before any text is shown.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001170static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001171 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001172 SkPdfObject* fontName = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001173
1174#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001175 printf("font name: %s\n", fontName->nameValue2().c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001176#endif
1177
edisonn@google.com571c70b2013-07-10 17:09:50 +00001178 if (pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1179 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1180 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1181 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1182 // TODO(edisonn): try to recover and draw it any way?
1183 return kIgnoreError_PdfResult;
1184 }
1185 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1186
1187 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1188
1189 if (skfont) {
1190 pdfContext->fGraphicsState.fSkFont = skfont;
1191 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001192 }
edisonn@google.com571c70b2013-07-10 17:09:50 +00001193 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001194}
1195
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001196static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001197 if (!pdfContext->fGraphicsState.fTextBlock) {
1198 // TODO(edisonn): try to recover and draw it any way?
1199 return kIgnoreError_PdfResult;
1200 }
1201
1202 PdfResult ret = DrawText(pdfContext,
1203 pdfContext->fObjectStack.top(),
1204 canvas);
1205 pdfContext->fObjectStack.pop();
1206
1207 return ret;
1208}
1209
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001210static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001211 if (!pdfContext->fGraphicsState.fTextBlock) {
1212 // TODO(edisonn): try to recover and draw it any way?
1213 return kIgnoreError_PdfResult;
1214 }
1215
1216 PdfOp_T_star(pdfContext, canvas, looper);
1217 // Do not pop, and push, just transfer the param to Tj
1218 return PdfOp_Tj(pdfContext, canvas, looper);
1219}
1220
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001221static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001222 if (!pdfContext->fGraphicsState.fTextBlock) {
1223 // TODO(edisonn): try to recover and draw it any way?
1224 return kIgnoreError_PdfResult;
1225 }
1226
1227 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1228 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1229 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1230
1231 pdfContext->fObjectStack.push(aw);
1232 PdfOp_Tw(pdfContext, canvas, looper);
1233
1234 pdfContext->fObjectStack.push(ac);
1235 PdfOp_Tc(pdfContext, canvas, looper);
1236
1237 pdfContext->fObjectStack.push(str);
1238 PdfOp_quote(pdfContext, canvas, looper);
1239
1240 return kPartial_PdfResult;
1241}
1242
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001243static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001244 if (!pdfContext->fGraphicsState.fTextBlock) {
1245 // TODO(edisonn): try to recover and draw it any way?
1246 return kIgnoreError_PdfResult;
1247 }
1248
edisonn@google.com571c70b2013-07-10 17:09:50 +00001249 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001250 pdfContext->fObjectStack.pop();
1251
edisonn@google.com571c70b2013-07-10 17:09:50 +00001252 if (!array->isArray()) {
1253 return kIgnoreError_PdfResult;
1254 }
1255
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001256 for( int i=0; i<static_cast<int>(array->size()); i++ )
1257 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001258 if( (*array)[i]->isAnyString()) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001259 SkPdfObject* obj = (*array)[i];
1260 DrawText(pdfContext,
1261 obj,
1262 canvas);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001263 } else if ((*array)[i]->isNumber()) {
1264 double dx = (*array)[i]->numberValue();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001265 SkMatrix matrix;
1266 matrix.setAll(SkDoubleToScalar(1),
1267 SkDoubleToScalar(0),
1268 // TODO(edisonn): use writing mode, vertical/horizontal.
1269 SkDoubleToScalar(-dx), // amount is substracted!!!
1270 SkDoubleToScalar(0),
1271 SkDoubleToScalar(1),
1272 SkDoubleToScalar(0),
1273 SkDoubleToScalar(0),
1274 SkDoubleToScalar(0),
1275 SkDoubleToScalar(1));
1276
1277 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1278 }
1279 }
1280 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1281}
1282
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001283static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001284 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->strRef(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001285 return kOK_PdfResult;
1286}
1287
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001288static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001289 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1290}
1291
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001292static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001293 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1294}
1295
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001296static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001297 double c[4];
edisonn@google.com571c70b2013-07-10 17:09:50 +00001298// int64_t v[4];
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001299
1300 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1301
1302 bool doubles = true;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001303 if (colorOperator->fColorSpace.equals("Indexed")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001304 doubles = false;
1305 }
1306
1307#ifdef PDF_TRACE
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001308 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001309#endif
1310
1311 for (int i = n - 1; i >= 0 ; i--) {
1312 if (doubles) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001313 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1314// } else {
1315// v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001316 }
1317 }
1318
1319 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001320 // TODO(edisonn): do possible field values to enum at parsing time!
1321 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001322 if (colorOperator->fColorSpace.equals("DeviceRGB") || colorOperator->fColorSpace.equals("RGB")) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001323 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
1324 }
1325 return kPartial_PdfResult;
1326}
1327
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001328static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001329 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1330}
1331
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001332static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001333 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1334}
1335
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001336static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001337 //SkPdfString* name;
1338 if (pdfContext->fObjectStack.top()->isName()) {
1339 // TODO(edisonn): get name, pass it
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001340 pdfContext->fObjectStack.pop();
1341 }
1342
1343 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1344 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1345
1346 return kPartial_PdfResult;
1347}
1348
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001349static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001350 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1351}
1352
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001353static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001354 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1355}
1356
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001357static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001358 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001359 return kNYI_PdfResult;
1360}
1361
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001362static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001363 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1364}
1365
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001366static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001367 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1368}
1369
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001370static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001371 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1372 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1373 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001374
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001375 colorOperator->fColorSpace = strings_DeviceRGB;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001376 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
1377 return kOK_PdfResult;
1378}
1379
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001380static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001381 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1382}
1383
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001384static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001385 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1386}
1387
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001388static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001389 // TODO(edisonn): spec has some rules about overprint, implement them.
edisonn@google.com571c70b2013-07-10 17:09:50 +00001390 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1391 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1392 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
1393 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001394
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001395 colorOperator->fColorSpace = strings_DeviceCMYK;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001396 // TODO(edisonn): Set color.
1397 return kNYI_PdfResult;
1398}
1399
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001400static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001401 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1402}
1403
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001404static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001405 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1406}
1407
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001408static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001409 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1410 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1411
1412 return kOK_PdfResult;
1413}
1414
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001415static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001416 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1417
1418#ifdef PDF_TRACE
1419 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
1420 printf("CLIP IS RECT\n");
1421 }
1422#endif
1423
1424 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
1425 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1426 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1427
1428 return kPartial_PdfResult;
1429}
1430
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001431static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001432 *looper = new PdfCompatibilitySectionLooper();
1433 return kOK_PdfResult;
1434}
1435
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001436static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001437#ifdef ASSERT_BAD_PDF_OPS
1438 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1439 // have the assert when testing good pdfs.
1440#endif
1441 return kIgnoreError_PdfResult;
1442}
1443
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001444static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001445 *looper = new PdfInlineImageLooper();
1446 return kOK_PdfResult;
1447}
1448
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001449static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001450#ifdef ASSERT_BAD_PDF_OPS
1451 SkASSERT(false); // must be processed in inline image looper, but let's
1452 // have the assert when testing good pdfs.
1453#endif
1454 return kIgnoreError_PdfResult;
1455}
1456
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001457static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001458#ifdef ASSERT_BAD_PDF_OPS
1459 SkASSERT(false); // must be processed in inline image looper, but let's
1460 // have the assert when testing good pdfs.
1461#endif
1462 return kIgnoreError_PdfResult;
1463}
1464
1465//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001466static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001467 double lineWidth = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001468 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1469
1470 return kOK_PdfResult;
1471}
1472
1473//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 +00001474static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001475 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001476 //double lineCap = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001477
1478 return kNYI_PdfResult;
1479}
1480
1481//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 +00001482static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001483 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001484 //double lineJoin = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001485
1486 return kNYI_PdfResult;
1487}
1488
1489//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001490static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001491 pdfContext->fObjectStack.pop();
edisonn@google.com571c70b2013-07-10 17:09:50 +00001492 //double miterLimit = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001493
1494 return kNYI_PdfResult;
1495}
1496
1497//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1498//page 155).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001499static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001500 pdfContext->fObjectStack.pop();
1501 pdfContext->fObjectStack.pop();
1502
1503 return kNYI_PdfResult;
1504}
1505
1506//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 +00001507static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001508 pdfContext->fObjectStack.pop();
1509
1510 return kNYI_PdfResult;
1511}
1512
1513//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1514//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1515//fies the output device’s default flatness tolerance.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001516static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001517 pdfContext->fObjectStack.pop();
1518
1519 return kNYI_PdfResult;
1520}
1521
1522//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
1523//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 +00001524static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001525 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001526
1527#ifdef PDF_TRACE
1528 std::string str;
1529#endif
1530
1531 //Next, get the ExtGState Dictionary from the Resource Dictionary:
edisonn@google.com571c70b2013-07-10 17:09:50 +00001532 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001533
1534 if (extGStateDictionary == NULL) {
1535#ifdef PDF_TRACE
1536 printf("ExtGState is NULL!\n");
1537#endif
1538 return kIgnoreError_PdfResult;
1539 }
1540
edisonn@google.com571c70b2013-07-10 17:09:50 +00001541 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001542
edisonn@google.com571c70b2013-07-10 17:09:50 +00001543 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
1544 return kIgnoreError_PdfResult;
1545 }
1546 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001547
1548 // TODO(edisonn): now load all those properties in graphic state.
1549 if (gs == NULL) {
1550 return kIgnoreError_PdfResult;
1551 }
1552
1553 if (gs->has_CA()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001554 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001555 }
1556
1557 if (gs->has_ca()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001558 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001559 }
1560
1561 if (gs->has_LW()) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001562 pdfContext->fGraphicsState.fLineWidth = gs->LW(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001563 }
1564
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001565 return kNYI_PdfResult;
1566}
1567
1568//charSpace Tc Set the character spacing, Tc
1569//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
1570//Initial value: 0.
1571PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001572 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001573 pdfContext->fGraphicsState.fCharSpace = charSpace;
1574
1575 return kOK_PdfResult;
1576}
1577
1578//wordSpace Tw Set the word spacing, T
1579//w
1580//, to wordSpace, which is a number expressed in unscaled
1581//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
1582//value: 0.
1583PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001584 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001585 pdfContext->fGraphicsState.fWordSpace = wordSpace;
1586
1587 return kOK_PdfResult;
1588}
1589
1590//scale Tz Set the horizontal scaling, Th
1591//, to (scale ˜ 100). scale is a number specifying the
1592//percentage of the normal width. Initial value: 100 (normal width).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001593static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001594 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001595
1596 return kNYI_PdfResult;
1597}
1598
1599//render Tr Set the text rendering mode, T
1600//mode, to render, which is an integer. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001601static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001602 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001603
1604 return kNYI_PdfResult;
1605}
1606//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
1607//units. Initial value: 0.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001608static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001609 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001610
1611 return kNYI_PdfResult;
1612}
1613
1614//wx wy d0
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001615static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001616 pdfContext->fObjectStack.pop();
1617 pdfContext->fObjectStack.pop();
1618
1619 return kNYI_PdfResult;
1620}
1621
1622//wx wy llx lly urx ury d1
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001623static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001624 pdfContext->fObjectStack.pop();
1625 pdfContext->fObjectStack.pop();
1626 pdfContext->fObjectStack.pop();
1627 pdfContext->fObjectStack.pop();
1628 pdfContext->fObjectStack.pop();
1629 pdfContext->fObjectStack.pop();
1630
1631 return kNYI_PdfResult;
1632}
1633
1634//name sh
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001635static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001636 pdfContext->fObjectStack.pop();
1637
1638 return kNYI_PdfResult;
1639}
1640
1641//name Do
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001642static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001643 SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001644
edisonn@google.com571c70b2013-07-10 17:09:50 +00001645 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001646
1647 if (xObject == NULL) {
1648#ifdef PDF_TRACE
1649 printf("XObject is NULL!\n");
1650#endif
1651 return kIgnoreError_PdfResult;
1652 }
1653
edisonn@google.com571c70b2013-07-10 17:09:50 +00001654 SkPdfObject* value = xObject->get(name);
1655 value = pdfContext->fPdfDoc->resolveReference(value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001656
1657#ifdef PDF_TRACE
1658// value->ToString(str);
edisonn@google.com571c70b2013-07-10 17:09:50 +00001659// printf("Do object value: %s\n", str);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001660#endif
1661
edisonn@google.com571c70b2013-07-10 17:09:50 +00001662 return doXObject(pdfContext, canvas, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001663}
1664
1665//tag MP Designate a marked-content point. tag is a name object indicating the role or
1666//significance of the point.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001667static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001668 pdfContext->fObjectStack.pop();
1669
1670 return kNYI_PdfResult;
1671}
1672
1673//tag properties DP Designate a marked-content point with an associated property list. tag is a
1674//name object indicating the role or significance of the point; properties is
1675//either an inline dictionary containing the property list or a name object
1676//associated with it in the Properties subdictionary of the current resource
1677//dictionary (see Section 9.5.1, “Property Lists”).
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001678static PdfResult PdfOp_DP(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//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
1686//tag is a name object indicating the role or significance of the sequence.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001687static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001688 pdfContext->fObjectStack.pop();
1689
1690 return kNYI_PdfResult;
1691}
1692
1693//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
1694//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
1695//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 +00001696static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001697 pdfContext->fObjectStack.pop();
1698 pdfContext->fObjectStack.pop();
1699
1700 return kNYI_PdfResult;
1701}
1702
1703//— EMC End a marked-content sequence begun by a BMC or BDC operator.
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001704static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001705 return kNYI_PdfResult;
1706}
1707
edisonn@google.coma3356fc2013-07-10 18:20:06 +00001708static void initPdfOperatorRenderes() {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001709 static bool gInitialized = false;
1710 if (gInitialized) {
1711 return;
1712 }
1713
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001714 gPdfOps.set("q", PdfOp_q);
1715 gPdfOps.set("Q", PdfOp_Q);
1716 gPdfOps.set("cm", PdfOp_cm);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001717
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001718 gPdfOps.set("TD", PdfOp_TD);
1719 gPdfOps.set("Td", PdfOp_Td);
1720 gPdfOps.set("Tm", PdfOp_Tm);
1721 gPdfOps.set("T*", PdfOp_T_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001722
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001723 gPdfOps.set("m", PdfOp_m);
1724 gPdfOps.set("l", PdfOp_l);
1725 gPdfOps.set("c", PdfOp_c);
1726 gPdfOps.set("v", PdfOp_v);
1727 gPdfOps.set("y", PdfOp_y);
1728 gPdfOps.set("h", PdfOp_h);
1729 gPdfOps.set("re", PdfOp_re);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001730
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001731 gPdfOps.set("S", PdfOp_S);
1732 gPdfOps.set("s", PdfOp_s);
1733 gPdfOps.set("f", PdfOp_f);
1734 gPdfOps.set("F", PdfOp_F);
1735 gPdfOps.set("f*", PdfOp_f_star);
1736 gPdfOps.set("B", PdfOp_B);
1737 gPdfOps.set("B*", PdfOp_B_star);
1738 gPdfOps.set("b", PdfOp_b);
1739 gPdfOps.set("b*", PdfOp_b_star);
1740 gPdfOps.set("n", PdfOp_n);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001741
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001742 gPdfOps.set("BT", PdfOp_BT);
1743 gPdfOps.set("ET", PdfOp_ET);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001744
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001745 gPdfOps.set("Tj", PdfOp_Tj);
1746 gPdfOps.set("'", PdfOp_quote);
1747 gPdfOps.set("\"", PdfOp_doublequote);
1748 gPdfOps.set("TJ", PdfOp_TJ);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001749
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001750 gPdfOps.set("CS", PdfOp_CS);
1751 gPdfOps.set("cs", PdfOp_cs);
1752 gPdfOps.set("SC", PdfOp_SC);
1753 gPdfOps.set("SCN", PdfOp_SCN);
1754 gPdfOps.set("sc", PdfOp_sc);
1755 gPdfOps.set("scn", PdfOp_scn);
1756 gPdfOps.set("G", PdfOp_G);
1757 gPdfOps.set("g", PdfOp_g);
1758 gPdfOps.set("RG", PdfOp_RG);
1759 gPdfOps.set("rg", PdfOp_rg);
1760 gPdfOps.set("K", PdfOp_K);
1761 gPdfOps.set("k", PdfOp_k);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001762
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001763 gPdfOps.set("W", PdfOp_W);
1764 gPdfOps.set("W*", PdfOp_W_star);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001765
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001766 gPdfOps.set("BX", PdfOp_BX);
1767 gPdfOps.set("EX", PdfOp_EX);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001768
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001769 gPdfOps.set("BI", PdfOp_BI);
1770 gPdfOps.set("ID", PdfOp_ID);
1771 gPdfOps.set("EI", PdfOp_EI);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001772
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001773 gPdfOps.set("w", PdfOp_w);
1774 gPdfOps.set("J", PdfOp_J);
1775 gPdfOps.set("j", PdfOp_j);
1776 gPdfOps.set("M", PdfOp_M);
1777 gPdfOps.set("d", PdfOp_d);
1778 gPdfOps.set("ri", PdfOp_ri);
1779 gPdfOps.set("i", PdfOp_i);
1780 gPdfOps.set("gs", PdfOp_gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001781
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001782 gPdfOps.set("Tc", PdfOp_Tc);
1783 gPdfOps.set("Tw", PdfOp_Tw);
1784 gPdfOps.set("Tz", PdfOp_Tz);
1785 gPdfOps.set("TL", PdfOp_TL);
1786 gPdfOps.set("Tf", PdfOp_Tf);
1787 gPdfOps.set("Tr", PdfOp_Tr);
1788 gPdfOps.set("Ts", PdfOp_Ts);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001789
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001790 gPdfOps.set("d0", PdfOp_d0);
1791 gPdfOps.set("d1", PdfOp_d1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001792
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001793 gPdfOps.set("sh", PdfOp_sh);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001794
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001795 gPdfOps.set("Do", PdfOp_Do);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001796
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001797 gPdfOps.set("MP", PdfOp_MP);
1798 gPdfOps.set("DP", PdfOp_DP);
1799 gPdfOps.set("BMC", PdfOp_BMC);
1800 gPdfOps.set("BDC", PdfOp_BDC);
1801 gPdfOps.set("EMC", PdfOp_EMC);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001802
1803 gInitialized = true;
1804}
1805
1806class InitPdfOps {
1807public:
1808 InitPdfOps() {
1809 initPdfOperatorRenderes();
1810 }
1811};
1812
1813InitPdfOps gInitPdfOps;
1814
1815void reportPdfRenderStats() {
1816 std::map<std::string, int>::iterator iter;
1817
1818 for (int i = 0 ; i < kCount_PdfResult; i++) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001819 SkTDict<int>::Iter iter(gRenderStats[i]);
1820 const char* key;
1821 int value = 0;
1822 while ((key = iter.next(&value)) != NULL) {
1823 printf("%s: %s -> count %i\n", gRenderStatsNames[i], key, value);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001824 }
1825 }
1826}
1827
1828PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001829 char keyword[256];
1830
1831 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001832 {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001833 strncpy(keyword, token.fKeyword, token.fKeywordLength);
1834 keyword[token.fKeywordLength] = '\0';
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001835 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001836 PdfOperatorRenderer pdfOperatorRenderer = NULL;
1837 if (gPdfOps.find(keyword, &pdfOperatorRenderer) && pdfOperatorRenderer) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001838 // caller, main work is done by pdfOperatorRenderer(...)
1839 PdfTokenLooper* childLooper = NULL;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001840 PdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
1841
1842 int cnt = 0;
1843 gRenderStats[result].find(keyword, &cnt);
1844 gRenderStats[result].set(keyword, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001845
1846 if (childLooper) {
1847 childLooper->setUp(this);
1848 childLooper->loop();
1849 delete childLooper;
1850 }
1851 } else {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001852 int cnt = 0;
1853 gRenderStats[kUnsupported_PdfResult].find(keyword, &cnt);
1854 gRenderStats[kUnsupported_PdfResult].set(keyword, cnt + 1);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001855 }
1856 }
1857 else if (token.fType == kObject_TokenType)
1858 {
1859 fPdfContext->fObjectStack.push( token.fObject );
1860 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001861 else {
edisonn@google.com571c70b2013-07-10 17:09:50 +00001862 // TODO(edisonn): deine or use assert not reached
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001863 return kIgnoreError_PdfResult;
1864 }
1865 return kOK_PdfResult;
1866}
1867
1868void PdfMainLooper::loop() {
1869 PdfToken token;
1870 while (readToken(fTokenizer, &token)) {
1871 consumeToken(token);
1872 }
1873}
1874
1875PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
edisonn@google.com78b38b12013-07-15 18:20:58 +00001876 SkASSERT(false);
1877 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001878}
1879
1880void PdfInlineImageLooper::loop() {
edisonn@google.com78b38b12013-07-15 18:20:58 +00001881 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001882}
1883
1884PdfResult PdfInlineImageLooper::done() {
1885
1886 // TODO(edisonn): long to short names
1887 // TODO(edisonn): set properties in a map
1888 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
1889 // the stream.
1890
1891 SkBitmap bitmap;
1892 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
1893
1894 // TODO(edisonn): matrix use.
1895 // Draw dummy red square, to show the prezence of the inline image.
1896 fCanvas->drawBitmap(bitmap,
1897 SkDoubleToScalar(0),
1898 SkDoubleToScalar(0),
1899 NULL);
1900 return kNYI_PdfResult;
1901}
1902
1903PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
1904 return fParent->consumeToken(token);
1905}
1906
1907void PdfCompatibilitySectionLooper::loop() {
1908 // TODO(edisonn): save stacks position, or create a new stack?
1909 // TODO(edisonn): what happens if we pop out more variables then when we started?
1910 // restore them? fail? We could create a new operands stack for every new BX/EX section,
1911 // pop-ing too much will not affect outside the section.
1912 PdfToken token;
1913 while (readToken(fTokenizer, &token)) {
1914 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
1915 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
1916 looper->setUp(this);
1917 looper->loop();
1918 delete looper;
1919 } else {
1920 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
1921 fParent->consumeToken(token);
1922 }
1923 }
1924 // TODO(edisonn): restore stack.
1925}
1926
1927// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
1928// TODO(edisonn): Add API for Forms viewing and editing
1929// e.g. SkBitmap getPage(int page);
1930// int formsCount();
1931// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
1932// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
1933// if we load the first page, and we zoom to fit to screen horizontally, then load only those
1934// resources needed, so the preview is fast.
1935// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
1936// references automatically.
1937
edisonn@google.com222382b2013-07-10 22:33:10 +00001938PdfContext* gPdfContext = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001939
edisonn@google.com444e25a2013-07-11 15:20:50 +00001940bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
edisonn@google.com222382b2013-07-10 22:33:10 +00001941 if (!fPdfDoc) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001942 return false;
1943 }
1944
edisonn@google.com222382b2013-07-10 22:33:10 +00001945 if (page < 0 || page >= pages()) {
1946 return false;
1947 }
1948
edisonn@google.com222382b2013-07-10 22:33:10 +00001949 PdfContext pdfContext(fPdfDoc);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001950
1951 SkPdfNativeTokenizer* tokenizer = fPdfDoc->tokenizerOfPage(page, pdfContext.fTmpPageAllocator);
edisonn@google.com1f080162013-07-23 21:05:49 +00001952 if (!tokenizer) {
1953 // TODO(edisonn): report/warning/debug
1954 return false;
1955 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +00001956
edisonn@google.com222382b2013-07-10 22:33:10 +00001957 pdfContext.fOriginalMatrix = SkMatrix::I();
1958 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
1959
1960 gPdfContext = &pdfContext;
1961
1962 // TODO(edisonn): get matrix stuff right.
edisonn@google.com222382b2013-07-10 22:33:10 +00001963 SkScalar z = SkIntToScalar(0);
edisonn@google.com444e25a2013-07-11 15:20:50 +00001964 SkScalar w = dst.width();
1965 SkScalar h = dst.height();
edisonn@google.com222382b2013-07-10 22:33:10 +00001966
edisonn@google.com444e25a2013-07-11 15:20:50 +00001967 SkScalar wp = fPdfDoc->MediaBox(page).width();
1968 SkScalar hp = fPdfDoc->MediaBox(page).height();
1969
1970 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 +00001971// SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
1972
1973 // TODO(edisonn): add flag for this app to create sourunding buffer zone
1974 // TODO(edisonn): add flagg for no clipping.
1975 // Use larger image to make sure we do not draw anything outside of page
1976 // could be used in tests.
1977
1978#ifdef PDF_DEBUG_3X
1979 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)};
1980#else
1981 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
1982#endif
1983 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
1984 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
1985
1986 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
1987 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
1988
1989 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
1990 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
1991
1992 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
1993 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
1994
1995
1996 pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
1997 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
1998 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
1999
edisonn@google.com222382b2013-07-10 22:33:10 +00002000#ifndef PDF_DEBUG_NO_PAGE_CLIPING
edisonn@google.com444e25a2013-07-11 15:20:50 +00002001 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
edisonn@google.com222382b2013-07-10 22:33:10 +00002002#endif
2003
edisonn@google.com444e25a2013-07-11 15:20:50 +00002004 canvas->setMatrix(pdfContext.fOriginalMatrix);
2005
edisonn@google.com222382b2013-07-10 22:33:10 +00002006// erase with red before?
2007// SkPaint paint;
2008// paint.setColor(SK_ColorRED);
2009// canvas->drawRect(rect, paint);
2010
2011 PdfMainLooper looper(NULL, tokenizer, &pdfContext, canvas);
2012 looper.loop();
2013
2014 delete tokenizer;
2015
2016 canvas->flush();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00002017 return true;
2018}
edisonn@google.com222382b2013-07-10 22:33:10 +00002019
2020bool SkPdfRenderer::load(const SkString inputFileName) {
2021 unload();
2022
2023 // TODO(edisonn): create static function that could return NULL if there are errors
2024 fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
edisonn@google.com6a9d4362013-07-11 16:25:51 +00002025 if (fPdfDoc->pages() == 0) {
2026 delete fPdfDoc;
2027 fPdfDoc = NULL;
2028 }
edisonn@google.com222382b2013-07-10 22:33:10 +00002029
2030 return fPdfDoc != NULL;
2031}
2032
edisonn@google.com147adb12013-07-24 15:56:19 +00002033bool SkPdfRenderer::load(SkStream* stream) {
2034 unload();
2035
2036 // TODO(edisonn): create static function that could return NULL if there are errors
2037 fPdfDoc = new SkNativeParsedPDF(stream);
2038 if (fPdfDoc->pages() == 0) {
2039 delete fPdfDoc;
2040 fPdfDoc = NULL;
2041 }
2042
2043 return fPdfDoc != NULL;
2044}
2045
2046
edisonn@google.com222382b2013-07-10 22:33:10 +00002047int SkPdfRenderer::pages() const {
2048 return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
2049}
2050
2051void SkPdfRenderer::unload() {
2052 delete fPdfDoc;
2053 fPdfDoc = NULL;
2054}
2055
2056SkRect SkPdfRenderer::MediaBox(int page) const {
2057 SkASSERT(fPdfDoc);
2058 return fPdfDoc->MediaBox(page);
2059}
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002060
edisonn@google.com7b328fd2013-07-11 12:53:06 +00002061size_t SkPdfRenderer::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +00002062 return fPdfDoc ? fPdfDoc->bytesUsed() : 0;
2063}
edisonn@google.com147adb12013-07-24 15:56:19 +00002064
2065bool SkPDFNativeRenderToBitmap(SkStream* stream,
2066 SkBitmap* output,
2067 int page,
2068 SkPdfContent content,
2069 double dpi) {
2070 SkASSERT(page >= 0);
2071 SkPdfRenderer renderer;
2072 renderer.load(stream);
2073 if (!renderer.loaded() || page >= renderer.pages() || page < 0) {
2074 return false;
2075 }
2076
2077 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
2078
2079 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2080 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0)));
2081
2082 rect = SkRect::MakeWH(width, height);
2083
2084 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height));
2085
2086 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*output)));
2087 SkCanvas canvas(device);
2088
2089 return renderer.renderPage(page, &canvas, rect);
2090}