Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(119)

Side by Side Diff: experimental/PdfViewer/pdf_viewer_main.cpp

Issue 17856004: refactoring for pdf viewer lib (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « experimental/PdfViewer/generate_code.py ('k') | experimental/PdfViewer/spec2def.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /*
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" 1 #include "SkCanvas.h"
9 #include "SkDevice.h" 2 #include "SkDevice.h"
10 #include "SkForceLinking.h" 3 #include "SkForceLinking.h"
11 #include "SkGraphics.h" 4 #include "SkGraphics.h"
12 #include "SkImageDecoder.h" 5 #include "SkImageDecoder.h"
13 #include "SkImageEncoder.h" 6 #include "SkImageEncoder.h"
14 #include "SkOSFile.h" 7 #include "SkOSFile.h"
15 #include "SkPicture.h" 8 #include "SkPicture.h"
16 #include "SkStream.h" 9 #include "SkStream.h"
17 #include "SkTypeface.h" 10 #include "SkTypeface.h"
18 #include "SkTArray.h" 11 #include "SkTArray.h"
19 #include "picture_utils.h" 12 #include "picture_utils.h"
20 13
21 #include <iostream>
22 #include <cstdio>
23 #include <stack>
24
25 #include "podofo.h"
26 using 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 f rom rendered x,y
34
35 // TODO(edisonn): security - validate all the user input, all pdf!
36
37
38 #include "SkPdfHeaders_autogen.h"
39 #include "SkPdfPodofoMapper_autogen.h"
40 #include "SkPdfParser.h" 14 #include "SkPdfParser.h"
41 15
42 #include "SkPdfBasics.h"
43 #include "SkPdfUtils.h"
44
45 #include "SkPdfFont.h"
46
47 // TODO(edisonn): fix the mess with the files.
48 #include "SkPdfFont.cpp"
49 #include "SkPdfBasics.cpp"
50 #include "SkPdfUtils.cpp"
51
52 bool skpdfmap(const PdfMemDocument& podofoDoc, const PdfObject& podofoObj, SkPdf Object** out) {
53 return PodofoMapper::map(podofoDoc, podofoObj, out);
54 }
55
56
57 /*
58 * TODO(edisonn):
59 * - all font types and all ppdf font features
60 * - word spacing
61 * - load font for baidu.pdf
62 * - load font for youtube.pdf
63 * - parser for pdf from the definition already available in pdfspec_autoge n.py
64 * - all docs from ~/work
65 * - encapsulate podofo in the pdf api so the skpdf does not know anything about podofo ... in progress
66 * - load gs/ especially smask and already known prop (skp) ... in progress
67 * - wrapper on classes for customizations? e.g.
68 * SkPdfPageObjectVanila - has only the basic loaders/getters
69 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add custom izations here
70 * need to find a nice object model for all this with constructors and factories
71 * - deal with inheritable automatically ?
72 * - deal with specific type in spec directly, add all dictionary types to known types
73 */
74
75
76 // TODO(edisonn): move in trace util.
77 #ifdef PDF_TRACE
78 static void SkTraceMatrix(const SkMatrix& matrix, const char* sz = "") {
79 printf("SkMatrix %s ", sz);
80 for (int i = 0 ; i < 9 ; i++) {
81 printf("%f ", SkScalarToDouble(matrix.get(i)));
82 }
83 printf("\n");
84 }
85
86 static void SkTraceRect(const SkRect& rect, const char* sz = "") {
87 printf("SkRect %s ", sz);
88 printf("x = %f ", SkScalarToDouble(rect.x()));
89 printf("y = %f ", SkScalarToDouble(rect.y()));
90 printf("w = %f ", SkScalarToDouble(rect.width()));
91 printf("h = %f ", SkScalarToDouble(rect.height()));
92 printf("\n");
93 }
94
95 #else
96 #define SkTraceMatrix(a,b)
97 #define SkTraceRect(a,b)
98 #endif
99
100 using namespace std;
101 using namespace PoDoFo;
102
103 // Utilities
104 static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
105 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
106
107 bitmap->allocPixels();
108 bitmap->eraseColor(color);
109 }
110
111 // TODO(edisonn): synonyms? DeviceRGB and RGB ...
112 int GetColorSpaceComponents(const std::string& colorSpace) {
113 if (colorSpace == "DeviceCMYK") {
114 return 4;
115 } else if (colorSpace == "DeviceGray" ||
116 colorSpace == "CalGray" ||
117 colorSpace == "Indexed") {
118 return 1;
119 } else if (colorSpace == "DeviceRGB" ||
120 colorSpace == "CalRGB" ||
121 colorSpace == "Lab") {
122 return 3;
123 } else {
124 return 0;
125 }
126 }
127
128 const PdfObject* resolveReferenceObject(const PdfMemDocument* pdfDoc,
129 const PdfObject* obj,
130 bool resolveOneElementArrays) {
131 while (obj && (obj->IsReference() || (resolveOneElementArrays &&
132 obj->IsArray() &&
133 obj->GetArray().GetSize() == 1))) {
134 if (obj->IsReference()) {
135 // We need to force the non const, the only update we will do is for recurssion checks.
136 PdfReference& ref = (PdfReference&)obj->GetReference();
137 obj = pdfDoc->GetObjects().GetObject(ref);
138 } else {
139 obj = &obj->GetArray()[0];
140 }
141 }
142
143 return obj;
144 }
145
146 static SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
147 SkMatrix matrix;
148 matrix.setAll(SkDoubleToScalar(array[0]),
149 SkDoubleToScalar(array[2]),
150 SkDoubleToScalar(array[4]),
151 SkDoubleToScalar(array[1]),
152 SkDoubleToScalar(array[3]),
153 SkDoubleToScalar(array[5]),
154 SkDoubleToScalar(0),
155 SkDoubleToScalar(0),
156 SkDoubleToScalar(1));
157
158 return matrix;
159 }
160
161 SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
162 double array[6];
163
164 // TODO(edisonn): security issue, ret if size() != 6
165 for (int i = 0; i < 6; i++) {
166 const PdfObject* elem = resolveReferenceObject(pdfArray->doc(), (*pdfArr ay)[i]->podofo());
167 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
168 return SkMatrix::I(); // TODO(edisonn): report issue
169 }
170 array[i] = elem->GetReal();
171 }
172
173 return SkMatrixFromPdfMatrix(array);
174 }
175
176 PdfContext* gPdfContext = NULL;
177 SkBitmap* gDumpBitmap = NULL;
178 SkCanvas* gDumpCanvas = NULL;
179 char gLastKeyword[100] = "";
180 int gLastOpKeyword = -1;
181 char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh ,EI,Do,EX,";
182 int gReadOp = 0;
183
184
185
186 bool hasVisualEffect(const char* pdfOp) {
187 return true;
188 if (*pdfOp == '\0') return false;
189
190 char markedPdfOp[100] = ",";
191 strcat(markedPdfOp, pdfOp);
192 strcat(markedPdfOp, ",");
193
194 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
195 }
196
197 // TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrum entation.
198 static bool readToken(SkPdfTokenizer* fTokenizer, PdfToken* token) {
199 bool ret = fTokenizer->readToken(token);
200
201 gReadOp++;
202
203 #ifdef PDF_TRACE_DIFF_IN_PNG
204 // TODO(edisonn): compare with old bitmap, and save only new bits are availa ble, and save
205 // the numbar and name of last operation, so the file name will reflect op t hat changed.
206 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits.
207 gDumpCanvas->flush();
208
209 SkBitmap bitmap;
210 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
211
212 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSiz e());
213
214 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
215 SkCanvas canvas(device);
216
217 // draw context stuff here
218 SkPaint blueBorder;
219 blueBorder.setColor(SK_ColorBLUE);
220 blueBorder.setStyle(SkPaint::kStroke_Style);
221 blueBorder.setTextSize(SkDoubleToScalar(20));
222
223 SkString str;
224
225 const SkClipStack* clipStack = gDumpCanvas->getClipStack();
226 if (clipStack) {
227 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterSt art);
228 const SkClipStack::Element* elem;
229 double y = 0;
230 int total = 0;
231 while (elem = iter.next()) {
232 total++;
233 y += 30;
234
235 switch (elem->getType()) {
236 case SkClipStack::Element::kRect_Type:
237 canvas.drawRect(elem->getRect(), blueBorder);
238 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoub leToScalar(10), SkDoubleToScalar(y), blueBorder);
239 break;
240 case SkClipStack::Element::kPath_Type:
241 canvas.drawPath(elem->getPath(), blueBorder);
242 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoub leToScalar(10), SkDoubleToScalar(y), blueBorder);
243 break;
244 case SkClipStack::Element::kEmpty_Type:
245 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!") , SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
246 break;
247 default:
248 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!! "), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
249 break;
250 }
251 }
252
253 y += 30;
254 str.printf("Number of clips in stack: %i", total);
255 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDou bleToScalar(y), blueBorder);
256 }
257
258 const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
259 SkPath clipPath;
260 if (clipRegion.getBoundaryPath(&clipPath)) {
261 SkPaint redBorder;
262 redBorder.setColor(SK_ColorRED);
263 redBorder.setStyle(SkPaint::kStroke_Style);
264 canvas.drawPath(clipPath, redBorder);
265 }
266
267 canvas.flush();
268
269 SkString out;
270
271 // TODO(edisonn): get the image, and overlay on top of it, the clip , gr afic state, teh stack,
272 // ... and other properties, to be able to debug th code easily
273
274 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", g LastOpKeyword, gLastKeyword);
275 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Typ e, 100);
276 }
277
278 if (token->fType == kKeyword_TokenType) {
279 strcpy(gLastKeyword, token->fKeyword);
280 gLastOpKeyword = gReadOp;
281 } else {
282 strcpy(gLastKeyword, "");
283 }
284 #endif
285
286 return ret;
287 }
288
289 // TODO(edisonn): Document PdfTokenLooper and subclasses.
290 class PdfTokenLooper {
291 protected:
292 PdfTokenLooper* fParent;
293 SkPdfTokenizer* fTokenizer;
294 PdfContext* fPdfContext;
295 SkCanvas* fCanvas;
296
297 public:
298 PdfTokenLooper(PdfTokenLooper* parent,
299 SkPdfTokenizer* tokenizer,
300 PdfContext* pdfContext,
301 SkCanvas* canvas)
302 : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanv as(canvas) {}
303
304 virtual PdfResult consumeToken(PdfToken& token) = 0;
305 virtual void loop() = 0;
306
307 void setUp(PdfTokenLooper* parent) {
308 fParent = parent;
309 fTokenizer = parent->fTokenizer;
310 fPdfContext = parent->fPdfContext;
311 fCanvas = parent->fCanvas;
312 }
313 };
314
315 class PdfMainLooper : public PdfTokenLooper {
316 public:
317 PdfMainLooper(PdfTokenLooper* parent,
318 SkPdfTokenizer* tokenizer,
319 PdfContext* pdfContext,
320 SkCanvas* canvas)
321 : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
322
323 virtual PdfResult consumeToken(PdfToken& token);
324 virtual void loop();
325 };
326
327 class PdfInlineImageLooper : public PdfTokenLooper {
328 public:
329 PdfInlineImageLooper()
330 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
331
332 virtual PdfResult consumeToken(PdfToken& token);
333 virtual void loop();
334 PdfResult done();
335 };
336
337 class PdfCompatibilitySectionLooper : public PdfTokenLooper {
338 public:
339 PdfCompatibilitySectionLooper()
340 : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
341
342 virtual PdfResult consumeToken(PdfToken& token);
343 virtual void loop();
344 };
345
346 typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper* *);
347
348 map<std::string, PdfOperatorRenderer> gPdfOps;
349
350 map<std::string, int> gRenderStats[kCount_PdfResult];
351
352 char* gRenderStatsNames[kCount_PdfResult] = {
353 "Success",
354 "Partially implemented",
355 "Not yet implemented",
356 "Ignore Error",
357 "Error",
358 "Unsupported/Unknown"
359 };
360
361 static SkTypeface* SkTypefaceFromPdfFont(PdfFont* font) {
362 if (font == NULL) {
363 return SkTypeface::CreateFromName("Times New Roman", SkTypeface::kNormal );
364 }
365
366 PdfObject* fontObject = font->GetObject();
367
368 PdfObject* pBaseFont = NULL;
369 // TODO(edisonn): warning, PoDoFo has a bug in PdfFont constructor, does not call InitVars()
370 // for now fixed locally.
371 pBaseFont = fontObject->GetIndirectKey( "BaseFont" );
372 const char* pszBaseFontName = pBaseFont->GetName().GetName().c_str();
373
374 #ifdef PDF_TRACE
375 std::string str;
376 fontObject->ToString(str);
377 printf("Base Font Name: %s\n", pszBaseFontName);
378 printf("Font Object Data: %s\n", str.c_str());
379 #endif
380
381 SkTypeface* typeface = SkTypefaceFromPdfStandardFont(pszBaseFontName, font-> IsBold(), font->IsItalic());
382
383 if (typeface != NULL) {
384 return typeface;
385 }
386
387 char name[1000];
388 // HACK
389 strncpy(name, pszBaseFontName, 1000);
390 char* comma = strstr(name, ",");
391 char* dash = strstr(name, "-");
392 if (comma) *comma = '\0';
393 if (dash) *dash = '\0';
394
395 typeface = SkTypeface::CreateFromName(
396 name,
397 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
398 (font->IsItalic() ? SkTypeface::kItalic : 0))) ;
399
400 if (typeface != NULL) {
401 #ifdef PDF_TRACE
402 printf("HACKED FONT found %s\n", name);
403 #endif
404 return typeface;
405 }
406
407 #ifdef PDF_TRACE
408 printf("FONT_NOT_FOUND %s\n", pszBaseFontName);
409 #endif
410
411 // TODO(edisonn): Report Warning, NYI
412 return SkTypeface::CreateFromName(
413 "Times New Roman",
414 SkTypeface::Style((font->IsBold() ? SkTypeface::kBold : 0) |
415 (font->IsItalic() ? SkTypeface::kItalic : 0))) ;
416 }
417
418 PdfResult DrawText(PdfContext* pdfContext,
419 const SkPdfObject* str,
420 SkCanvas* canvas)
421 {
422
423 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
424 if (skfont == NULL) {
425 skfont = SkPdfFont::Default();
426 }
427
428 SkUnencodedText binary(str);
429
430 SkDecodedText decoded;
431
432 if (skfont->encoding() == NULL) {
433 // TODO(edisonn): report warning
434 return kNYI_PdfResult;
435 }
436
437 skfont->encoding()->decodeText(binary, &decoded);
438
439 SkPaint paint;
440 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCur FontSize == 0?
441 // Or maybe just not call setTextSize at all?
442 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
443 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSi ze));
444 }
445
446 // if (fCurFont && fCurFont->GetFontScale() != 0) {
447 // paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0)) ;
448 // }
449
450 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
451
452 canvas->save();
453
454 #if 1
455 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm;
456
457 SkPoint point1;
458 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0 ), &point1);
459
460 SkMatrix mirror;
461 mirror.setTranslate(0, -point1.y());
462 // TODO(edisonn): fix rotated text, and skewed too
463 mirror.postScale(SK_Scalar1, -SK_Scalar1);
464 // TODO(edisonn): post rotate, skew
465 mirror.postTranslate(0, point1.y());
466
467 matrix.postConcat(mirror);
468
469 canvas->setMatrix(matrix);
470
471 SkTraceMatrix(matrix, "mirrored");
472 #endif
473
474 skfont->drawText(decoded, &paint, pdfContext, canvas, &pdfContext->fGraphics State.fMatrixTm);
475 canvas->restore();
476
477 return kPartial_PdfResult;
478 }
479
480 // TODO(edisonn): create header files with declarations!
481 PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per);
482 PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per);
483 PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper);
484 PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper);
485
486 // TODO(edisonn): deal with synonyms (/BPC == /BitsPerComponent), here or in Get Key?
487 // Always pass long form in key, and have a map of long -> short key
488 bool LongFromDictionary(const PdfMemDocument* pdfDoc,
489 const PdfDictionary& dict,
490 const char* key,
491 long* data) {
492 const PdfObject* value = resolveReferenceObject(pdfDoc,
493 dict.GetKey(PdfName(key)));
494
495 if (value == NULL || !value->IsNumber()) {
496 return false;
497 }
498 if (data == NULL) {
499 return true;
500 }
501
502 *data = value->GetNumber();
503 return true;
504 }
505
506 bool LongFromDictionary(const PdfMemDocument* pdfDoc,
507 const PdfDictionary& dict,
508 const char* key,
509 const char* abr,
510 long* data) {
511 if (LongFromDictionary(pdfDoc, dict, key, data)) return true;
512 if (abr == NULL || *abr == '\0') return false;
513 return LongFromDictionary(pdfDoc, dict, abr, data);
514 }
515
516 bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
517 const PdfDictionary& dict,
518 const char* key,
519 double* data) {
520 const PdfObject* value = resolveReferenceObject(pdfDoc,
521 dict.GetKey(PdfName(key)));
522
523 if (value == NULL || (!value->IsReal() && !value->IsNumber())) {
524 return false;
525 }
526 if (data == NULL) {
527 return true;
528 }
529
530 *data = value->GetReal();
531 return true;
532 }
533
534 bool DoubleFromDictionary(const PdfMemDocument* pdfDoc,
535 const PdfDictionary& dict,
536 const char* key,
537 const char* abr,
538 double* data) {
539 if (DoubleFromDictionary(pdfDoc, dict, key, data)) return true;
540 if (abr == NULL || *abr == '\0') return false;
541 return DoubleFromDictionary(pdfDoc, dict, abr, data);
542 }
543
544
545 bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
546 const PdfDictionary& dict,
547 const char* key,
548 bool* data) {
549 const PdfObject* value = resolveReferenceObject(pdfDoc,
550 dict.GetKey(PdfName(key)));
551
552 if (value == NULL || !value->IsBool()) {
553 return false;
554 }
555 if (data == NULL) {
556 return true;
557 }
558
559 *data = value->GetBool();
560 return true;
561 }
562
563 bool BoolFromDictionary(const PdfMemDocument* pdfDoc,
564 const PdfDictionary& dict,
565 const char* key,
566 const char* abr,
567 bool* data) {
568 if (BoolFromDictionary(pdfDoc, dict, key, data)) return true;
569 if (abr == NULL || *abr == '\0') return false;
570 return BoolFromDictionary(pdfDoc, dict, abr, data);
571 }
572
573 bool NameFromDictionary(const PdfMemDocument* pdfDoc,
574 const PdfDictionary& dict,
575 const char* key,
576 std::string* data) {
577 const PdfObject* value = resolveReferenceObject(pdfDoc,
578 dict.GetKey(PdfName(key)),
579 true);
580 if (value == NULL || !value->IsName()) {
581 return false;
582 }
583 if (data == NULL) {
584 return true;
585 }
586
587 *data = value->GetName().GetName();
588 return true;
589 }
590
591 bool NameFromDictionary(const PdfMemDocument* pdfDoc,
592 const PdfDictionary& dict,
593 const char* key,
594 const char* abr,
595 std::string* data) {
596 if (NameFromDictionary(pdfDoc, dict, key, data)) return true;
597 if (abr == NULL || *abr == '\0') return false;
598 return NameFromDictionary(pdfDoc, dict, abr, data);
599 }
600
601 bool StringFromDictionary(const PdfMemDocument* pdfDoc,
602 const PdfDictionary& dict,
603 const char* key,
604 std::string* data) {
605 const PdfObject* value = resolveReferenceObject(pdfDoc,
606 dict.GetKey(PdfName(key)),
607 true);
608 if (value == NULL || (!value->IsString() && !value->IsHexString())) {
609 return false;
610 }
611 if (data == NULL) {
612 return true;
613 }
614
615 *data = value->GetString().GetString();
616 return true;
617 }
618
619 bool StringFromDictionary(const PdfMemDocument* pdfDoc,
620 const PdfDictionary& dict,
621 const char* key,
622 const char* abr,
623 std::string* data) {
624 if (StringFromDictionary(pdfDoc, dict, key, data)) return true;
625 if (abr == NULL || *abr == '\0') return false;
626 return StringFromDictionary(pdfDoc, dict, abr, data);
627 }
628
629 bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
630 const PdfDictionary& dict,
631 const char* key,
632 SkPdfArray** data) {
633 const PdfObject* value = resolveReferenceObject(pdfDoc,
634 dict.GetKey(PdfName(key)),
635 true);
636 if (value == NULL || !value->IsArray()) {
637 return false;
638 }
639 if (data == NULL) {
640 return true;
641 }
642
643 return PodofoMapper::map(*pdfDoc, *value, (SkPdfObject**)data);
644 }
645
646
647 bool ArrayFromDictionary(const PdfMemDocument* pdfDoc,
648 const PdfDictionary& dict,
649 const char* key,
650 const char* abr,
651 SkPdfArray** data) {
652 if (ArrayFromDictionary(pdfDoc, dict, key, data)) return true;
653 if (abr == NULL || *abr == '\0') return false;
654 return ArrayFromDictionary(pdfDoc, dict, abr, data);
655 }
656
657
658 bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
659 const PdfDictionary& dict,
660 const char* key,
661 SkPdfDictionary** data) {
662 const PdfObject* value = resolveReferenceObject(pdfDoc,
663 dict.GetKey(PdfName(key)),
664 true);
665 if (value == NULL || !value->IsDictionary()) {
666 return false;
667 }
668 if (data == NULL) {
669 return true;
670 }
671
672 return PodofoMapper::map(*pdfDoc, *value, (SkPdfObject**)data);
673 }
674
675 bool DictionaryFromDictionary(const PdfMemDocument* pdfDoc,
676 const PdfDictionary& dict,
677 const char* key,
678 const char* abr,
679 SkPdfDictionary** data) {
680 if (DictionaryFromDictionary(pdfDoc, dict, key, data)) return true;
681 if (abr == NULL || *abr == '\0') return false;
682 return DictionaryFromDictionary(pdfDoc, dict, abr, data);
683 }
684
685 template <typename T>
686 bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
687 const PdfDictionary& dict,
688 const char* key,
689 T** data) {
690 const PdfObject* value = resolveReferenceObject(pdfDoc,
691 dict.GetKey(PdfName(key)),
692 true);
693 if (value == NULL || !value->IsDictionary()) {
694 return false;
695 }
696
697 if (data == NULL) {
698 return true;
699 }
700
701 return PodofoMapper::map(*pdfDoc, *value, (T**)data);
702 }
703
704 template <typename T>
705 bool DictionaryFromDictionary2(const PdfMemDocument* pdfDoc,
706 const PdfDictionary& dict,
707 const char* key,
708 const char* abr,
709 T** data) {
710 if (DictionaryFromDictionary2<T>(pdfDoc, dict, key, data)) return true;
711 if (abr == NULL || *abr == '\0') return false;
712 return DictionaryFromDictionary2<T>(pdfDoc, dict, abr, data);
713 }
714
715 bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
716 const PdfDictionary& dict,
717 const char* key,
718 SkPdfObject** data) {
719 const PdfObject* value = resolveReferenceObject(pdfDoc,
720 dict.GetKey(PdfName(key)),
721 true);
722 if (value == NULL) {
723 return false;
724 }
725 if (data == NULL) {
726 return true;
727 }
728 return PodofoMapper::map(*pdfDoc, *value, data);
729 }
730
731 bool ObjectFromDictionary(const PdfMemDocument* pdfDoc,
732 const PdfDictionary& dict,
733 const char* key,
734 const char* abr,
735 SkPdfObject** data) {
736 if (ObjectFromDictionary(pdfDoc, dict, key, data)) return true;
737 if (abr == NULL || *abr == '\0') return false;
738 return ObjectFromDictionary(pdfDoc, dict, abr, data);
739 }
740
741 bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
742 const PdfDictionary& dict,
743 const char* key,
744 SkPdfStream** data) {
745 const PdfObject* value = resolveReferenceObject(pdfDoc,
746 dict.GetKey(PdfName(key)),
747 true);
748 if (value == NULL) {
749 return false;
750 }
751 if (data == NULL) {
752 return true;
753 }
754 return PodofoMapper::map(*pdfDoc, *value, data);
755 }
756
757 bool StreamFromDictionary(const PdfMemDocument* pdfDoc,
758 const PdfDictionary& dict,
759 const char* key,
760 const char* abr,
761 SkPdfStream** data) {
762 if (StreamFromDictionary(pdfDoc, dict, key, data)) return true;
763 if (abr == NULL || *abr == '\0') return false;
764 return StreamFromDictionary(pdfDoc, dict, abr, data);
765 }
766
767
768 // TODO(edisonn): perf!!!
769
770 static SkColorTable* getGrayColortable() {
771 static SkColorTable* grayColortable = NULL;
772 if (grayColortable == NULL) {
773 SkPMColor* colors = new SkPMColor[256];
774 for (int i = 0 ; i < 256; i++) {
775 colors[i] = SkPreMultiplyARGB(255, i, i, i);
776 }
777 grayColortable = new SkColorTable(colors, 256);
778 }
779 return grayColortable;
780 }
781
782 SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, pdf_long uncompressedStreamLength,
783 int width, int height, int bytesPerLine,
784 int bpc, const std::string& colorSpace,
785 bool transparencyMask) {
786 SkBitmap bitmap;
787
788 int components = GetColorSpaceComponents(colorSpace);
789 //#define MAX_COMPONENTS 10
790
791 int bitsPerLine = width * components * bpc;
792 // TODO(edisonn): assume start of lines are aligned at 32 bits?
793 // Is there a faster way to load the uncompressed stream into a bitmap?
794
795 // minimal support for now
796 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
797 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * size of(SkColor));
798
799 for (int h = 0 ; h < height; h++) {
800 long i = width * (height - 1 - h);
801 for (int w = 0 ; w < width; w++) {
802 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 * w],
803 uncompressedStream[3 * w + 1],
804 uncompressedStream[3 * w + 2]);
805 i++;
806 }
807 uncompressedStream += bytesPerLine;
808 }
809
810 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
811 bitmap.setPixels(uncompressedStreamArgb);
812 }
813 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
814 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * hei ght);
815
816 for (int h = 0 ; h < height; h++) {
817 long i = width * (height - 1 - h);
818 for (int w = 0 ; w < width; w++) {
819 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedS tream[w] :
820 uncompressedStream[ w];
821 i++;
822 }
823 uncompressedStream += bytesPerLine;
824 }
825
826 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIn dex8_Config,
827 width, height);
828 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGray Colortable());
829 }
830
831 // TODO(edisonn): Report Warning, NYI, or error
832 return bitmap;
833 }
834
835 bool transferImageStreamToARGB(unsigned char* uncompressedStream, pdf_long uncom pressedStreamLength,
836 int width, int bytesPerLine,
837 int bpc, const std::string& colorSpace,
838 SkColor** uncompressedStreamArgb,
839 pdf_long* uncompressedStreamLengthInBytesArgb) {
840 int components = GetColorSpaceComponents(colorSpace);
841 //#define MAX_COMPONENTS 10
842
843 int bitsPerLine = width * components * bpc;
844 // TODO(edisonn): assume start of lines are aligned at 32 bits?
845 int height = uncompressedStreamLength / bytesPerLine;
846
847 // minimal support for now
848 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) {
849 *uncompressedStreamLengthInBytesArgb = width * height * 4;
850 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBy tesArgb);
851
852 for (int h = 0 ; h < height; h++) {
853 long i = width * (height - 1 - h);
854 for (int w = 0 ; w < width; w++) {
855 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[ 3 * w],
856 uncompressedStream[ 3 * w + 1],
857 uncompressedStream[ 3 * w + 2]);
858 i++;
859 }
860 uncompressedStream += bytesPerLine;
861 }
862 return true;
863 }
864
865 if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
866 *uncompressedStreamLengthInBytesArgb = width * height * 4;
867 *uncompressedStreamArgb = (SkColor*)malloc(*uncompressedStreamLengthInBy tesArgb);
868
869 for (int h = 0 ; h < height; h++) {
870 long i = width * (height - 1 - h);
871 for (int w = 0 ; w < width; w++) {
872 (*uncompressedStreamArgb)[i] = SkColorSetRGB(uncompressedStream[ w],
873 uncompressedStream[ w],
874 uncompressedStream[ w]);
875 i++;
876 }
877 uncompressedStream += bytesPerLine;
878 }
879 return true;
880 }
881
882 return false;
883 }
884
885 // utils
886
887 // TODO(edisonn): add cache, or put the bitmap property directly on the PdfObjec t
888 // TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config
889 // TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 2 22, 444 to closest
890 // skia format, through a table
891
892 // this functions returns the image, it does not look at the smask.
893
894 SkBitmap getImageFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* image, bool transparencyMask) {
895 if (image == NULL || !image->valid()) {
896 // TODO(edisonn): report warning to be used in testing.
897 return SkBitmap();
898 }
899
900 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw ...
901 // PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc,
902 // obj.GetDictionary().GetKey(PdfNa me("Filter")));
903 // if (value && value->IsArray() && value->GetArray().GetSize() == 1) {
904 // value = resolveReferenceObject(pdfContext->fPdfDoc,
905 // &value->GetArray()[0]);
906 // }
907 // if (value && value->IsName() && value->GetName().GetName() == "DCTDecode") {
908 // SkStream stream = SkStream::
909 // SkImageDecoder::Factory()
910 // }
911
912 long bpc = image->BitsPerComponent();
913 long width = image->Width();
914 long height = image->Height();
915 std::string colorSpace = "DeviceRGB";
916
917 // TODO(edisonn): color space can be an array too!
918 if (image->isColorSpaceAName()) {
919 colorSpace = image->getColorSpaceAsName();
920 }
921
922 /*
923 bool imageMask = image->imageMask();
924
925 if (imageMask) {
926 if (bpc != 0 && bpc != 1) {
927 // TODO(edisonn): report warning to be used in testing.
928 return SkBitmap();
929 }
930 bpc = 1;
931 }
932 */
933
934 const PdfObject* obj = image->podofo();
935
936 char* uncompressedStream = NULL;
937 pdf_long uncompressedStreamLength = 0;
938
939 PdfResult ret = kPartial_PdfResult;
940 // TODO(edisonn): get rid of try/catch exceptions! We should not throw on us er data!
941 try {
942 obj->GetStream()->GetFilteredCopy(&uncompressedStream, &uncompressedStre amLength);
943 } catch (PdfError& e) {
944 // TODO(edisonn): report warning to be used in testing.
945 return SkBitmap();
946 }
947
948 int bytesPerLine = uncompressedStreamLength / height;
949 #ifdef PDF_TRACE
950 if (uncompressedStreamLength % height != 0) {
951 printf("Warning uncompressedStreamLength % height != 0 !!!\n");
952 }
953 #endif
954
955 SkBitmap bitmap = transferImageStreamToBitmap(
956 (unsigned char*)uncompressedStream, uncompressedStreamLength,
957 width, height, bytesPerLine,
958 bpc, colorSpace,
959 transparencyMask);
960
961 free(uncompressedStream);
962
963 return bitmap;
964 }
965
966 SkBitmap getSmaskFromObject(PdfContext* pdfContext, const SkPdfImageDictionary* obj) {
967 const PdfObject* sMask = resolveReferenceObject(&pdfContext->fPdfDoc->podofo (),
968 obj->podofo()->GetDictionary().Get Key(PdfName("SMask")));
969
970 #ifdef PDF_TRACE
971 std::string str;
972 if (sMask) {
973 sMask->ToString(str);
974 printf("/SMask of /Subtype /Image: %s\n", str.c_str());
975 }
976 #endif
977
978 if (sMask) {
979 SkPdfImageDictionary skxobjmask(&pdfContext->fPdfDoc->podofo(), sMask);
980 return getImageFromObject(pdfContext, &skxobjmask, true);
981 }
982
983 // TODO(edisonn): implement GS SMask. Default to empty right now.
984 return pdfContext->fGraphicsState.fSMask;
985 }
986
987 PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfI mageDictionary* skpdfimage) {
988 if (skpdfimage == NULL || !skpdfimage->valid()) {
989 return kIgnoreError_PdfResult;
990 }
991
992 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
993 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
994
995 canvas->save();
996 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
997 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
998
999 if (sMask.empty()) {
1000 canvas->drawBitmapRect(image, dst, NULL);
1001 } else {
1002 canvas->saveLayer(&dst, NULL);
1003 canvas->drawBitmapRect(image, dst, NULL);
1004 SkPaint xfer;
1005 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
1006 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_M ode
1007 canvas->drawBitmapRect(sMask, dst, &xfer);
1008 canvas->restore();
1009 }
1010
1011 canvas->restore();
1012
1013 return kPartial_PdfResult;
1014 }
1015
1016 bool SkMatrixFromDictionary(const PdfMemDocument* pdfDoc,
1017 const PdfDictionary& dict,
1018 const char* key,
1019 SkMatrix** matrix) {
1020 const PdfObject* value = resolveReferenceObject(pdfDoc,
1021 dict.GetKey(PdfName(key)));
1022
1023 if (value == NULL || !value->IsArray()) {
1024 return false;
1025 }
1026
1027 if (value->GetArray().GetSize() != 6) {
1028 return false;
1029 }
1030
1031 double array[6];
1032 for (int i = 0; i < 6; i++) {
1033 const PdfObject* elem = resolveReferenceObject(pdfDoc, &value->GetArray( )[i]);
1034 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1035 return false;
1036 }
1037 array[i] = elem->GetReal();
1038 }
1039
1040 *matrix = new SkMatrix();
1041 **matrix = SkMatrixFromPdfMatrix(array);
1042 return true;
1043 }
1044
1045 bool SkMatrixFromDictionary(const PdfMemDocument* pdfDoc,
1046 const PdfDictionary& dict,
1047 const char* key,
1048 const char* abr,
1049 SkMatrix** data) {
1050 if (SkMatrixFromDictionary(pdfDoc, dict, key, data)) return true;
1051 if (abr == NULL || *abr == '\0') return false;
1052 return SkMatrixFromDictionary(pdfDoc, dict, abr, data);
1053
1054 }
1055
1056 bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
1057 const PdfDictionary& dict,
1058 const char* key,
1059 SkRect** rect) {
1060 const PdfObject* value = resolveReferenceObject(pdfDoc,
1061 dict.GetKey(PdfName(key)));
1062
1063 if (value == NULL || !value->IsArray()) {
1064 return false;
1065 }
1066
1067 if (value->GetArray().GetSize() != 4) {
1068 return false;
1069 }
1070
1071 double array[4];
1072 for (int i = 0; i < 4; i++) {
1073 const PdfObject* elem = resolveReferenceObject(pdfDoc, &value->GetArray( )[i]);
1074 if (elem == NULL || (!elem->IsReal() && !elem->IsNumber())) {
1075 return false;
1076 }
1077 array[i] = elem->GetReal();
1078 }
1079
1080 *rect = new SkRect();
1081 **rect = SkRect::MakeLTRB(SkDoubleToScalar(array[0]),
1082 SkDoubleToScalar(array[1]),
1083 SkDoubleToScalar(array[2]),
1084 SkDoubleToScalar(array[3]));
1085 return true;
1086 }
1087
1088 bool SkRectFromDictionary(const PdfMemDocument* pdfDoc,
1089 const PdfDictionary& dict,
1090 const char* key,
1091 const char* abr,
1092 SkRect** data) {
1093 if (SkRectFromDictionary(pdfDoc, dict, key, data)) return true;
1094 if (abr == NULL || *abr == '\0') return false;
1095 return SkRectFromDictionary(pdfDoc, dict, abr, data);
1096
1097 }
1098
1099
1100 SkPdfObject* get(const SkPdfObject* obj, const char* key, const char* abr = "") {
1101 SkPdfObject* ret = NULL;
1102 if (obj == NULL) return NULL;
1103 const SkPdfDictionary* dict = obj->asDictionary();
1104 if (dict == NULL) return NULL;
1105 if (!dict->podofo()->IsDictionary()) return NULL;
1106 ObjectFromDictionary(dict->doc(), dict->podofo()->GetDictionary(), key, abr, &ret);
1107 return ret;
1108 }
1109
1110 PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1For mDictionary* skobj) {
1111 if (!skobj || !skobj->podofo() || !skobj->podofo()->HasStream() || skobj->po dofo()->GetStream() == NULL || skobj->podofo()->GetStream()->GetLength() == 0) {
1112 return kOK_PdfResult;
1113 }
1114
1115 PdfOp_q(pdfContext, canvas, NULL);
1116 canvas->save();
1117
1118
1119 if (skobj->Resources()) {
1120 pdfContext->fGraphicsState.fResources = skobj->Resources();
1121 }
1122
1123 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
1124
1125 if (skobj->Matrix()) {
1126 pdfContext->fGraphicsState.fMatrix.preConcat(*skobj->Matrix());
1127 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatri x;
1128 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatr ix;
1129 // TODO(edisonn) reset matrixTm and matricTlm also?
1130 }
1131
1132 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1133
1134 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1135
1136 if (skobj->BBox()) {
1137 canvas->clipRect(*skobj->BBox(), SkRegion::kIntersect_Op, true); // TOD O(edisonn): AA from settings.
1138 }
1139
1140 // TODO(edisonn): iterate smart on the stream even if it is compressed, toke nize it as we go.
1141 // For this PdfContentsTokenizer needs to be extended.
1142
1143 PdfResult ret = kPartial_PdfResult;
1144 SkPdfTokenizer tokenizer(skobj);
1145 PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1146 looper.loop();
1147
1148 // TODO(edisonn): should we restore the variable stack at the same state?
1149 // There could be operands left, that could be consumed by a parent tokenize r when we pop.
1150 canvas->restore();
1151 PdfOp_Q(pdfContext, canvas, NULL);
1152 return ret;
1153 }
1154
1155 PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const PdfObject & obj) {
1156 return kNYI_PdfResult;
1157 }
1158
1159 PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, SkPdfObject* sko bj, SkRect bBox, SkMatrix matrix, double textSize) {
1160 if (!skobj || !skobj->podofo() || !skobj->podofo()->HasStream() || skobj->po dofo()->GetStream() == NULL || skobj->podofo()->GetStream()->GetLength() == 0) {
1161 return kOK_PdfResult;
1162 }
1163
1164 PdfOp_q(pdfContext, canvas, NULL);
1165 canvas->save();
1166
1167 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1168 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), Sk DoubleToScalar(textSize));
1169
1170 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm;
1171 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1172
1173 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
1174
1175 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1176
1177 SkRect rm = bBox;
1178 pdfContext->fGraphicsState.fMatrix.mapRect(&rm);
1179
1180 SkTraceRect(rm, "bbox mapped");
1181
1182 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings.
1183
1184 // TODO(edisonn): iterate smart on the stream even if it is compressed, toke nize it as we go.
1185 // For this PdfContentsTokenizer needs to be extended.
1186
1187 PdfResult ret = kPartial_PdfResult;
1188 SkPdfTokenizer tokenizer(skobj);
1189 PdfMainLooper looper(NULL, &tokenizer, pdfContext, canvas);
1190 looper.loop();
1191
1192 // TODO(edisonn): should we restore the variable stack at the same state?
1193 // There could be operands left, that could be consumed by a parent tokenize r when we pop.
1194 canvas->restore();
1195 PdfOp_Q(pdfContext, canvas, NULL);
1196 return ret;
1197 }
1198
1199
1200 // TODO(edisonn): faster, have the property on the SkPdfObject itself?
1201 std::set<const PdfObject*> gInRendering;
1202
1203 class CheckRecursiveRendering {
1204 const PdfObject& fObj;
1205 public:
1206 CheckRecursiveRendering(const PdfObject& obj) : fObj(obj) {
1207 gInRendering.insert(&obj);
1208 }
1209
1210 ~CheckRecursiveRendering() {
1211 //SkASSERT(fObj.fInRendering);
1212 gInRendering.erase(&fObj);
1213 }
1214
1215 static bool IsInRendering(const PdfObject& obj) {
1216 return gInRendering.find(&obj) != gInRendering.end();
1217 }
1218 };
1219
1220 PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject& obj) {
1221 if (CheckRecursiveRendering::IsInRendering(*obj.podofo())) {
1222 // Oops, corrupt PDF!
1223 return kIgnoreError_PdfResult;
1224 }
1225
1226 CheckRecursiveRendering checkRecursion(*obj.podofo());
1227
1228 // TODO(edisonn): check type
1229 SkPdfXObjectDictionary* skobj = NULL;
1230 if (!PodofoMapper::map(obj, &skobj)) return kIgnoreError_PdfResult;
1231
1232 if (!skobj || !skobj->valid()) return kIgnoreError_PdfResult;
1233
1234 PdfResult ret = kIgnoreError_PdfResult;
1235 switch (skobj->getType())
1236 {
1237 case kImageDictionary_SkPdfObjectType:
1238 ret = doXObject_Image(pdfContext, canvas, skobj->asImageDictionary() );
1239 break;
1240 case kType1FormDictionary_SkPdfObjectType:
1241 ret = doXObject_Form(pdfContext, canvas, skobj->asType1FormDictionar y());
1242 break;
1243 //case kObjectDictionaryXObjectPS_SkPdfObjectType:
1244 //return doXObject_PS(skxobj.asPS());
1245 }
1246
1247 delete skobj;
1248 return ret;
1249 }
1250
1251 PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1252 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
1253 canvas->save();
1254 return kOK_PdfResult;
1255 }
1256
1257 PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1258 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
1259 pdfContext->fStateStack.pop();
1260 canvas->restore();
1261 return kOK_PdfResult;
1262 }
1263
1264 PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1265 double array[6];
1266 for (int i = 0 ; i < 6 ; i++) {
1267 array[5 - i] = pdfContext->fObjectStack.top()->asNumber()->value();
1268 pdfContext->fObjectStack.pop();
1269 }
1270
1271 // a b
1272 // c d
1273 // e f
1274
1275 // 0 1
1276 // 2 3
1277 // 4 5
1278
1279 // sx ky
1280 // kx sy
1281 // tx ty
1282 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1283
1284 pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
1285
1286 #ifdef PDF_TRACE
1287 printf("cm ");
1288 for (int i = 0 ; i < 6 ; i++) {
1289 printf("%f ", array[i]);
1290 }
1291 printf("\n");
1292 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix);
1293 #endif
1294
1295 return kOK_PdfResult;
1296 }
1297
1298 //leading TL Set the text leading, Tl
1299 //, to leading, which is a number expressed in unscaled text
1300 //space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
1301 PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1302 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext- >fObjectStack.pop();
1303
1304 pdfContext->fGraphicsState.fTextLeading = ty;
1305
1306 return kOK_PdfResult;
1307 }
1308
1309 PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1310 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext- >fObjectStack.pop();
1311 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext- >fObjectStack.pop();
1312
1313 double array[6] = {1, 0, 0, 1, tx, ty};
1314 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1315
1316 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1317 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1318
1319 return kPartial_PdfResult;
1320 }
1321
1322 PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1323 double ty = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext- >fObjectStack.pop();
1324 double tx = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext- >fObjectStack.pop();
1325
1326 // TODO(edisonn): Create factory methods or constructors so podofo is hidden
1327 PdfObject _ty(PdfVariant(-ty));
1328 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo() , &_ty));
1329
1330 PdfOp_TL(pdfContext, canvas, looper);
1331
1332 PdfObject vtx(PdfVariant(-(-tx))); // TODO(edisonn): Hmm, the compiler thin ks I have here a function pointer if we use (tx), but not -(-tx)
1333 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo() , &vtx));
1334
1335 PdfObject vty(PdfVariant(-(-ty)));
1336 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo() , &vty));
1337
1338 return PdfOp_Td(pdfContext, canvas, looper);
1339 }
1340
1341 PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1342 double f = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext-> fObjectStack.pop();
1343 double e = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext-> fObjectStack.pop();
1344 double d = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext-> fObjectStack.pop();
1345 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext-> fObjectStack.pop();
1346 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext-> fObjectStack.pop();
1347 double a = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext-> fObjectStack.pop();
1348
1349 double array[6];
1350 array[0] = a;
1351 array[1] = b;
1352 array[2] = c;
1353 array[3] = d;
1354 array[4] = e;
1355 array[5] = f;
1356
1357 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1358 matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
1359
1360 // TODO(edisonn): Text positioning.
1361 pdfContext->fGraphicsState.fMatrixTm = matrix;
1362 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1363
1364 return kPartial_PdfResult;
1365 }
1366
1367 //— T* Move to the start of the next line. This operator has the same effect as the code
1368 //0 Tl Td
1369 //where Tl is the current leading parameter in the text state
1370 PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper* * looper) {
1371 PdfObject zero(PdfVariant(0.0));
1372 PdfObject tl(PdfVariant(-(-pdfContext->fGraphicsState.fTextLeading)));
1373
1374 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo() , &zero));
1375 pdfContext->fObjectStack.push(new SkPdfNumber(&pdfContext->fPdfDoc->podofo() , &tl));
1376 return PdfOp_Td(pdfContext, canvas, looper);
1377 }
1378
1379 PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1380 if (pdfContext->fGraphicsState.fPathClosed) {
1381 pdfContext->fGraphicsState.fPath.reset();
1382 pdfContext->fGraphicsState.fPathClosed = false;
1383 }
1384
1385 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumb er()->value(); pdfContext->fObjectStack.pop();
1386 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumb er()->value(); pdfContext->fObjectStack.pop();
1387
1388 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphi csState.fCurPosX),
1389 SkDoubleToScalar(pdfContext->fGraphics State.fCurPosY));
1390
1391 return kOK_PdfResult;
1392 }
1393
1394 PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1395 if (pdfContext->fGraphicsState.fPathClosed) {
1396 pdfContext->fGraphicsState.fPath.reset();
1397 pdfContext->fGraphicsState.fPathClosed = false;
1398 }
1399
1400 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->asNumb er()->value(); pdfContext->fObjectStack.pop();
1401 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->asNumb er()->value(); pdfContext->fObjectStack.pop();
1402
1403 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphi csState.fCurPosX),
1404 SkDoubleToScalar(pdfContext->fGraphics State.fCurPosY));
1405
1406 return kOK_PdfResult;
1407 }
1408
1409 PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1410 if (pdfContext->fGraphicsState.fPathClosed) {
1411 pdfContext->fGraphicsState.fPath.reset();
1412 pdfContext->fGraphicsState.fPathClosed = false;
1413 }
1414
1415 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1416 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1417 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1418 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1419 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1420 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1421
1422 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca lar(y1),
1423 SkDoubleToScalar(x2), SkDoubleToScal ar(y2),
1424 SkDoubleToScalar(x3), SkDoubleToScal ar(y3));
1425
1426 pdfContext->fGraphicsState.fCurPosX = x3;
1427 pdfContext->fGraphicsState.fCurPosY = y3;
1428
1429 return kOK_PdfResult;
1430 }
1431
1432 PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1433 if (pdfContext->fGraphicsState.fPathClosed) {
1434 pdfContext->fGraphicsState.fPath.reset();
1435 pdfContext->fGraphicsState.fPathClosed = false;
1436 }
1437
1438 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1439 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1440 double y2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1441 double x2 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1442 double y1 = pdfContext->fGraphicsState.fCurPosY;
1443 double x1 = pdfContext->fGraphicsState.fCurPosX;
1444
1445 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca lar(y1),
1446 SkDoubleToScalar(x2), SkDoubleToScal ar(y2),
1447 SkDoubleToScalar(x3), SkDoubleToScal ar(y3));
1448
1449 pdfContext->fGraphicsState.fCurPosX = x3;
1450 pdfContext->fGraphicsState.fCurPosY = y3;
1451
1452 return kOK_PdfResult;
1453 }
1454
1455 PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1456 if (pdfContext->fGraphicsState.fPathClosed) {
1457 pdfContext->fGraphicsState.fPath.reset();
1458 pdfContext->fGraphicsState.fPathClosed = false;
1459 }
1460
1461 double y3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1462 double x3 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1463 double y2 = pdfContext->fGraphicsState.fCurPosY;
1464 double x2 = pdfContext->fGraphicsState.fCurPosX;
1465 double y1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1466 double x1 = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1467
1468 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca lar(y1),
1469 SkDoubleToScalar(x2), SkDoubleToScal ar(y2),
1470 SkDoubleToScalar(x3), SkDoubleToScal ar(y3));
1471
1472 pdfContext->fGraphicsState.fCurPosX = x3;
1473 pdfContext->fGraphicsState.fCurPosY = y3;
1474
1475 return kOK_PdfResult;
1476 }
1477
1478 PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1479 if (pdfContext->fGraphicsState.fPathClosed) {
1480 pdfContext->fGraphicsState.fPath.reset();
1481 pdfContext->fGraphicsState.fPathClosed = false;
1482 }
1483
1484 double height = pdfContext->fObjectStack.top()->asNumber()->value(); pd fContext->fObjectStack.pop();
1485 double width = pdfContext->fObjectStack.top()->asNumber()->value(); pd fContext->fObjectStack.pop();
1486 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pd fContext->fObjectStack.pop();
1487 double x = pdfContext->fObjectStack.top()->asNumber()->value(); pd fContext->fObjectStack.pop();
1488
1489 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScal ar(y),
1490 SkDoubleToScalar(x + width), SkDouble ToScalar(y + height));
1491
1492 pdfContext->fGraphicsState.fCurPosX = x;
1493 pdfContext->fGraphicsState.fCurPosY = y + height;
1494
1495 return kOK_PdfResult;
1496 }
1497
1498 PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1499 pdfContext->fGraphicsState.fPath.close();
1500 return kOK_PdfResult;
1501 }
1502
1503 PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, bool fil l, bool stroke, bool close, bool evenOdd) {
1504 SkPath path = pdfContext->fGraphicsState.fPath;
1505
1506 if (close) {
1507 path.close();
1508 }
1509
1510 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1511
1512 SkPaint paint;
1513
1514 SkPoint line[2];
1515 if (fill && !stroke && path.isLine(line)) {
1516 paint.setStyle(SkPaint::kStroke_Style);
1517
1518 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1519 paint.setStrokeWidth(SkDoubleToScalar(0));
1520
1521 canvas->drawPath(path, paint);
1522 } else {
1523 if (fill) {
1524 paint.setStyle(SkPaint::kFill_Style);
1525 if (evenOdd) {
1526 path.setFillType(SkPath::kEvenOdd_FillType);
1527 }
1528
1529 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1530
1531 canvas->drawPath(path, paint);
1532 }
1533
1534 if (stroke) {
1535 paint.setStyle(SkPaint::kStroke_Style);
1536
1537 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1538
1539 path.setFillType(SkPath::kWinding_FillType); // reset it, just in c ase it messes up the stroke
1540 canvas->drawPath(path, paint);
1541 }
1542 }
1543
1544 pdfContext->fGraphicsState.fPath.reset();
1545 // todo zoom ... other stuff ?
1546
1547 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1548 #ifndef PDF_DEBUG_NO_CLIPING
1549 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kInters ect_Op, true);
1550 #endif
1551 }
1552
1553 //pdfContext->fGraphicsState.fClipPath.reset();
1554 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1555
1556 return kPartial_PdfResult;
1557
1558 }
1559
1560 PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1561 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1562 }
1563
1564 PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1565 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1566 }
1567
1568 PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1569 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1570 }
1571
1572 PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1573 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1574 }
1575
1576 PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper* * looper) {
1577 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1578 }
1579
1580 PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1581 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1582 }
1583
1584 PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper* * looper) {
1585 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1586 }
1587
1588 PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1589 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1590 }
1591
1592 PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper* * looper) {
1593 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1594 }
1595
1596 PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1597 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
1598 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1599 #ifndef PDF_DEBUG_NO_CLIPING
1600 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kInters ect_Op, true);
1601 #endif
1602 }
1603
1604 //pdfContext->fGraphicsState.fClipPath.reset();
1605 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1606
1607 pdfContext->fGraphicsState.fPathClosed = true;
1608
1609 return kOK_PdfResult;
1610 }
1611
1612 PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1613 pdfContext->fGraphicsState.fTextBlock = true;
1614 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
1615 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
1616
1617 return kPartial_PdfResult;
1618 }
1619
1620 PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1621 if (!pdfContext->fGraphicsState.fTextBlock) {
1622 return kIgnoreError_PdfResult;
1623 }
1624 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1625 return kPartial_PdfResult;
1626 }
1627
1628 //font size Tf Set the text font, Tf
1629 //, to font and the text font size, Tfs, to size. font is the name of a
1630 //font resource in the Fontsubdictionary of the current resource dictionary; siz e is
1631 //a number representing a scale factor. There is no initial value for either fon t or
1632 //size; they must be specified explicitly using Tf before any text is shown.
1633 PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1634 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->as Number()->value(); pdfContext->fObjectStack.pop();
1635 std::string fontName = pdfContext->fObjectStack.top()->asName()->value(); pdfContext->fObjectStack.pop();
1636
1637 #ifdef PDF_TRACE
1638 printf("font name: %s\n", fontName.c_str());
1639 std::string str;
1640 pdfContext->fGraphicsState.fResources->podofo()->ToString(str);
1641 printf("Print Tf Resources: %s\n", str.c_str());
1642 pdfContext->fGraphicsState.fResources->Font()->podofo()->ToString(str);
1643 printf("Print Tf Resources/Font: %s\n", str.c_str());
1644 #endif
1645
1646 SkPdfFontDictionary* fd = NULL;
1647 if (pdfContext->fGraphicsState.fResources->Font()) {
1648 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font()->ge t(fontName.c_str());
1649 PodofoMapper::map(*objFont, &fd);
1650
1651 #ifdef PDF_TRACE
1652 objFont->podofo()->ToString(str);
1653 printf("Print Font loaded: %s\n", str.c_str());
1654 fd->podofo()->ToString(str);
1655 printf("Print Font loaded and resolved and upgraded: %s\n", str.c_str()) ;
1656 #endif
1657
1658 }
1659
1660 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(fd);
1661
1662 if (skfont) {
1663 pdfContext->fGraphicsState.fSkFont = skfont;
1664 }
1665
1666 return kPartial_PdfResult;
1667 }
1668
1669 PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1670 if (!pdfContext->fGraphicsState.fTextBlock) {
1671 // TODO(edisonn): try to recover and draw it any way?
1672 return kIgnoreError_PdfResult;
1673 }
1674
1675 PdfResult ret = DrawText(pdfContext,
1676 pdfContext->fObjectStack.top(),
1677 canvas);
1678 pdfContext->fObjectStack.pop();
1679
1680 return ret;
1681 }
1682
1683 PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
1684 if (!pdfContext->fGraphicsState.fTextBlock) {
1685 // TODO(edisonn): try to recover and draw it any way?
1686 return kIgnoreError_PdfResult;
1687 }
1688
1689 PdfOp_T_star(pdfContext, canvas, looper);
1690 // Do not pop, and push, just transfer the param to Tj
1691 return PdfOp_Tj(pdfContext, canvas, looper);
1692 }
1693
1694 PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLo oper** looper) {
1695 if (!pdfContext->fGraphicsState.fTextBlock) {
1696 // TODO(edisonn): try to recover and draw it any way?
1697 return kIgnoreError_PdfResult;
1698 }
1699
1700 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObject Stack.pop();
1701 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObject Stack.pop();
1702 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObject Stack.pop();
1703
1704 pdfContext->fObjectStack.push(aw);
1705 PdfOp_Tw(pdfContext, canvas, looper);
1706
1707 pdfContext->fObjectStack.push(ac);
1708 PdfOp_Tc(pdfContext, canvas, looper);
1709
1710 pdfContext->fObjectStack.push(str);
1711 PdfOp_quote(pdfContext, canvas, looper);
1712
1713 return kPartial_PdfResult;
1714 }
1715
1716 PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1717 if (!pdfContext->fGraphicsState.fTextBlock) {
1718 // TODO(edisonn): try to recover and draw it any way?
1719 return kIgnoreError_PdfResult;
1720 }
1721
1722 SkPdfArray* array = pdfContext->fObjectStack.top()->asArray();
1723 pdfContext->fObjectStack.pop();
1724
1725 for( int i=0; i<static_cast<int>(array->size()); i++ )
1726 {
1727 if( (*array)[i]->asString()) {
1728 SkPdfObject* obj = (*array)[i];
1729 DrawText(pdfContext,
1730 obj,
1731 canvas);
1732 } else if ((*array)[i]->asInteger() || (*array)[i]->asNumber()) {
1733 double dx = (*array)[i]->asNumber()->value();
1734 SkMatrix matrix;
1735 matrix.setAll(SkDoubleToScalar(1),
1736 SkDoubleToScalar(0),
1737 // TODO(edisonn): use writing mode, vertical/horizonta l.
1738 SkDoubleToScalar(-dx), // amount is substracted!!!
1739 SkDoubleToScalar(0),
1740 SkDoubleToScalar(1),
1741 SkDoubleToScalar(0),
1742 SkDoubleToScalar(0),
1743 SkDoubleToScalar(0),
1744 SkDoubleToScalar(1));
1745
1746 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1747 }
1748 }
1749 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText befor e returing OK.
1750 }
1751
1752 PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator * colorOperator) {
1753 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->asName()->value (); pdfContext->fObjectStack.pop();
1754 return kOK_PdfResult;
1755 }
1756
1757 PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1758 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking );
1759 }
1760
1761 PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1762 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok ing);
1763 }
1764
1765 PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator * colorOperator) {
1766 double c[4];
1767 pdf_int64 v[4];
1768
1769 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1770
1771 bool doubles = true;
1772 if (colorOperator->fColorSpace == "Indexed") {
1773 doubles = false;
1774 }
1775
1776 #ifdef PDF_TRACE
1777 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.c_str(), n);
1778 #endif
1779
1780 for (int i = n - 1; i >= 0 ; i--) {
1781 if (doubles) {
1782 c[i] = pdfContext->fObjectStack.top()->asNumber()->value(); pdfC ontext->fObjectStack.pop();
1783 } else {
1784 v[i] = pdfContext->fObjectStack.top()->asInteger()->value(); pdfCo ntext->fObjectStack.pop();
1785 }
1786 }
1787
1788 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
1789 if (colorOperator->fColorSpace == "DeviceRGB") {
1790 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2]));
1791 }
1792 return kPartial_PdfResult;
1793 }
1794
1795 PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1796 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking );
1797 }
1798
1799 PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1800 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok ing);
1801 }
1802
1803 PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperat or* colorOperator) {
1804 PdfString name;
1805
1806 if (pdfContext->fObjectStack.top()->asName()) {
1807 pdfContext->fObjectStack.pop();
1808 }
1809
1810 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implemen t spec.
1811 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1812
1813 return kPartial_PdfResult;
1814 }
1815
1816 PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** l ooper) {
1817 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroki ng);
1818 }
1819
1820 PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** l ooper) {
1821 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStr oking);
1822 }
1823
1824 PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1825 double gray = pdfContext->fObjectStack.top()->asNumber()->value(); pdfCo ntext->fObjectStack.pop();
1826 return kNYI_PdfResult;
1827 }
1828
1829 PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1830 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1831 }
1832
1833 PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1834 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrokin g);
1835 }
1836
1837 PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator * colorOperator) {
1838 double b = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1839 double g = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1840 double r = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1841
1842 colorOperator->fColorSpace = "DeviceRGB";
1843 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b));
1844 return kOK_PdfResult;
1845 }
1846
1847 PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1848 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking );
1849 }
1850
1851 PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1852 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok ing);
1853 }
1854
1855 PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, PdfColorOperator* colorOperator) {
1856 // TODO(edisonn): spec has some rules about overprint, implement them.
1857 double k = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1858 double y = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1859 double m = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1860 double c = pdfContext->fObjectStack.top()->asNumber()->value(); pdfConte xt->fObjectStack.pop();
1861
1862 colorOperator->fColorSpace = "DeviceCMYK";
1863 // TODO(edisonn): Set color.
1864 return kNYI_PdfResult;
1865 }
1866
1867 PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1868 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1869 }
1870
1871 PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1872 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrokin g);
1873 }
1874
1875 PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1876 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1877 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1878
1879 return kOK_PdfResult;
1880 }
1881
1882 PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper* * looper) {
1883 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1884
1885 #ifdef PDF_TRACE
1886 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) {
1887 printf("CLIP IS RECT\n");
1888 }
1889 #endif
1890
1891 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even o dd.
1892 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1893 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1894
1895 return kPartial_PdfResult;
1896 }
1897
1898 PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1899 *looper = new PdfCompatibilitySectionLooper();
1900 return kOK_PdfResult;
1901 }
1902
1903 PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1904 #ifdef ASSERT_BAD_PDF_OPS
1905 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, b ut let's
1906 // have the assert when testing good pdfs.
1907 #endif
1908 return kIgnoreError_PdfResult;
1909 }
1910
1911 PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1912 *looper = new PdfInlineImageLooper();
1913 return kOK_PdfResult;
1914 }
1915
1916 PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1917 #ifdef ASSERT_BAD_PDF_OPS
1918 SkASSERT(false); // must be processed in inline image looper, but let's
1919 // have the assert when testing good pdfs.
1920 #endif
1921 return kIgnoreError_PdfResult;
1922 }
1923
1924 PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1925 #ifdef ASSERT_BAD_PDF_OPS
1926 SkASSERT(false); // must be processed in inline image looper, but let's
1927 // have the assert when testing good pdfs.
1928 #endif
1929 return kIgnoreError_PdfResult;
1930 }
1931
1932 //lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
1933 PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1934 double lineWidth = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1935 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1936
1937 return kOK_PdfResult;
1938 }
1939
1940 //lineCap J Set the line cap style in the graphics state (see “Line Cap Style” o n page 153).
1941 PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1942 pdfContext->fObjectStack.pop();
1943 //double lineCap = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1944
1945 return kNYI_PdfResult;
1946 }
1947
1948 //lineJoin j Set the line join style in the graphics state (see “Line Join Style ” on page 153).
1949 PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1950 pdfContext->fObjectStack.pop();
1951 //double lineJoin = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1952
1953 return kNYI_PdfResult;
1954 }
1955
1956 //miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on p age 153).
1957 PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1958 pdfContext->fObjectStack.pop();
1959 //double miterLimit = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
1960
1961 return kNYI_PdfResult;
1962 }
1963
1964 //dashArray dashPhase d Set the line dash pattern in the graphics state (see “Li ne Dash Pattern” on
1965 //page 155).
1966 PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1967 pdfContext->fObjectStack.pop();
1968 pdfContext->fObjectStack.pop();
1969
1970 return kNYI_PdfResult;
1971 }
1972
1973 //intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents” on page 197).
1974 PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1975 pdfContext->fObjectStack.pop();
1976
1977 return kNYI_PdfResult;
1978 }
1979
1980 //flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
1981 //Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
1982 //fies the output device’s default flatness tolerance.
1983 PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo per) {
1984 pdfContext->fObjectStack.pop();
1985
1986 return kNYI_PdfResult;
1987 }
1988
1989 //dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictN ame is
1990 //the name of a graphics state parameter dictionary in the ExtGState subdictiona ry of the current resource dictionary (see the next section).
1991 PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
1992 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdf Context->fObjectStack.pop();
1993
1994 #ifdef PDF_TRACE
1995 std::string str;
1996 #endif
1997
1998 //Next, get the ExtGState Dictionary from the Resource Dictionary:
1999 const SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fRes ources->ExtGState();
2000
2001 if (extGStateDictionary == NULL) {
2002 #ifdef PDF_TRACE
2003 printf("ExtGState is NULL!\n");
2004 #endif
2005 return kIgnoreError_PdfResult;
2006 }
2007
2008 SkPdfObject* value = extGStateDictionary->get(name.c_str());
2009
2010 #ifdef PDF_TRACE
2011 // value->ToString(str);
2012 // printf("gs object value: %s\n", str.c_str());
2013 #endif
2014
2015 SkPdfGraphicsStateDictionary* gs = NULL;
2016 PodofoMapper::map(*value, &gs);
2017
2018 // TODO(edisonn): now load all those properties in graphic state.
2019 if (gs == NULL) {
2020 return kIgnoreError_PdfResult;
2021 }
2022
2023 if (gs->has_CA()) {
2024 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA();
2025 }
2026
2027 if (gs->has_ca()) {
2028 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca();
2029 }
2030
2031 if (gs->has_LW()) {
2032 pdfContext->fGraphicsState.fLineWidth = gs->LW();
2033 }
2034
2035
2036
2037 return kNYI_PdfResult;
2038 }
2039
2040 //charSpace Tc Set the character spacing, Tc
2041 //, to charSpace, which is a number expressed in unscaled text space units. Char acter spacing is used by the Tj, TJ, and ' operators.
2042 //Initial value: 0.
2043 PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2044 double charSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2045 pdfContext->fGraphicsState.fCharSpace = charSpace;
2046
2047 return kOK_PdfResult;
2048 }
2049
2050 //wordSpace Tw Set the word spacing, T
2051 //w
2052 //, to wordSpace, which is a number expressed in unscaled
2053 //text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2054 //value: 0.
2055 PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2056 double wordSpace = pdfContext->fObjectStack.top()->asNumber()->value(); pdfContext->fObjectStack.pop();
2057 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2058
2059 return kOK_PdfResult;
2060 }
2061
2062 //scale Tz Set the horizontal scaling, Th
2063 //, to (scale ˜ 100). scale is a number specifying the
2064 //percentage of the normal width. Initial value: 100 (normal width).
2065 PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2066 double scale = pdfContext->fObjectStack.top()->asNumber()->value(); pdfC ontext->fObjectStack.pop();
2067
2068 return kNYI_PdfResult;
2069 }
2070
2071 //render Tr Set the text rendering mode, T
2072 //mode, to render, which is an integer. Initial value: 0.
2073 PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2074 double render = pdfContext->fObjectStack.top()->asNumber()->value(); pdf Context->fObjectStack.pop();
2075
2076 return kNYI_PdfResult;
2077 }
2078 //rise Ts Set the text rise, Trise, to rise, which is a number expressed in unsc aled text space
2079 //units. Initial value: 0.
2080 PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2081 double rise = pdfContext->fObjectStack.top()->asNumber()->value(); pdfCo ntext->fObjectStack.pop();
2082
2083 return kNYI_PdfResult;
2084 }
2085
2086 //wx wy d0
2087 PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2088 pdfContext->fObjectStack.pop();
2089 pdfContext->fObjectStack.pop();
2090
2091 return kNYI_PdfResult;
2092 }
2093
2094 //wx wy llx lly urx ury d1
2095 PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2096 pdfContext->fObjectStack.pop();
2097 pdfContext->fObjectStack.pop();
2098 pdfContext->fObjectStack.pop();
2099 pdfContext->fObjectStack.pop();
2100 pdfContext->fObjectStack.pop();
2101 pdfContext->fObjectStack.pop();
2102
2103 return kNYI_PdfResult;
2104 }
2105
2106 //name sh
2107 PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2108 pdfContext->fObjectStack.pop();
2109
2110 return kNYI_PdfResult;
2111 }
2112
2113 //name Do
2114 PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2115 std::string name = pdfContext->fObjectStack.top()->asName()->value(); pdf Context->fObjectStack.pop();
2116
2117 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject() ;
2118
2119 if (xObject == NULL) {
2120 #ifdef PDF_TRACE
2121 printf("XObject is NULL!\n");
2122 #endif
2123 return kIgnoreError_PdfResult;
2124 }
2125
2126 SkPdfObject* value = xObject->get(name.c_str());
2127
2128 #ifdef PDF_TRACE
2129 // value->ToString(str);
2130 // printf("Do object value: %s\n", str.c_str());
2131 #endif
2132
2133 return doXObject(pdfContext, canvas, *value);
2134 }
2135
2136 //tag MP Designate a marked-content point. tag is a name object indicating the r ole or
2137 //significance of the point.
2138 PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2139 pdfContext->fObjectStack.pop();
2140
2141 return kNYI_PdfResult;
2142 }
2143
2144 //tag properties DP Designate a marked-content point with an associated property list. tag is a
2145 //name object indicating the role or significance of the point; properties is
2146 //either an inline dictionary containing the property list or a name object
2147 //associated with it in the Properties subdictionary of the current resource
2148 //dictionary (see Section 9.5.1, “Property Lists”).
2149 PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo oper) {
2150 pdfContext->fObjectStack.pop();
2151 pdfContext->fObjectStack.pop();
2152
2153 return kNYI_PdfResult;
2154 }
2155
2156 //tag BMC Begin a marked-content sequence terminated by a balancing EMC operator .
2157 //tag is a name object indicating the role or significance of the sequence.
2158 PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** l ooper) {
2159 pdfContext->fObjectStack.pop();
2160
2161 return kNYI_PdfResult;
2162 }
2163
2164 //tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2165 //by a balancing EMCoperator. tag is a name object indicating the role or signif icance of the sequence; propertiesis either an inline dictionary containing the
2166 //property list or a name object associated with it in the Properties subdiction ary of the current resource dictionary (see Section 9.5.1, “Property Lists”).
2167 PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** l ooper) {
2168 pdfContext->fObjectStack.pop();
2169 pdfContext->fObjectStack.pop();
2170
2171 return kNYI_PdfResult;
2172 }
2173
2174 //— EMC End a marked-content sequence begun by a BMC or BDC operator.
2175 PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** l ooper) {
2176 return kNYI_PdfResult;
2177 }
2178
2179 void initPdfOperatorRenderes() {
2180 static bool gInitialized = false;
2181 if (gInitialized) {
2182 return;
2183 }
2184
2185 gPdfOps["q"] = PdfOp_q;
2186 gPdfOps["Q"] = PdfOp_Q;
2187 gPdfOps["cm"] = PdfOp_cm;
2188
2189 gPdfOps["TD"] = PdfOp_TD;
2190 gPdfOps["Td"] = PdfOp_Td;
2191 gPdfOps["Tm"] = PdfOp_Tm;
2192 gPdfOps["T*"] = PdfOp_T_star;
2193
2194 gPdfOps["m"] = PdfOp_m;
2195 gPdfOps["l"] = PdfOp_l;
2196 gPdfOps["c"] = PdfOp_c;
2197 gPdfOps["v"] = PdfOp_v;
2198 gPdfOps["y"] = PdfOp_y;
2199 gPdfOps["h"] = PdfOp_h;
2200 gPdfOps["re"] = PdfOp_re;
2201
2202 gPdfOps["S"] = PdfOp_S;
2203 gPdfOps["s"] = PdfOp_s;
2204 gPdfOps["f"] = PdfOp_f;
2205 gPdfOps["F"] = PdfOp_F;
2206 gPdfOps["f*"] = PdfOp_f_star;
2207 gPdfOps["B"] = PdfOp_B;
2208 gPdfOps["B*"] = PdfOp_B_star;
2209 gPdfOps["b"] = PdfOp_b;
2210 gPdfOps["b*"] = PdfOp_b_star;
2211 gPdfOps["n"] = PdfOp_n;
2212
2213 gPdfOps["BT"] = PdfOp_BT;
2214 gPdfOps["ET"] = PdfOp_ET;
2215
2216 gPdfOps["Tj"] = PdfOp_Tj;
2217 gPdfOps["'"] = PdfOp_quote;
2218 gPdfOps["\""] = PdfOp_doublequote;
2219 gPdfOps["TJ"] = PdfOp_TJ;
2220
2221 gPdfOps["CS"] = PdfOp_CS;
2222 gPdfOps["cs"] = PdfOp_cs;
2223 gPdfOps["SC"] = PdfOp_SC;
2224 gPdfOps["SCN"] = PdfOp_SCN;
2225 gPdfOps["sc"] = PdfOp_sc;
2226 gPdfOps["scn"] = PdfOp_scn;
2227 gPdfOps["G"] = PdfOp_G;
2228 gPdfOps["g"] = PdfOp_g;
2229 gPdfOps["RG"] = PdfOp_RG;
2230 gPdfOps["rg"] = PdfOp_rg;
2231 gPdfOps["K"] = PdfOp_K;
2232 gPdfOps["k"] = PdfOp_k;
2233
2234 gPdfOps["W"] = PdfOp_W;
2235 gPdfOps["W*"] = PdfOp_W_star;
2236
2237 gPdfOps["BX"] = PdfOp_BX;
2238 gPdfOps["EX"] = PdfOp_EX;
2239
2240 gPdfOps["BI"] = PdfOp_BI;
2241 gPdfOps["ID"] = PdfOp_ID;
2242 gPdfOps["EI"] = PdfOp_EI;
2243
2244 gPdfOps["w"] = PdfOp_w;
2245 gPdfOps["J"] = PdfOp_J;
2246 gPdfOps["j"] = PdfOp_j;
2247 gPdfOps["M"] = PdfOp_M;
2248 gPdfOps["d"] = PdfOp_d;
2249 gPdfOps["ri"] = PdfOp_ri;
2250 gPdfOps["i"] = PdfOp_i;
2251 gPdfOps["gs"] = PdfOp_gs;
2252
2253 gPdfOps["Tc"] = PdfOp_Tc;
2254 gPdfOps["Tw"] = PdfOp_Tw;
2255 gPdfOps["Tz"] = PdfOp_Tz;
2256 gPdfOps["TL"] = PdfOp_TL;
2257 gPdfOps["Tf"] = PdfOp_Tf;
2258 gPdfOps["Tr"] = PdfOp_Tr;
2259 gPdfOps["Ts"] = PdfOp_Ts;
2260
2261 gPdfOps["d0"] = PdfOp_d0;
2262 gPdfOps["d1"] = PdfOp_d1;
2263
2264 gPdfOps["sh"] = PdfOp_sh;
2265
2266 gPdfOps["Do"] = PdfOp_Do;
2267
2268 gPdfOps["MP"] = PdfOp_MP;
2269 gPdfOps["DP"] = PdfOp_DP;
2270 gPdfOps["BMC"] = PdfOp_BMC;
2271 gPdfOps["BDC"] = PdfOp_BDC;
2272 gPdfOps["EMC"] = PdfOp_EMC;
2273
2274 gInitialized = true;
2275 }
2276
2277 void reportPdfRenderStats() {
2278 std::map<std::string, int>::iterator iter;
2279
2280 for (int i = 0 ; i < kCount_PdfResult; i++) {
2281 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++it er) {
2282 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_s tr(), iter->second);
2283 }
2284 }
2285 }
2286
2287 PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
2288 if (token.fType == kKeyword_TokenType)
2289 {
2290 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
2291 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[token.fKeyword];
2292 if (pdfOperatorRenderer) {
2293 // caller, main work is done by pdfOperatorRenderer(...)
2294 PdfTokenLooper* childLooper = NULL;
2295 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper) ][token.fKeyword]++;
2296
2297 if (childLooper) {
2298 childLooper->setUp(this);
2299 childLooper->loop();
2300 delete childLooper;
2301 }
2302 } else {
2303 gRenderStats[kUnsupported_PdfResult][token.fKeyword]++;
2304 }
2305 }
2306 else if (token.fType == kObject_TokenType)
2307 {
2308 fPdfContext->fObjectStack.push( token.fObject );
2309 }
2310 else if ( token.fType == kImageData_TokenType) {
2311 // TODO(edisonn): implement inline image.
2312 }
2313 else {
2314 return kIgnoreError_PdfResult;
2315 }
2316 return kOK_PdfResult;
2317 }
2318
2319 void PdfMainLooper::loop() {
2320 PdfToken token;
2321 while (readToken(fTokenizer, &token)) {
2322 consumeToken(token);
2323 }
2324 }
2325
2326 PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
2327 //pdfContext.fInlineImage.fKeyValuePairs[key] = value;
2328 return kNYI_PdfResult;
2329 }
2330
2331 void PdfInlineImageLooper::loop() {
2332 PdfToken token;
2333 while (readToken(fTokenizer, &token)) {
2334 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2335 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2336 looper->setUp(this);
2337 looper->loop();
2338 } else {
2339 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI" ) == 0) {
2340 done();
2341 return;
2342 }
2343
2344 consumeToken(token);
2345 }
2346 }
2347 // TODO(edisonn): report error/warning, EOF without EI.
2348 }
2349
2350 PdfResult PdfInlineImageLooper::done() {
2351
2352 // TODO(edisonn): long to short names
2353 // TODO(edisonn): set properties in a map
2354 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilitie s to uncompress
2355 // the stream.
2356
2357 SkBitmap bitmap;
2358 setup_bitmap(&bitmap, 50, 50, SK_ColorRED);
2359
2360 // TODO(edisonn): matrix use.
2361 // Draw dummy red square, to show the prezence of the inline image.
2362 fCanvas->drawBitmap(bitmap,
2363 SkDoubleToScalar(0),
2364 SkDoubleToScalar(0),
2365 NULL);
2366 return kNYI_PdfResult;
2367 }
2368
2369 PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2370 return fParent->consumeToken(token);
2371 }
2372
2373 void PdfCompatibilitySectionLooper::loop() {
2374 // TODO(edisonn): save stacks position, or create a new stack?
2375 // TODO(edisonn): what happens if we pop out more variables then when we sta rted?
2376 // restore them? fail? We could create a new operands stack for every new BX /EX section,
2377 // pop-ing too much will not affect outside the section.
2378 PdfToken token;
2379 while (readToken(fTokenizer, &token)) {
2380 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2381 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
2382 looper->setUp(this);
2383 looper->loop();
2384 delete looper;
2385 } else {
2386 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX" ) == 0) break;
2387 fParent->consumeToken(token);
2388 }
2389 }
2390 // TODO(edisonn): restore stack.
2391 }
2392
2393 // TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf
2394 // TODO(edisonn): Add API for Forms viewing and editing
2395 // e.g. SkBitmap getPage(int page);
2396 // int formsCount();
2397 // SkForm getForm(int formID); // SkForm(SkRect, .. other data)
2398 // TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsin g all content, ...
2399 // if we load the first page, and we zoom to fit to screen horizontally, then lo ad only those
2400 // resources needed, so the preview is fast.
2401 // TODO (edisonn): hide parser/tokenizer behind and interface and a query langua ge, and resolve
2402 // references automatically.
2403 class SkPdfViewer : public SkRefCnt {
2404 public:
2405
2406 bool load(const SkString inputFileName, SkPicture* out) {
2407
2408 initPdfOperatorRenderes();
2409
2410 try
2411 {
2412 std::cout << "Init: " << inputFileName.c_str() << std::endl;
2413
2414 SkPdfDoc doc(inputFileName.c_str());
2415 if( !doc.pages() )
2416 {
2417 std::cout << "ERROR: Empty Document" << inputFileName.c_str() << std ::endl;
2418 return false;
2419 } else {
2420
2421 for (int pn = 0; pn < doc.pages(); ++pn) {
2422 SkPdfPageObjectDictionary* page = doc.page(pn);
2423
2424 // TODO(edisonn): implement inheritance properties as per PDF sp ec
2425 //SkRect rect = page->MediaBox();
2426 SkRect rect = doc.MediaBox(pn);
2427
2428 #ifdef PDF_TRACE
2429 printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rec t.width()), SkScalarToDouble(rect.height()));
2430 #endif
2431
2432 // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to use?
2433
2434 SkBitmap bitmap;
2435 #ifdef PDF_DEBUG_3X
2436 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()));
2437 #else
2438 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int) SkScalarToDouble(rect.height()));
2439 #endif
2440 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
2441 SkCanvas canvas(device);
2442
2443 SkPdfTokenizer* tokenizer = doc.tokenizerOfPage(pn);
2444
2445 PdfContext pdfContext(&doc);
2446 pdfContext.fOriginalMatrix = SkMatrix::I();
2447 pdfContext.fGraphicsState.fResources = NULL;
2448 PodofoMapper::map(*page->Resources(), &pdfContext.fGraphicsState .fResources);
2449
2450 gPdfContext = &pdfContext;
2451 gDumpBitmap = &bitmap;
2452 gDumpCanvas = &canvas;
2453
2454
2455 // TODO(edisonn): get matrix stuff right.
2456 // TODO(edisonn): add DPI/scale/zoom.
2457 SkScalar z = SkIntToScalar(0);
2458 SkScalar w = rect.width();
2459 SkScalar h = rect.height();
2460
2461 SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(w, z), SkPoint::Make(w, h), SkPoint::Make(z, h)};
2462 // SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
2463
2464 // TODO(edisonn): add flag for this app to create sourunding buf fer zone
2465 // TODO(edisonn): add flagg for no clipping.
2466 // Use larger image to make sure we do not draw anything outside of page
2467 // could be used in tests.
2468
2469 #ifdef PDF_DEBUG_3X
2470 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)};
2471 #else
2472 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h) , SkPoint::Make(w, z), SkPoint::Make(z, z)};
2473 #endif
2474 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h )};
2475 //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
2476
2477 //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h )};
2478 //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
2479
2480 //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h ), SkPoint::Make(w, h)};
2481 //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
2482
2483 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace , skiaSpace, 4));
2484 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2485
2486
2487 pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
2488 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState. fMatrix;
2489 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState .fMatrix;
2490
2491 canvas.setMatrix(pdfContext.fOriginalMatrix);
2492
2493 #ifndef PDF_DEBUG_NO_PAGE_CLIPING
2494 canvas.clipRect(SkRect::MakeXYWH(z, z, w, h), SkRegion::kInterse ct_Op, true);
2495 #endif
2496
2497 PdfMainLooper looper(NULL, tokenizer, &pdfContext, &canvas);
2498 looper.loop();
2499
2500 delete tokenizer;
2501
2502 canvas.flush();
2503
2504 SkString out;
2505 out.appendf("%s-%i.png", inputFileName.c_str(), pn);
2506 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder:: kPNG_Type, 100);
2507 }
2508 return true;
2509 }
2510 }
2511 catch( PdfError & e )
2512 {
2513 std::cout << "ERROR: PDF can't be parsed!" << inputFileName.c_str() << s td::endl;
2514 return false;
2515 }
2516
2517 return true;
2518 }
2519 bool write(void*) const { return false; }
2520 };
2521
2522
2523
2524 /** 16 /**
2525 * Given list of directories and files to use as input, expects to find .pdf 17 * Given list of directories and files to use as input, expects to find .pdf
2526 * files and it will convert them to .png files writing them in the same directo ry 18 * files and it will convert them to .png files writing them in the same directo ry
2527 * one file for each page. 19 * one file for each page.
2528 * 20 *
2529 * Returns zero exit code if all .pdf files were converted successfully, 21 * Returns zero exit code if all .pdf files were converted successfully,
2530 * otherwise returns error code 1. 22 * otherwise returns error code 1.
2531 */ 23 */
2532 24
2533 static const char PDF_FILE_EXTENSION[] = "pdf"; 25 static const char PDF_FILE_EXTENSION[] = "pdf";
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
2725 } 217 }
2726 218
2727 return 0; 219 return 0;
2728 } 220 }
2729 221
2730 #if !defined SK_BUILD_FOR_IOS 222 #if !defined SK_BUILD_FOR_IOS
2731 int main(int argc, char * const argv[]) { 223 int main(int argc, char * const argv[]) {
2732 return tool_main(argc, (char**) argv); 224 return tool_main(argc, (char**) argv);
2733 } 225 }
2734 #endif 226 #endif
OLDNEW
« no previous file with comments | « experimental/PdfViewer/generate_code.py ('k') | experimental/PdfViewer/spec2def.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698