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