blob: 030251250e00bcfacf64115f7f8b5a89d230731c [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"
19#include "picture_utils.h"
20
21#include <iostream>
22#include <cstdio>
23#include <stack>
24
25#include "podofo.h"
26using namespace PoDoFo;
27
28
29__SK_FORCE_IMAGE_DECODER_LINKING;
30
31// TODO(edisonn): tool, show what objects were read at least, show the ones not even read
32// keep for each object pos in file
33// plug in for VS? syntax coloring, show selected object ... from the text, or from rendered x,y
34
35// TODO(edisonn): security - validate all the user input, all pdf!
36
edisonn@google.com6e49c342013-06-27 20:03:43 +000037// TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor changes in text positioning and font
38// this way, we look more at other features and layout in diffs
edisonn@google.com131d4ee2013-06-26 17:48:12 +000039
edisonn@google.com3aac1f92013-07-02 22:42:53 +000040// TODO(edisonn): move trace dump in the get functions, and mapper ones too so it ghappens automatically
41/*
42#ifdef PDF_TRACE
43 std::string str;
44 pdfContext->fGraphicsState.fResources->podofo()->ToString(str);
45 printf("Print Tf Resources: %s\n", str.c_str());
46#endif
47 */
48
edisonn@google.com131d4ee2013-06-26 17:48:12 +000049#include "SkPdfHeaders_autogen.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +000050#include "SkPdfMapper_autogen.h"
edisonn@google.com131d4ee2013-06-26 17:48:12 +000051#include "SkPdfParser.h"
52
53#include "SkPdfBasics.h"
54#include "SkPdfUtils.h"
55
56#include "SkPdfFont.h"
57
edisonn@google.com131d4ee2013-06-26 17:48:12 +000058/*
59 * TODO(edisonn):
60 * - all font types and all ppdf font features
61 * - word spacing
62 * - load font for baidu.pdf
63 * - load font for youtube.pdf
64 * - parser for pdf from the definition already available in pdfspec_autogen.py
65 * - all docs from ~/work
66 * - encapsulate podofo in the pdf api so the skpdf does not know anything about podofo ... in progress
67 * - load gs/ especially smask and already known prop (skp) ... in progress
68 * - wrapper on classes for customizations? e.g.
69 * SkPdfPageObjectVanila - has only the basic loaders/getters
70 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add customizations here
71 * need to find a nice object model for all this with constructors and factories
72 * - deal with inheritable automatically ?
73 * - deal with specific type in spec directly, add all dictionary types to known types
74*/
75
76using namespace std;
77using namespace PoDoFo;
78
79// Utilities
80static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
81 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
82
83 bitmap->allocPixels();
84 bitmap->eraseColor(color);
85}
86
87// TODO(edisonn): synonyms? DeviceRGB and RGB ...
88int GetColorSpaceComponents(const std::string& colorSpace) {
89 if (colorSpace == "DeviceCMYK") {
90 return 4;
91 } else if (colorSpace == "DeviceGray" ||
92 colorSpace == "CalGray" ||
93 colorSpace == "Indexed") {
94 return 1;
95 } else if (colorSpace == "DeviceRGB" ||
96 colorSpace == "CalRGB" ||
97 colorSpace == "Lab") {
98 return 3;
99 } else {
100 return 0;
101 }
102}
103
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000104SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000105 SkMatrix matrix;
106 matrix.setAll(SkDoubleToScalar(array[0]),
107 SkDoubleToScalar(array[2]),
108 SkDoubleToScalar(array[4]),
109 SkDoubleToScalar(array[1]),
110 SkDoubleToScalar(array[3]),
111 SkDoubleToScalar(array[5]),
112 SkDoubleToScalar(0),
113 SkDoubleToScalar(0),
114 SkDoubleToScalar(1));
115
116 return matrix;
117}
118
119SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
120 double array[6];
121
122 // TODO(edisonn): security issue, ret if size() != 6
123 for (int i = 0; i < 6; i++) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000124 const SkPdfObject* elem = pdfArray->operator [](i);
125 if (elem == NULL || (!elem->asNumber() && !elem->asInteger())) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000126 return SkMatrix::I(); // TODO(edisonn): report issue
127 }
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000128 array[i] = elem->asNumber() ? elem->asNumber()->value() : elem->asInteger()->value();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000129 }
130
131 return SkMatrixFromPdfMatrix(array);
132}
133
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000134SkBitmap* gDumpBitmap = NULL;
135SkCanvas* gDumpCanvas = NULL;
136char gLastKeyword[100] = "";
137int gLastOpKeyword = -1;
138char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
139int gReadOp = 0;
140
141
142
143bool hasVisualEffect(const char* pdfOp) {
144 return true;
145 if (*pdfOp == '\0') return false;
146
147 char markedPdfOp[100] = ",";
148 strcat(markedPdfOp, pdfOp);
149 strcat(markedPdfOp, ",");
150
151 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
152}
153
154// TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000155static bool readToken(SkPdfPodofoTokenizer* fTokenizer, PdfToken* token) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000156 bool ret = fTokenizer->readToken(token);
157
158 gReadOp++;
159
160#ifdef PDF_TRACE_DIFF_IN_PNG
161 // TODO(edisonn): compare with old bitmap, and save only new bits are available, and save
162 // the numbar and name of last operation, so the file name will reflect op that changed.
163 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
164 gDumpCanvas->flush();
165
166 SkBitmap bitmap;
167 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
168
169 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
170
171 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
172 SkCanvas canvas(device);
173
174 // draw context stuff here
175 SkPaint blueBorder;
176 blueBorder.setColor(SK_ColorBLUE);
177 blueBorder.setStyle(SkPaint::kStroke_Style);
178 blueBorder.setTextSize(SkDoubleToScalar(20));
179
180 SkString str;
181
182 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
183 if (clipStack) {
184 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
185 const SkClipStack::Element* elem;
186 double y = 0;
187 int total = 0;
188 while (elem = iter.next()) {
189 total++;
190 y += 30;
191
192 switch (elem->getType()) {
193 case SkClipStack::Element::kRect_Type:
194 canvas.drawRect(elem->getRect(), blueBorder);
195 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
196 break;
197 case SkClipStack::Element::kPath_Type:
198 canvas.drawPath(elem->getPath(), blueBorder);
199 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
200 break;
201 case SkClipStack::Element::kEmpty_Type:
202 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
203 break;
204 default:
205 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
206 break;
207 }
208 }
209
210 y += 30;
211 str.printf("Number of clips in stack: %i", total);
212 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
213 }
214
215 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
216 SkPath clipPath;
217 if (clipRegion.getBoundaryPath(&clipPath)) {
218 SkPaint redBorder;
219 redBorder.setColor(SK_ColorRED);
220 redBorder.setStyle(SkPaint::kStroke_Style);
221 canvas.drawPath(clipPath, redBorder);
222 }
223
224 canvas.flush();
225
226 SkString out;
227
228 // TODO(edisonn): get the image, and overlay on top of it, the clip , grafic state, teh stack,
229 // ... and other properties, to be able to debug th code easily
230
231 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", gLastOpKeyword, gLastKeyword);
232 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
233 }
234
235 if (token->fType == kKeyword_TokenType) {
236 strcpy(gLastKeyword, token->fKeyword);
237 gLastOpKeyword = gReadOp;
238 } else {
239 strcpy(gLastKeyword, "");
240 }
241#endif
242
243 return ret;
244}
245
246
247
248typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper**);
249
250map<std::string, PdfOperatorRenderer> gPdfOps;
251
252map<std::string, int> gRenderStats[kCount_PdfResult];
253
254char* gRenderStatsNames[kCount_PdfResult] = {
255 "Success",
256 "Partially implemented",
257 "Not yet implemented",
258 "Ignore Error",
259 "Error",
260 "Unsupported/Unknown"
261};
262
263static SkTypeface* SkTypefaceFromPdfFont(PdfFont* font) {
264 if (font == NULL) {
265 return SkTypeface::CreateFromName("Times New Roman", SkTypeface::kNormal);
266 }
267
268 PdfObject* fontObject = font->GetObject();
269
270 PdfObject* pBaseFont = NULL;
271 // TODO(edisonn): warning, PoDoFo has a bug in PdfFont constructor, does not call InitVars()
272 // for now fixed locally.
273 pBaseFont = fontObject->GetIndirectKey( "BaseFont" );
274 const char* pszBaseFontName = pBaseFont->GetName().GetName().c_str();
275
276#ifdef PDF_TRACE
277 std::string str;
278 fontObject->ToString(str);
279 printf("Base Font Name: %s\n", pszBaseFontName);
280 printf("Font Object Data: %s\n", str.c_str());
281#endif
282
283 SkTypeface* typeface = SkTypefaceFromPdfStandardFont(pszBaseFontName, font->IsBold(), font->IsItalic());
284
285 if (typeface != NULL) {
286 return typeface;
287 }
288
289 char name[1000];
290 // HACK
291 strncpy(name, pszBaseFontName, 1000);
292 char* comma = strstr(name, ",");
293 char* dash = strstr(name, "-");
294 if (comma) *comma = '\0';
295 if (dash) *dash = '\0';
296
297 typeface = SkTypeface::CreateFromName(
298 name,
299 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
300 (font->IsItalic() ? SkTypeface::kItalic : 0)));
301
302 if (typeface != NULL) {
303#ifdef PDF_TRACE
304 printf("HACKED FONT found %s\n", name);
305#endif
306 return typeface;
307 }
308
309#ifdef PDF_TRACE
310 printf("FONT_NOT_FOUND %s\n", pszBaseFontName);
311#endif
312
313 // TODO(edisonn): Report Warning, NYI
314 return SkTypeface::CreateFromName(
315 "Times New Roman",
316 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
317 (font->IsItalic() ? SkTypeface::kItalic : 0)));
318}
319
320PdfResult DrawText(PdfContext* pdfContext,
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000321 const SkPdfObject* _str,
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000322 SkCanvas* canvas)
323{
324
325 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
326 if (skfont == NULL) {
327 skfont = SkPdfFont::Default();
328 }
329
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000330 const SkPdfString* str = _str->asString();
331
332 if (str == NULL) {
333 // TODO(edisonn): report warning
334 return kIgnoreError_PdfResult;
335 }
336
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000337 SkUnencodedText binary(str);
338
339 SkDecodedText decoded;
340
341 if (skfont->encoding() == NULL) {
342 // TODO(edisonn): report warning
343 return kNYI_PdfResult;
344 }
345
346 skfont->encoding()->decodeText(binary, &decoded);
347
348 SkPaint paint;
349 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCurFontSize == 0?
350 // Or maybe just not call setTextSize at all?
351 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
352 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
353 }
354
355// if (fCurFont && fCurFont->GetFontScale() != 0) {
356// paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0));
357// }
358
359 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
360
361 canvas->save();
362
363#if 1
364 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
365
366 SkPoint point1;
367 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0), &point1);
368
369 SkMatrix mirror;
370 mirror.setTranslate(0, -point1.y());
371 // TODO(edisonn): fix rotated text, and skewed too
372 mirror.postScale(SK_Scalar1, -SK_Scalar1);
373 // TODO(edisonn): post rotate, skew
374 mirror.postTranslate(0, point1.y());
375
376 matrix.postConcat(mirror);
377
378 canvas->setMatrix(matrix);
379
380 SkTraceMatrix(matrix, "mirrored");
381#endif
382
edisonn@google.com6e49c342013-06-27 20:03:43 +0000383 skfont->drawText(decoded, &paint, pdfContext, canvas);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000384 canvas->restore();
385
386 return kPartial_PdfResult;
387}
388
389// TODO(edisonn): create header files with declarations!
390PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
391PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
392PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
393PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper);
394
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000395// TODO(edisonn): perf!!!
396
397static SkColorTable* getGrayColortable() {
398 static SkColorTable* grayColortable = NULL;
399 if (grayColortable == NULL) {
400 SkPMColor* colors = new SkPMColor[256];
401 for (int i = 0 ; i < 256; i++) {
402 colors[i] = SkPreMultiplyARGB(255, i, i, i);
403 }
404 grayColortable = new SkColorTable(colors, 256);
405 }
406 return grayColortable;
407}
408
409SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
410 int width, int height, int bytesPerLine,
411 int bpc, const std::string& colorSpace,
412 bool transparencyMask) {
413 SkBitmap bitmap;
414
415 int components = GetColorSpaceComponents(colorSpace);
416//#define MAX_COMPONENTS 10
417
418 int bitsPerLine = width * components * bpc;
419 // TODO(edisonn): assume start of lines are aligned at 32 bits?
420 // Is there a faster way to load the uncompressed stream into a bitmap?
421
422 // minimal support for now
423 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
424 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(SkColor));
425
426 for (int h = 0 ; h < height; h++) {
427 long i = width * (height - 1 - h);
428 for (int w = 0 ; w < width; w++) {
429 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
430 uncompressedStream[3 * w + 1],
431 uncompressedStream[3 * w + 2]);
432 i++;
433 }
434 uncompressedStream += bytesPerLine;
435 }
436
437 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
438 bitmap.setPixels(uncompressedStreamArgb);
439 }
440 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
441 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
442
443 for (int h = 0 ; h < height; h++) {
444 long i = width * (height - 1 - h);
445 for (int w = 0 ; w < width; w++) {
446 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
447 uncompressedStream[w];
448 i++;
449 }
450 uncompressedStream += bytesPerLine;
451 }
452
453 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
454 width, height);
455 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
456 }
457
458 // TODO(edisonn): Report Warning, NYI, or error
459 return bitmap;
460}
461
462bool transferImageStreamToARGB(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
463 int width, int bytesPerLine,
464 int bpc, const std::string& colorSpace,
465 SkColor** uncompressedStreamArgb,
466 pdf_long* uncompressedStreamLengthInBytesArgb) {
467 int components = GetColorSpaceComponents(colorSpace);
468//#define MAX_COMPONENTS 10
469
470 int bitsPerLine = width * components * bpc;
471 // TODO(edisonn): assume start of lines are aligned at 32 bits?
472 int height = uncompressedStreamLength / bytesPerLine;
473
474 // minimal support for now
475 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
476 *uncompressedStreamLengthInBytesArgb = width * height * 4;
477 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
478
479 for (int h = 0 ; h < height; h++) {
480 long i = width * (height - 1 - h);
481 for (int w = 0 ; w < width; w++) {
482 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[3 * w],
483 uncompressedStream[3 * w + 1],
484 uncompressedStream[3 * w + 2]);
485 i++;
486 }
487 uncompressedStream += bytesPerLine;
488 }
489 return true;
490 }
491
492 if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
493 *uncompressedStreamLengthInBytesArgb = width * height * 4;
494 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBytesArgb);
495
496 for (int h = 0 ; h < height; h++) {
497 long i = width * (height - 1 - h);
498 for (int w = 0 ; w < width; w++) {
499 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[w],
500 uncompressedStream[w],
501 uncompressedStream[w]);
502 i++;
503 }
504 uncompressedStream += bytesPerLine;
505 }
506 return true;
507 }
508
509 return false;
510}
511
512// utils
513
514// TODO(edisonn): add cache, or put the bitmap property directly on the PdfObject
515// TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
516// TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
517// skia format, through a table
518
519// this functions returns the image, it does not look at the smask.
520
521SkBitmap getImageFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* image, bool transparencyMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000522 if (image == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000523 // TODO(edisonn): report warning to be used in testing.
524 return SkBitmap();
525 }
526
527 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
528// PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
529// obj.GetDictionary().GetKey(PdfName("Filter")));
530// if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
531// value = resolveReferenceObject(pdfContext->fPdfDoc,
532// &value->GetArray()[0]);
533// }
534// if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
535// SkStream stream = SkStream::
536// SkImageDecoder::Factory()
537// }
538
539 long bpc = image->BitsPerComponent();
540 long width = image->Width();
541 long height = image->Height();
542 std::string colorSpace = "DeviceRGB";
543
544 // TODO(edisonn): color space can be an array too!
545 if (image->isColorSpaceAName()) {
546 colorSpace = image->getColorSpaceAsName();
547 }
548
549/*
550 bool imageMask = image->imageMask();
551
552 if (imageMask) {
553 if (bpc != 0 && bpc != 1) {
554 // TODO(edisonn): report warning to be used in testing.
555 return SkBitmap();
556 }
557 bpc = 1;
558 }
559*/
560
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000561 char* uncompressedStream = NULL;
562 pdf_long uncompressedStreamLength = 0;
563
564 PdfResult ret = kPartial_PdfResult;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000565 SkPdfStream* stream = NULL;
566 image->doc()->mapper()->mapStream(image, &stream);
567
568 if (!stream || !stream->GetFilteredCopy(&uncompressedStream, &uncompressedStreamLength) ||
569 uncompressedStream == NULL || uncompressedStreamLength == 0) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000570 // TODO(edisonn): report warning to be used in testing.
571 return SkBitmap();
572 }
573
574 int bytesPerLine = uncompressedStreamLength / height;
575#ifdef PDF_TRACE
576 if (uncompressedStreamLength % height != 0) {
577 printf("Warning uncompressedStreamLength % height != 0 !!!\n");
578 }
579#endif
580
581 SkBitmap bitmap = transferImageStreamToBitmap(
582 (unsigned char*)uncompressedStream, uncompressedStreamLength,
583 width, height, bytesPerLine,
584 bpc, colorSpace,
585 transparencyMask);
586
587 free(uncompressedStream);
588
589 return bitmap;
590}
591
592SkBitmap getSmaskFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000593 const SkPdfImageDictionary* sMask = obj->SMask();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000594
595 if (sMask) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000596 return getImageFromObject(pdfContext, sMask, true);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000597 }
598
599 // TODO(edisonn): implement GS SMask. Default to empty right now.
600 return pdfContext->fGraphicsState.fSMask;
601}
602
603PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfImageDictionary* skpdfimage) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000604 if (skpdfimage == NULL) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000605 return kIgnoreError_PdfResult;
606 }
607
608 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
609 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
610
611 canvas->save();
612 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
613 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
614
615 if (sMask.empty()) {
616 canvas->drawBitmapRect(image, dst, NULL);
617 } else {
618 canvas->saveLayer(&dst, NULL);
619 canvas->drawBitmapRect(image, dst, NULL);
620 SkPaint xfer;
621 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
622 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
623 canvas->drawBitmapRect(sMask, dst, &xfer);
624 canvas->restore();
625 }
626
627 canvas->restore();
628
629 return kPartial_PdfResult;
630}
631
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000632
633
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000634
635PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000636 if (!skobj) {
637 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000638 }
639
640 PdfOp_q(pdfContext, canvas, NULL);
641 canvas->save();
642
643
644 if (skobj->Resources()) {
645 pdfContext->fGraphicsState.fResources = skobj->Resources();
646 }
647
648 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
649
650 if (skobj->Matrix()) {
651 pdfContext->fGraphicsState.fMatrix.preConcat(*skobj->Matrix());
652 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
653 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
654 // TODO(edisonn) reset matrixTm and matricTlm also?
655 }
656
657 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
658
659 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
660
661 if (skobj->BBox()) {
662 canvas->clipRect(*skobj->BBox(), SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
663 }
664
665 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
666 // For this PdfContentsTokenizer needs to be extended.
667
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000668 SkPdfStream* stream = NULL;
669 skobj->doc()->mapper()->mapStream(skobj, &stream);
670
671 SkPdfPodofoTokenizer* tokenizer = skobj->doc()->tokenizerOfStream(stream);
672 if (tokenizer != NULL) {
673 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
674 looper.loop();
675 delete tokenizer;
676 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000677
678 // TODO(edisonn): should we restore the variable stack at the same state?
679 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
680 canvas->restore();
681 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000682 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000683}
684
685PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject& obj) {
686 return kNYI_PdfResult;
687}
688
689PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, SkPdfObject* skobj, SkRect bBox, SkMatrix matrix, double textSize) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000690 if (!skobj) {
691 return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000692 }
693
694 PdfOp_q(pdfContext, canvas, NULL);
695 canvas->save();
696
697 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
698 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
699
700 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm;
701 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
702
703 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
704
705 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
706
707 SkRect rm = bBox;
708 pdfContext->fGraphicsState.fMatrix.mapRect(&rm);
709
710 SkTraceRect(rm, "bbox mapped");
711
712 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
713
714 // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go.
715 // For this PdfContentsTokenizer needs to be extended.
716
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000717 SkPdfStream* stream = NULL;
718 skobj->doc()->mapper()->mapStream(skobj, &stream);
719
720 SkPdfPodofoTokenizer* tokenizer = skobj->doc()->tokenizerOfStream(stream);
721 if (tokenizer != NULL) {
722 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas);
723 looper.loop();
724 delete tokenizer;
725 }
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000726
727 // TODO(edisonn): should we restore the variable stack at the same state?
728 // There could be operands left, that could be consumed by a parent tokenizer when we pop.
729 canvas->restore();
730 PdfOp_Q(pdfContext, canvas, NULL);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000731
732 return kPartial_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000733}
734
735
736// TODO(edisonn): faster, have the property on the SkPdfObject itself?
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000737std::set<const void*> gInRendering;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000738
739class CheckRecursiveRendering {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000740 const void* fUniqueData;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000741public:
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000742 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj->data()) {
743 gInRendering.insert(obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000744 }
745
746 ~CheckRecursiveRendering() {
747 //SkASSERT(fObj.fInRendering);
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000748 gInRendering.erase(fUniqueData);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000749 }
750
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000751 static bool IsInRendering(const SkPdfObject* obj) {
752 return gInRendering.find(obj->data()) != gInRendering.end();
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000753 }
754};
755
756PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject& obj) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000757 if (CheckRecursiveRendering::IsInRendering(&obj)) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000758 // Oops, corrupt PDF!
759 return kIgnoreError_PdfResult;
760 }
761
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000762 CheckRecursiveRendering checkRecursion(&obj);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000763
764 // TODO(edisonn): check type
765 SkPdfXObjectDictionary* skobj = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000766 if (!obj.doc()->mapper()->mapXObjectDictionary(&obj, &skobj)) return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000767
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000768 if (!skobj) return kIgnoreError_PdfResult;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000769
770 PdfResult ret = kIgnoreError_PdfResult;
771 switch (skobj->getType())
772 {
773 case kImageDictionary_SkPdfObjectType:
774 ret = doXObject_Image(pdfContext, canvas, skobj->asImageDictionary());
775 break;
776 case kType1FormDictionary_SkPdfObjectType:
777 ret = doXObject_Form(pdfContext, canvas, skobj->asType1FormDictionary());
778 break;
779 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
780 //return doXObject_PS(skxobj.asPS());
781 }
782
783 delete skobj;
784 return ret;
785}
786
787PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
788 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
789 canvas->save();
790 return kOK_PdfResult;
791}
792
793PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
794 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
795 pdfContext->fStateStack.pop();
796 canvas->restore();
797 return kOK_PdfResult;
798}
799
800PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
801 double array[6];
802 for (int i = 0 ; i < 6 ; i++) {
803 array[5 - i] = pdfContext->fObjectStack.top()->asNumber()->value();
804 pdfContext->fObjectStack.pop();
805 }
806
807 // a b
808 // c d
809 // e f
810
811 // 0 1
812 // 2 3
813 // 4 5
814
815 // sx ky
816 // kx sy
817 // tx ty
818 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
819
820 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
821
822#ifdef PDF_TRACE
823 printf("cm ");
824 for (int i = 0 ; i < 6 ; i++) {
825 printf("%f ", array[i]);
826 }
827 printf("\n");
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000828 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "cm");
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000829#endif
830
831 return kOK_PdfResult;
832}
833
834//leading TL Set the text leading, Tl
835//, to leading, which is a number expressed in unscaled text
836//space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
837PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
838 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
839
840 pdfContext->fGraphicsState.fTextLeading = ty;
841
842 return kOK_PdfResult;
843}
844
845PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
846 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
847 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
848
849 double array[6] = {1, 0, 0, 1, tx, ty};
850 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
851
852 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
853 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
854
855 return kPartial_PdfResult;
856}
857
858PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
859 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
860 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
861
862 // TODO(edisonn): Create factory methods or constructors so podofo is hidden
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000863 SkPdfNumber* _ty = pdfContext->fPdfDoc->createNumber(-ty);
864 pdfContext->fObjectStack.push(_ty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000865
866 PdfOp_TL(pdfContext, canvas, looper);
867
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000868 SkPdfNumber* vtx = pdfContext->fPdfDoc->createNumber(tx);
869 pdfContext->fObjectStack.push(vtx);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000870
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000871 SkPdfNumber* vty = pdfContext->fPdfDoc->createNumber(ty);
872 pdfContext->fObjectStack.push(vty);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000873
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000874 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
875
876 // TODO(edisonn): delete all the objects after rendering was complete, in this way pdf is rendered faster
877 // and the cleanup can happen while the user looks at the image
878 delete _ty;
879 delete vtx;
880 delete vty;
881
882 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000883}
884
885PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
886 double f = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
887 double e = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
888 double d = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
889 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
890 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
891 double a = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
892
893 double array[6];
894 array[0] = a;
895 array[1] = b;
896 array[2] = c;
897 array[3] = d;
898 array[4] = e;
899 array[5] = f;
900
901 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
902 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
903
904 // TODO(edisonn): Text positioning.
905 pdfContext->fGraphicsState.fMatrixTm = matrix;
906 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
907
908 return kPartial_PdfResult;
909}
910
911//— T* Move to the start of the next line. This operator has the same effect as the code
912//0 Tl Td
913//where Tl is the current leading parameter in the text state
914PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000915 SkPdfNumber* zero = pdfContext->fPdfDoc->createNumber(0.0);
916 SkPdfNumber* tl = pdfContext->fPdfDoc->createNumber(pdfContext->fGraphicsState.fTextLeading);
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000917
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000918 pdfContext->fObjectStack.push(zero);
919 pdfContext->fObjectStack.push(tl);
920
921 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper);
922
923 delete zero; // TODO(edisonn): do not alocate and delete constants!
924 delete tl;
925
926 return ret;
edisonn@google.com131d4ee2013-06-26 17:48:12 +0000927}
928
929PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
930 if (pdfContext->fGraphicsState.fPathClosed) {
931 pdfContext->fGraphicsState.fPath.reset();
932 pdfContext->fGraphicsState.fPathClosed = false;
933 }
934
935 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
936 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
937
938 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
939 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
940
941 return kOK_PdfResult;
942}
943
944PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
945 if (pdfContext->fGraphicsState.fPathClosed) {
946 pdfContext->fGraphicsState.fPath.reset();
947 pdfContext->fGraphicsState.fPathClosed = false;
948 }
949
950 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
951 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
952
953 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
954 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
955
956 return kOK_PdfResult;
957}
958
959PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
960 if (pdfContext->fGraphicsState.fPathClosed) {
961 pdfContext->fGraphicsState.fPath.reset();
962 pdfContext->fGraphicsState.fPathClosed = false;
963 }
964
965 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
966 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
967 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
968 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
969 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
970 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
971
972 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
973 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
974 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
975
976 pdfContext->fGraphicsState.fCurPosX = x3;
977 pdfContext->fGraphicsState.fCurPosY = y3;
978
979 return kOK_PdfResult;
980}
981
982PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
983 if (pdfContext->fGraphicsState.fPathClosed) {
984 pdfContext->fGraphicsState.fPath.reset();
985 pdfContext->fGraphicsState.fPathClosed = false;
986 }
987
988 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
989 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
990 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
991 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
992 double y1 = pdfContext->fGraphicsState.fCurPosY;
993 double x1 = pdfContext->fGraphicsState.fCurPosX;
994
995 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
996 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
997 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
998
999 pdfContext->fGraphicsState.fCurPosX = x3;
1000 pdfContext->fGraphicsState.fCurPosY = y3;
1001
1002 return kOK_PdfResult;
1003}
1004
1005PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1006 if (pdfContext->fGraphicsState.fPathClosed) {
1007 pdfContext->fGraphicsState.fPath.reset();
1008 pdfContext->fGraphicsState.fPathClosed = false;
1009 }
1010
1011 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1012 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1013 double y2 = pdfContext->fGraphicsState.fCurPosY;
1014 double x2 = pdfContext->fGraphicsState.fCurPosX;
1015 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1016 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1017
1018 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1019 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1020 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1021
1022 pdfContext->fGraphicsState.fCurPosX = x3;
1023 pdfContext->fGraphicsState.fCurPosY = y3;
1024
1025 return kOK_PdfResult;
1026}
1027
1028PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1029 if (pdfContext->fGraphicsState.fPathClosed) {
1030 pdfContext->fGraphicsState.fPath.reset();
1031 pdfContext->fGraphicsState.fPathClosed = false;
1032 }
1033
1034 double height = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1035 double width = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1036 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1037 double x = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1038
1039 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScalar(y),
1040 SkDoubleToScalar(x + width), SkDoubleToScalar(y + height));
1041
1042 pdfContext->fGraphicsState.fCurPosX = x;
1043 pdfContext->fGraphicsState.fCurPosY = y + height;
1044
1045 return kOK_PdfResult;
1046}
1047
1048PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1049 pdfContext->fGraphicsState.fPath.close();
1050 return kOK_PdfResult;
1051}
1052
1053PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fill, bool stroke, bool close, bool evenOdd) {
1054 SkPath path = pdfContext->fGraphicsState.fPath;
1055
1056 if (close) {
1057 path.close();
1058 }
1059
1060 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1061
1062 SkPaint paint;
1063
1064 SkPoint line[2];
1065 if (fill && !stroke && path.isLine(line)) {
1066 paint.setStyle(SkPaint::kStroke_Style);
1067
1068 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1069 paint.setStrokeWidth(SkDoubleToScalar(0));
1070
1071 canvas->drawPath(path, paint);
1072 } else {
1073 if (fill) {
1074 paint.setStyle(SkPaint::kFill_Style);
1075 if (evenOdd) {
1076 path.setFillType(SkPath::kEvenOdd_FillType);
1077 }
1078
1079 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1080
1081 canvas->drawPath(path, paint);
1082 }
1083
1084 if (stroke) {
1085 paint.setStyle(SkPaint::kStroke_Style);
1086
1087 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1088
1089 path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke
1090 canvas->drawPath(path, paint);
1091 }
1092 }
1093
1094 pdfContext->fGraphicsState.fPath.reset();
1095 // todo zoom ... other stuff ?
1096
1097 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1098#ifndef PDF_DEBUG_NO_CLIPING
1099 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1100#endif
1101 }
1102
1103 //pdfContext->fGraphicsState.fClipPath.reset();
1104 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1105
1106 return kPartial_PdfResult;
1107
1108}
1109
1110PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1111 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1112}
1113
1114PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1115 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1116}
1117
1118PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1119 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1120}
1121
1122PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1123 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1124}
1125
1126PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1127 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1128}
1129
1130PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1131 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1132}
1133
1134PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1135 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1136}
1137
1138PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1139 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1140}
1141
1142PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1143 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1144}
1145
1146PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1147 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1148 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1149#ifndef PDF_DEBUG_NO_CLIPING
1150 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1151#endif
1152 }
1153
1154 //pdfContext->fGraphicsState.fClipPath.reset();
1155 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1156
1157 pdfContext->fGraphicsState.fPathClosed = true;
1158
1159 return kOK_PdfResult;
1160}
1161
1162PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1163 pdfContext->fGraphicsState.fTextBlock = true;
1164 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1165 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1166
1167 return kPartial_PdfResult;
1168}
1169
1170PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1171 if (!pdfContext->fGraphicsState.fTextBlock) {
1172 return kIgnoreError_PdfResult;
1173 }
1174 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1175 return kPartial_PdfResult;
1176}
1177
1178//font size Tf Set the text font, Tf
1179//, to font and the text font size, Tfs, to size. font is the name of a
1180//font resource in the Fontsubdictionary of the current resource dictionary; size is
1181//a number representing a scale factor. There is no initial value for either font or
1182//size; they must be specified explicitly using Tf before any text is shown.
1183PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1184 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1185 std::string fontName = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1186
1187#ifdef PDF_TRACE
1188 printf("font name: %s\n", fontName.c_str());
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001189#endif
1190
1191 SkPdfFontDictionary* fd = NULL;
1192 if (pdfContext->fGraphicsState.fResources->Font()) {
1193 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font()->get(fontName.c_str());
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001194 objFont->doc()->mapper()->mapFontDictionary(objFont, &fd);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001195 }
1196
1197 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(fd);
1198
1199 if (skfont) {
1200 pdfContext->fGraphicsState.fSkFont = skfont;
1201 }
1202
1203 return kPartial_PdfResult;
1204}
1205
1206PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1207 if (!pdfContext->fGraphicsState.fTextBlock) {
1208 // TODO(edisonn): try to recover and draw it any way?
1209 return kIgnoreError_PdfResult;
1210 }
1211
1212 PdfResult ret = DrawText(pdfContext,
1213 pdfContext->fObjectStack.top(),
1214 canvas);
1215 pdfContext->fObjectStack.pop();
1216
1217 return ret;
1218}
1219
1220PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1221 if (!pdfContext->fGraphicsState.fTextBlock) {
1222 // TODO(edisonn): try to recover and draw it any way?
1223 return kIgnoreError_PdfResult;
1224 }
1225
1226 PdfOp_T_star(pdfContext, canvas, looper);
1227 // Do not pop, and push, just transfer the param to Tj
1228 return PdfOp_Tj(pdfContext, canvas, looper);
1229}
1230
1231PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1232 if (!pdfContext->fGraphicsState.fTextBlock) {
1233 // TODO(edisonn): try to recover and draw it any way?
1234 return kIgnoreError_PdfResult;
1235 }
1236
1237 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1238 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1239 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1240
1241 pdfContext->fObjectStack.push(aw);
1242 PdfOp_Tw(pdfContext, canvas, looper);
1243
1244 pdfContext->fObjectStack.push(ac);
1245 PdfOp_Tc(pdfContext, canvas, looper);
1246
1247 pdfContext->fObjectStack.push(str);
1248 PdfOp_quote(pdfContext, canvas, looper);
1249
1250 return kPartial_PdfResult;
1251}
1252
1253PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1254 if (!pdfContext->fGraphicsState.fTextBlock) {
1255 // TODO(edisonn): try to recover and draw it any way?
1256 return kIgnoreError_PdfResult;
1257 }
1258
1259 SkPdfArray* array = pdfContext->fObjectStack.top()->asArray();
1260 pdfContext->fObjectStack.pop();
1261
1262 for( int i=0; i<static_cast<int>(array->size()); i++ )
1263 {
1264 if( (*array)[i]->asString()) {
1265 SkPdfObject* obj = (*array)[i];
1266 DrawText(pdfContext,
1267 obj,
1268 canvas);
1269 } else if ((*array)[i]->asInteger() || (*array)[i]->asNumber()) {
1270 double dx = (*array)[i]->asNumber()->value();
1271 SkMatrix matrix;
1272 matrix.setAll(SkDoubleToScalar(1),
1273 SkDoubleToScalar(0),
1274 // TODO(edisonn): use writing mode, vertical/horizontal.
1275 SkDoubleToScalar(-dx), // amount is substracted!!!
1276 SkDoubleToScalar(0),
1277 SkDoubleToScalar(1),
1278 SkDoubleToScalar(0),
1279 SkDoubleToScalar(0),
1280 SkDoubleToScalar(0),
1281 SkDoubleToScalar(1));
1282
1283 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1284 }
1285 }
1286 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1287}
1288
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001289PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001290 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1291 return kOK_PdfResult;
1292}
1293
1294PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1295 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1296}
1297
1298PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1299 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1300}
1301
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001302PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001303 double c[4];
1304 pdf_int64 v[4];
1305
1306 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1307
1308 bool doubles = true;
1309 if (colorOperator->fColorSpace == "Indexed") {
1310 doubles = false;
1311 }
1312
1313#ifdef PDF_TRACE
1314 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.c_str(), n);
1315#endif
1316
1317 for (int i = n - 1; i >= 0 ; i--) {
1318 if (doubles) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001319 c[i] = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001320 } else {
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001321 v[i] = pdfContext->fObjectStack.top()->asInteger()->value(); pdfContext->fObjectStack.pop();
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001322 }
1323 }
1324
1325 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
1326 if (colorOperator->fColorSpace == "DeviceRGB") {
1327 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
1328 }
1329 return kPartial_PdfResult;
1330}
1331
1332PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1333 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1334}
1335
1336PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1337 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1338}
1339
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001340PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001341 PdfString name;
1342
1343 if (pdfContext->fObjectStack.top()->asName()) {
1344 pdfContext->fObjectStack.pop();
1345 }
1346
1347 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1348 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1349
1350 return kPartial_PdfResult;
1351}
1352
1353PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1354 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1355}
1356
1357PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1358 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1359}
1360
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001361PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001362 double gray = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1363 return kNYI_PdfResult;
1364}
1365
1366PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1367 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1368}
1369
1370PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1371 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1372}
1373
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001374PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001375 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1376 double g = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1377 double r = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1378
1379 colorOperator->fColorSpace = "DeviceRGB";
1380 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
1381 return kOK_PdfResult;
1382}
1383
1384PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1385 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1386}
1387
1388PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1389 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1390}
1391
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001392PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorOperator* colorOperator) {
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001393 // TODO(edisonn): spec has some rules about overprint, implement them.
1394 double k = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1395 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1396 double m = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1397 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1398
1399 colorOperator->fColorSpace = "DeviceCMYK";
1400 // TODO(edisonn): Set color.
1401 return kNYI_PdfResult;
1402}
1403
1404PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1405 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1406}
1407
1408PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1409 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1410}
1411
1412PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1413 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1414 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1415
1416 return kOK_PdfResult;
1417}
1418
1419PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1420 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1421
1422#ifdef PDF_TRACE
1423 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
1424 printf("CLIP IS RECT\n");
1425 }
1426#endif
1427
1428 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even odd.
1429 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1430 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1431
1432 return kPartial_PdfResult;
1433}
1434
1435PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1436 *looper = new PdfCompatibilitySectionLooper();
1437 return kOK_PdfResult;
1438}
1439
1440PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1441#ifdef ASSERT_BAD_PDF_OPS
1442 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, but let's
1443 // have the assert when testing good pdfs.
1444#endif
1445 return kIgnoreError_PdfResult;
1446}
1447
1448PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1449 *looper = new PdfInlineImageLooper();
1450 return kOK_PdfResult;
1451}
1452
1453PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1454#ifdef ASSERT_BAD_PDF_OPS
1455 SkASSERT(false); // must be processed in inline image looper, but let's
1456 // have the assert when testing good pdfs.
1457#endif
1458 return kIgnoreError_PdfResult;
1459}
1460
1461PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1462#ifdef ASSERT_BAD_PDF_OPS
1463 SkASSERT(false); // must be processed in inline image looper, but let's
1464 // have the assert when testing good pdfs.
1465#endif
1466 return kIgnoreError_PdfResult;
1467}
1468
1469//lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1470PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1471 double lineWidth = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1472 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1473
1474 return kOK_PdfResult;
1475}
1476
1477//lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
1478PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1479 pdfContext->fObjectStack.pop();
1480 //double lineCap = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1481
1482 return kNYI_PdfResult;
1483}
1484
1485//lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
1486PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1487 pdfContext->fObjectStack.pop();
1488 //double lineJoin = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1489
1490 return kNYI_PdfResult;
1491}
1492
1493//miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
1494PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1495 pdfContext->fObjectStack.pop();
1496 //double miterLimit = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1497
1498 return kNYI_PdfResult;
1499}
1500
1501//dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
1502//page 155).
1503PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1504 pdfContext->fObjectStack.pop();
1505 pdfContext->fObjectStack.pop();
1506
1507 return kNYI_PdfResult;
1508}
1509
1510//intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
1511PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1512 pdfContext->fObjectStack.pop();
1513
1514 return kNYI_PdfResult;
1515}
1516
1517//flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1518//Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1519//fies the output device’s default flatness tolerance.
1520PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1521 pdfContext->fObjectStack.pop();
1522
1523 return kNYI_PdfResult;
1524}
1525
1526//dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
1527//the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
1528PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1529 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1530
1531#ifdef PDF_TRACE
1532 std::string str;
1533#endif
1534
1535 //Next, get the ExtGState Dictionary from the Resource Dictionary:
1536 const SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState();
1537
1538 if (extGStateDictionary == NULL) {
1539#ifdef PDF_TRACE
1540 printf("ExtGState is NULL!\n");
1541#endif
1542 return kIgnoreError_PdfResult;
1543 }
1544
1545 SkPdfObject* value = extGStateDictionary->get(name.c_str());
1546
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001547 SkPdfGraphicsStateDictionary* gs = NULL;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001548 value->doc()->mapper()->mapGraphicsStateDictionary(value, &gs);
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001549
1550 // TODO(edisonn): now load all those properties in graphic state.
1551 if (gs == NULL) {
1552 return kIgnoreError_PdfResult;
1553 }
1554
1555 if (gs->has_CA()) {
1556 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA();
1557 }
1558
1559 if (gs->has_ca()) {
1560 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca();
1561 }
1562
1563 if (gs->has_LW()) {
1564 pdfContext->fGraphicsState.fLineWidth = gs->LW();
1565 }
1566
1567
1568
1569 return kNYI_PdfResult;
1570}
1571
1572//charSpace Tc Set the character spacing, Tc
1573//, to charSpace, which is a number expressed in unscaled text space units. Character spacing is used by the Tj, TJ, and ' operators.
1574//Initial value: 0.
1575PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1576 double charSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1577 pdfContext->fGraphicsState.fCharSpace = charSpace;
1578
1579 return kOK_PdfResult;
1580}
1581
1582//wordSpace Tw Set the word spacing, T
1583//w
1584//, to wordSpace, which is a number expressed in unscaled
1585//text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
1586//value: 0.
1587PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1588 double wordSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1589 pdfContext->fGraphicsState.fWordSpace = wordSpace;
1590
1591 return kOK_PdfResult;
1592}
1593
1594//scale Tz Set the horizontal scaling, Th
1595//, to (scale ˜ 100). scale is a number specifying the
1596//percentage of the normal width. Initial value: 100 (normal width).
1597PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1598 double scale = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1599
1600 return kNYI_PdfResult;
1601}
1602
1603//render Tr Set the text rendering mode, T
1604//mode, to render, which is an integer. Initial value: 0.
1605PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1606 double render = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1607
1608 return kNYI_PdfResult;
1609}
1610//rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
1611//units. Initial value: 0.
1612PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1613 double rise = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1614
1615 return kNYI_PdfResult;
1616}
1617
1618//wx wy d0
1619PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1620 pdfContext->fObjectStack.pop();
1621 pdfContext->fObjectStack.pop();
1622
1623 return kNYI_PdfResult;
1624}
1625
1626//wx wy llx lly urx ury d1
1627PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1628 pdfContext->fObjectStack.pop();
1629 pdfContext->fObjectStack.pop();
1630 pdfContext->fObjectStack.pop();
1631 pdfContext->fObjectStack.pop();
1632 pdfContext->fObjectStack.pop();
1633 pdfContext->fObjectStack.pop();
1634
1635 return kNYI_PdfResult;
1636}
1637
1638//name sh
1639PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1640 pdfContext->fObjectStack.pop();
1641
1642 return kNYI_PdfResult;
1643}
1644
1645//name Do
1646PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1647 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1648
1649 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject();
1650
1651 if (xObject == NULL) {
1652#ifdef PDF_TRACE
1653 printf("XObject is NULL!\n");
1654#endif
1655 return kIgnoreError_PdfResult;
1656 }
1657
1658 SkPdfObject* value = xObject->get(name.c_str());
1659
1660#ifdef PDF_TRACE
1661// value->ToString(str);
1662// printf("Do object value: %s\n", str.c_str());
1663#endif
1664
1665 return doXObject(pdfContext, canvas, *value);
1666}
1667
1668//tag MP Designate a marked-content point. tag is a name object indicating the role or
1669//significance of the point.
1670PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1671 pdfContext->fObjectStack.pop();
1672
1673 return kNYI_PdfResult;
1674}
1675
1676//tag properties DP Designate a marked-content point with an associated property list. tag is a
1677//name object indicating the role or significance of the point; properties is
1678//either an inline dictionary containing the property list or a name object
1679//associated with it in the Properties subdictionary of the current resource
1680//dictionary (see Section 9.5.1, “Property Lists”).
1681PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1682 pdfContext->fObjectStack.pop();
1683 pdfContext->fObjectStack.pop();
1684
1685 return kNYI_PdfResult;
1686}
1687
1688//tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
1689//tag is a name object indicating the role or significance of the sequence.
1690PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1691 pdfContext->fObjectStack.pop();
1692
1693 return kNYI_PdfResult;
1694}
1695
1696//tag properties BDC Begin a marked-content sequence with an associated property list, terminated
1697//by a balancing EMCoperator. tag is a name object indicating the role or significance of the sequence; propertiesis either an inline dictionary containing the
1698//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”).
1699PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1700 pdfContext->fObjectStack.pop();
1701 pdfContext->fObjectStack.pop();
1702
1703 return kNYI_PdfResult;
1704}
1705
1706//— EMC End a marked-content sequence begun by a BMC or BDC operator.
1707PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1708 return kNYI_PdfResult;
1709}
1710
1711void initPdfOperatorRenderes() {
1712 static bool gInitialized = false;
1713 if (gInitialized) {
1714 return;
1715 }
1716
1717 gPdfOps["q"] = PdfOp_q;
1718 gPdfOps["Q"] = PdfOp_Q;
1719 gPdfOps["cm"] = PdfOp_cm;
1720
1721 gPdfOps["TD"] = PdfOp_TD;
1722 gPdfOps["Td"] = PdfOp_Td;
1723 gPdfOps["Tm"] = PdfOp_Tm;
1724 gPdfOps["T*"] = PdfOp_T_star;
1725
1726 gPdfOps["m"] = PdfOp_m;
1727 gPdfOps["l"] = PdfOp_l;
1728 gPdfOps["c"] = PdfOp_c;
1729 gPdfOps["v"] = PdfOp_v;
1730 gPdfOps["y"] = PdfOp_y;
1731 gPdfOps["h"] = PdfOp_h;
1732 gPdfOps["re"] = PdfOp_re;
1733
1734 gPdfOps["S"] = PdfOp_S;
1735 gPdfOps["s"] = PdfOp_s;
1736 gPdfOps["f"] = PdfOp_f;
1737 gPdfOps["F"] = PdfOp_F;
1738 gPdfOps["f*"] = PdfOp_f_star;
1739 gPdfOps["B"] = PdfOp_B;
1740 gPdfOps["B*"] = PdfOp_B_star;
1741 gPdfOps["b"] = PdfOp_b;
1742 gPdfOps["b*"] = PdfOp_b_star;
1743 gPdfOps["n"] = PdfOp_n;
1744
1745 gPdfOps["BT"] = PdfOp_BT;
1746 gPdfOps["ET"] = PdfOp_ET;
1747
1748 gPdfOps["Tj"] = PdfOp_Tj;
1749 gPdfOps["'"] = PdfOp_quote;
1750 gPdfOps["\""] = PdfOp_doublequote;
1751 gPdfOps["TJ"] = PdfOp_TJ;
1752
1753 gPdfOps["CS"] = PdfOp_CS;
1754 gPdfOps["cs"] = PdfOp_cs;
1755 gPdfOps["SC"] = PdfOp_SC;
1756 gPdfOps["SCN"] = PdfOp_SCN;
1757 gPdfOps["sc"] = PdfOp_sc;
1758 gPdfOps["scn"] = PdfOp_scn;
1759 gPdfOps["G"] = PdfOp_G;
1760 gPdfOps["g"] = PdfOp_g;
1761 gPdfOps["RG"] = PdfOp_RG;
1762 gPdfOps["rg"] = PdfOp_rg;
1763 gPdfOps["K"] = PdfOp_K;
1764 gPdfOps["k"] = PdfOp_k;
1765
1766 gPdfOps["W"] = PdfOp_W;
1767 gPdfOps["W*"] = PdfOp_W_star;
1768
1769 gPdfOps["BX"] = PdfOp_BX;
1770 gPdfOps["EX"] = PdfOp_EX;
1771
1772 gPdfOps["BI"] = PdfOp_BI;
1773 gPdfOps["ID"] = PdfOp_ID;
1774 gPdfOps["EI"] = PdfOp_EI;
1775
1776 gPdfOps["w"] = PdfOp_w;
1777 gPdfOps["J"] = PdfOp_J;
1778 gPdfOps["j"] = PdfOp_j;
1779 gPdfOps["M"] = PdfOp_M;
1780 gPdfOps["d"] = PdfOp_d;
1781 gPdfOps["ri"] = PdfOp_ri;
1782 gPdfOps["i"] = PdfOp_i;
1783 gPdfOps["gs"] = PdfOp_gs;
1784
1785 gPdfOps["Tc"] = PdfOp_Tc;
1786 gPdfOps["Tw"] = PdfOp_Tw;
1787 gPdfOps["Tz"] = PdfOp_Tz;
1788 gPdfOps["TL"] = PdfOp_TL;
1789 gPdfOps["Tf"] = PdfOp_Tf;
1790 gPdfOps["Tr"] = PdfOp_Tr;
1791 gPdfOps["Ts"] = PdfOp_Ts;
1792
1793 gPdfOps["d0"] = PdfOp_d0;
1794 gPdfOps["d1"] = PdfOp_d1;
1795
1796 gPdfOps["sh"] = PdfOp_sh;
1797
1798 gPdfOps["Do"] = PdfOp_Do;
1799
1800 gPdfOps["MP"] = PdfOp_MP;
1801 gPdfOps["DP"] = PdfOp_DP;
1802 gPdfOps["BMC"] = PdfOp_BMC;
1803 gPdfOps["BDC"] = PdfOp_BDC;
1804 gPdfOps["EMC"] = PdfOp_EMC;
1805
1806 gInitialized = true;
1807}
1808
1809class InitPdfOps {
1810public:
1811 InitPdfOps() {
1812 initPdfOperatorRenderes();
1813 }
1814};
1815
1816InitPdfOps gInitPdfOps;
1817
1818void reportPdfRenderStats() {
1819 std::map<std::string, int>::iterator iter;
1820
1821 for (int i = 0 ; i < kCount_PdfResult; i++) {
1822 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++iter) {
1823 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_str(), iter->second);
1824 }
1825 }
1826}
1827
1828PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
1829 if (token.fType == kKeyword_TokenType)
1830 {
1831 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
1832 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[token.fKeyword];
1833 if (pdfOperatorRenderer) {
1834 // caller, main work is done by pdfOperatorRenderer(...)
1835 PdfTokenLooper* childLooper = NULL;
1836 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)][token.fKeyword]++;
1837
1838 if (childLooper) {
1839 childLooper->setUp(this);
1840 childLooper->loop();
1841 delete childLooper;
1842 }
1843 } else {
1844 gRenderStats[kUnsupported_PdfResult][token.fKeyword]++;
1845 }
1846 }
1847 else if (token.fType == kObject_TokenType)
1848 {
1849 fPdfContext->fObjectStack.push( token.fObject );
1850 }
1851 else if ( token.fType == kImageData_TokenType) {
1852 // TODO(edisonn): implement inline image.
1853 }
1854 else {
1855 return kIgnoreError_PdfResult;
1856 }
1857 return kOK_PdfResult;
1858}
1859
1860void PdfMainLooper::loop() {
1861 PdfToken token;
1862 while (readToken(fTokenizer, &token)) {
1863 consumeToken(token);
1864 }
1865}
1866
1867PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
1868 //pdfContext.fInlineImage.fKeyValuePairs[key] = value;
1869 return kNYI_PdfResult;
1870}
1871
1872void PdfInlineImageLooper::loop() {
1873 PdfToken token;
1874 while (readToken(fTokenizer, &token)) {
1875 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
1876 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
1877 looper->setUp(this);
1878 looper->loop();
1879 } else {
1880 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI") == 0) {
1881 done();
1882 return;
1883 }
1884
1885 consumeToken(token);
1886 }
1887 }
1888 // TODO(edisonn): report error/warning, EOF without EI.
1889}
1890
1891PdfResult PdfInlineImageLooper::done() {
1892
1893 // TODO(edisonn): long to short names
1894 // TODO(edisonn): set properties in a map
1895 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress
1896 // the stream.
1897
1898 SkBitmap bitmap;
1899 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
1900
1901 // TODO(edisonn): matrix use.
1902 // Draw dummy red square, to show the prezence of the inline image.
1903 fCanvas->drawBitmap(bitmap,
1904 SkDoubleToScalar(0),
1905 SkDoubleToScalar(0),
1906 NULL);
1907 return kNYI_PdfResult;
1908}
1909
1910PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
1911 return fParent->consumeToken(token);
1912}
1913
1914void PdfCompatibilitySectionLooper::loop() {
1915 // TODO(edisonn): save stacks position, or create a new stack?
1916 // TODO(edisonn): what happens if we pop out more variables then when we started?
1917 // restore them? fail? We could create a new operands stack for every new BX/EX section,
1918 // pop-ing too much will not affect outside the section.
1919 PdfToken token;
1920 while (readToken(fTokenizer, &token)) {
1921 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
1922 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
1923 looper->setUp(this);
1924 looper->loop();
1925 delete looper;
1926 } else {
1927 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) break;
1928 fParent->consumeToken(token);
1929 }
1930 }
1931 // TODO(edisonn): restore stack.
1932}
1933
1934// TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
1935// TODO(edisonn): Add API for Forms viewing and editing
1936// e.g. SkBitmap getPage(int page);
1937// int formsCount();
1938// SkForm getForm(int formID); // SkForm(SkRect, .. other data)
1939// TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsing all content, ...
1940// if we load the first page, and we zoom to fit to screen horizontally, then load only those
1941// resources needed, so the preview is fast.
1942// TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
1943// references automatically.
1944
1945bool SkPdfViewer::load(const SkString inputFileName, SkPicture* out) {
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001946 std::cout << "Init: " << inputFileName.c_str() << std::endl;
1947
1948 SkPodofoParsedPDF* doc = new SkPodofoParsedPDF(inputFileName.c_str());
1949 if (!doc->pages())
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001950 {
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001951 std::cout << "ERROR: Empty Document" << inputFileName.c_str() << std::endl;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001952 return false;
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001953 } else {
1954
1955 for (int pn = 0; pn < doc->pages(); ++pn) {
1956 // TODO(edisonn): implement inheritance properties as per PDF spec
1957 //SkRect rect = page->MediaBox();
1958 SkRect rect = doc->MediaBox(pn);
1959
1960#ifdef PDF_TRACE
1961 printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rect.width()), SkScalarToDouble(rect.height()));
1962#endif
1963
1964 // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to use?
1965
1966 SkBitmap bitmap;
1967#ifdef PDF_DEBUG_3X
1968 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()));
1969#else
1970 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
1971#endif
1972 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
1973 SkCanvas canvas(device);
1974
1975 gDumpBitmap = &bitmap;
1976
1977 gDumpCanvas = &canvas;
1978 doc->drawPage(pn, &canvas);
1979
1980 SkString out;
1981 if (doc->pages() > 1) {
1982 out.appendf("%s-%i.png", inputFileName.c_str(), pn);
1983 } else {
1984 out = inputFileName;
1985 // .pdf -> .png
1986 out[out.size() - 2] = 'n';
1987 out[out.size() - 1] = 'g';
1988 }
1989 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
1990 }
1991 return true;
edisonn@google.com131d4ee2013-06-26 17:48:12 +00001992 }
1993
1994 return true;
1995}