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

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

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

Powered by Google App Engine
This is Rietveld 408576698