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