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 "SkPdfRenderer.h" 9 10 #include "SkBitmapDevice.h" 11 #include "SkCanvas.h" 12 #include "SkColorPriv.h" 13 #include "SkDevice.h" 14 #include "SkForceLinking.h" 15 #include "SkGraphics.h" 16 #include "SkImageDecoder.h" 17 #include "SkImageEncoder.h" 18 #include "SkOSFile.h" 19 #include "SkPicture.h" 20 #include "SkPdfFont.h" 21 #include "SkPdfGraphicsState.h" 22 #include "SkPdfHeaders_autogen.h" 23 #include "SkPdfMapper_autogen.h" 24 #include "SkPdfNativeTokenizer.h" 25 #include "SkPdfRenderer.h" 26 #include "SkPdfReporter.h" 27 #include "SkPdfTokenLooper.h" 28 #include "SkPdfUtils.h" 29 #include "SkStream.h" 30 #include "SkTypeface.h" 31 #include "SkTArray.h" 32 #include "SkTDict.h" 33 34 // TODO(edisonn): #ifdef these ones, as they are used only for debugging. 35 extern "C" SkPdfContext* gPdfContext; 36 37 __SK_FORCE_IMAGE_DECODER_LINKING; 38 39 // TODO(edisonn): tool, show what objects were read during rendering - will help to identify 40 // features with incomplete implementation 41 // TODO(edisonn): security - validate all the user input, all pdf! 42 // TODO(edisonn): testability -add option to render without text, or only render text 43 44 // Helper macros to load variables from stack, and automatically check their type. 45 #define EXPECT_OPERANDS(name,pdfContext,n) \ 46 bool __failed = pdfContext->fObjectStack.count() < n; \ 47 SkPdfREPORTCODE(const char* __operator_name = name); \ 48 SkPdfREPORTCODE((void)__operator_name); \ 49 SkPdfReportIf(pdfContext->fObjectStack.count() < n, \ 50 kIgnoreError_SkPdfIssueSeverity, \ 51 kStackOverflow_SkPdfIssue, \ 52 "Not enought parameters.", NULL, pdfContext); \ 53 SkDEBUGCODE(int __cnt = n); 54 55 #define POP_OBJ(pdfContext,name) \ 56 SkDEBUGCODE(__cnt--); \ 57 SkASSERT(__cnt >= 0); \ 58 SkPdfNativeObject* name = NULL; \ 59 __failed = __failed || pdfContext->fObjectStack.count() == 0; \ 60 if (pdfContext->fObjectStack.count() > 0) { \ 61 name = pdfContext->fObjectStack.top(); \ 62 pdfContext->fObjectStack.pop(); \ 63 } 64 65 // TODO(edisonn): make all pop function to use name##_obj 66 #define POP_NUMBER(pdfContext,name) \ 67 SkDEBUGCODE(__cnt--); \ 68 SkASSERT(__cnt >= 0); \ 69 double name = 0; \ 70 SkPdfNativeObject* name##_obj = NULL; \ 71 __failed = __failed || pdfContext->fObjectStack.count() == 0; \ 72 if (pdfContext->fObjectStack.count() > 0) { \ 73 name##_obj = pdfContext->fObjectStack.top(); \ 74 pdfContext->fObjectStack.pop(); \ 75 if (!name##_obj || !name##_obj->isNumber()) { \ 76 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \ 77 __operator_name, \ 78 name##_obj, \ 79 SkPdfNativeObject::_kNumber_PdfObjectType, \ 80 NULL);\ 81 __failed = true;\ 82 } else { \ 83 name = name##_obj->numberValue(); \ 84 } \ 85 } 86 87 #define POP_INTEGER(pdfContext,name) \ 88 SkDEBUGCODE(__cnt--); \ 89 SkASSERT(__cnt >= 0); \ 90 int64_t name = 0; \ 91 __failed = __failed || pdfContext->fObjectStack.count() == 0; \ 92 SkPdfNativeObject* name##_obj = NULL; \ 93 if (pdfContext->fObjectStack.count() > 0) { \ 94 name##_obj = pdfContext->fObjectStack.top(); \ 95 pdfContext->fObjectStack.pop(); \ 96 if (!name##_obj || !name##_obj->isInteger()) { \ 97 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \ 98 __operator_name, \ 99 name##_obj, \ 100 SkPdfNativeObject::kInteger_PdfObjectType, \ 101 NULL);\ 102 __failed = true;\ 103 } else { \ 104 name = name##_obj->intValue(); \ 105 } \ 106 } 107 108 #define POP_NUMBER_INTO(pdfContext,var) \ 109 SkDEBUGCODE(__cnt--); \ 110 SkASSERT(__cnt >= 0); \ 111 __failed = __failed || pdfContext->fObjectStack.count() == 0; \ 112 if (pdfContext->fObjectStack.count() > 0) { \ 113 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \ 114 pdfContext->fObjectStack.pop(); \ 115 if (!tmp || !tmp->isNumber()) { \ 116 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \ 117 __operator_name, \ 118 tmp, \ 119 SkPdfNativeObject::kInteger_PdfObjectType | \ 120 SkPdfNativeObject::kReal_PdfObjectType, \ 121 NULL);\ 122 __failed = true;\ 123 } else { \ 124 var = tmp->numberValue(); \ 125 } \ 126 } 127 128 129 #define POP_NAME(pdfContext,name) \ 130 SkDEBUGCODE(__cnt--); \ 131 SkASSERT(__cnt >= 0); \ 132 SkPdfNativeObject* name = NULL; \ 133 __failed = __failed || pdfContext->fObjectStack.count() == 0; \ 134 if (pdfContext->fObjectStack.count() > 0) { \ 135 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \ 136 pdfContext->fObjectStack.pop(); \ 137 if (!tmp || !tmp->isName()) { \ 138 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \ 139 __operator_name, \ 140 tmp, \ 141 SkPdfNativeObject::kName_PdfObjectType, \ 142 NULL);\ 143 __failed = true;\ 144 } else { \ 145 name = tmp; \ 146 } \ 147 } 148 149 #define POP_STRING(pdfContext,name) \ 150 SkDEBUGCODE(__cnt--); \ 151 SkASSERT(__cnt >= 0); \ 152 SkPdfNativeObject* name = NULL; \ 153 __failed = __failed || pdfContext->fObjectStack.count() == 0; \ 154 if (pdfContext->fObjectStack.count() > 0) { \ 155 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \ 156 pdfContext->fObjectStack.pop(); \ 157 if (!tmp || !tmp->isAnyString()) { \ 158 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \ 159 __operator_name, \ 160 tmp, \ 161 SkPdfNativeObject::kString_PdfObjectType | \ 162 SkPdfNativeObject::kHexString_PdfObjectType, \ 163 NULL);\ 164 __failed = true;\ 165 } else { \ 166 name = tmp; \ 167 } \ 168 } 169 170 #define POP_ARRAY(pdfContext,name) \ 171 SkDEBUGCODE(__cnt--); \ 172 SkASSERT(__cnt >= 0); \ 173 SkPdfArray* name = NULL; \ 174 __failed = __failed || pdfContext->fObjectStack.count() == 0; \ 175 if (pdfContext->fObjectStack.count() > 0) { \ 176 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \ 177 pdfContext->fObjectStack.pop(); \ 178 if (!tmp || !tmp->isArray()) { \ 179 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \ 180 __operator_name, \ 181 tmp, \ 182 SkPdfNativeObject::kArray_PdfObjectType, \ 183 NULL);\ 184 __failed = true;\ 185 } else { \ 186 name = (SkPdfArray*)tmp; \ 187 } \ 188 } 189 190 #define CHECK_PARAMETERS() \ 191 SkASSERT(__cnt == 0); \ 192 if (__failed) return kIgnoreError_SkPdfResult; 193 194 195 NotOwnedString strings_DeviceRGB; 196 NotOwnedString strings_DeviceCMYK; 197 198 class StringsInit { 199 public: 200 StringsInit() { 201 NotOwnedString::init(&strings_DeviceRGB, "DeviceRGB"); 202 NotOwnedString::init(&strings_DeviceCMYK, "DeviceCMYK"); 203 } 204 }; 205 206 // TODO(edisonn): this will not work in chrome! Find another solution! 207 StringsInit gStringsInit; 208 209 // TODO(edisonn): Document SkPdfTokenLooper and subclasses. 210 class PdfInlineImageLooper : public SkPdfTokenLooper { 211 public: 212 explicit PdfInlineImageLooper(SkPdfTokenLooper* parent) 213 : INHERITED(parent) {} 214 215 SkPdfResult consumeToken(PdfToken& token) override; 216 void loop() override; 217 218 private: 219 typedef SkPdfTokenLooper INHERITED; 220 }; 221 222 class PdfCompatibilitySectionLooper : public SkPdfTokenLooper { 223 public: 224 explicit PdfCompatibilitySectionLooper(SkPdfTokenLooper* parent) 225 : INHERITED (parent) {} 226 227 SkPdfResult consumeToken(PdfToken& token) override; 228 void loop() override; 229 230 private: 231 typedef SkPdfTokenLooper INHERITED; 232 }; 233 234 // Utilities 235 static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) { 236 bitmap->allocN32Pixels(width, height); 237 bitmap->eraseColor(color); 238 } 239 240 // TODO(edisonn): synonyms? /DeviceRGB and /RGB mean the same thing. Context dependent. 241 static int GetColorSpaceComponents(NotOwnedString& colorSpace) { 242 if (colorSpace.equals("DeviceCMYK")) { 243 return 4; 244 } else if (colorSpace.equals("DeviceGray") || 245 colorSpace.equals("CalGray") || 246 colorSpace.equals("Indexed")) { 247 return 1; 248 } else if (colorSpace.equals("DeviceRGB") || 249 colorSpace.equals("CalRGB") || 250 colorSpace.equals("Lab")) { 251 return 3; 252 } else { 253 return 0; 254 } 255 } 256 257 SkMatrix SkMatrixFromPdfMatrix(double array[6]) { 258 SkMatrix matrix; 259 matrix.setAll(SkDoubleToScalar(array[0]), 260 SkDoubleToScalar(array[2]), 261 SkDoubleToScalar(array[4]), 262 SkDoubleToScalar(array[1]), 263 SkDoubleToScalar(array[3]), 264 SkDoubleToScalar(array[5]), 265 SkDoubleToScalar(0), 266 SkDoubleToScalar(0), 267 SkDoubleToScalar(1)); 268 269 return matrix; 270 } 271 272 SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) { 273 double array[6]; 274 275 // TODO(edisonn): security issue, ret if size() != 6 276 if (pdfArray == NULL) { 277 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue, 278 "null array passed to build matrix", NULL, NULL); 279 return SkMatrix::I(); 280 } 281 282 if (pdfArray->size() != 6) { 283 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnexpectedArraySize_SkPdfIssue, 284 "null array passed to build matrix", pdfArray, NULL); 285 return SkMatrix::I(); 286 } 287 288 for (int i = 0; i < 6; i++) { 289 const SkPdfNativeObject* elem = pdfArray->operator [](i); 290 if (elem == NULL || !elem->isNumber()) { 291 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, elem, 292 SkPdfNativeObject::_kNumber_PdfObjectType, NULL); 293 return SkMatrix::I(); 294 } 295 array[i] = elem->numberValue(); 296 } 297 298 return SkMatrixFromPdfMatrix(array); 299 } 300 301 // TODO(edisonn): debug code, used to analyze rendering when we find bugs. 302 extern "C" SkPdfNativeDoc* gDoc; 303 304 static SkPdfResult DrawText(SkPdfContext* pdfContext, 305 const SkPdfNativeObject* _str, 306 SkCanvas* canvas) 307 { 308 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont; 309 if (skfont == NULL) { 310 skfont = SkPdfFont::Default(); 311 } 312 313 if (_str == NULL || !_str->isAnyString()) { 314 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, 315 "DrawText", 316 _str, 317 SkPdfNativeObject::_kAnyString_PdfObjectType, 318 pdfContext); 319 return kIgnoreError_SkPdfResult; 320 } 321 const SkPdfString* str = (const SkPdfString*)_str; 322 323 SkUnencodedText binary(str); 324 325 SkDecodedText decoded; 326 327 if (skfont->encoding() == NULL) { 328 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingEncoding_SkPdfIssue, 329 "draw text", _str, pdfContext); 330 return kNYI_SkPdfResult; 331 } 332 333 skfont->encoding()->decodeText(binary, &decoded); 334 335 SkPaint paint; 336 // TODO(edisonn): does size 0 mean anything special? 337 if (pdfContext->fGraphicsState.fCurFontSize != 0) { 338 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize)); 339 } 340 341 // TODO(edisonn): implement font scaler 342 // if (fCurFont && fCurFont->GetFontScale() != 0) { 343 // paint.setTextScaleX(fCurFont->GetFontScale() / 100.0); 344 // } 345 346 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); 347 348 skfont->drawText(decoded, &paint, pdfContext, canvas); 349 350 return kOK_SkPdfResult; 351 } 352 353 // TODO(edisonn): create header files with declarations! 354 SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper* parentLooper); 355 SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper* parentLooper); 356 SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper* parentLooper); 357 SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper* parentLooper); 358 359 // TODO(edisonn): perf!!! 360 static SkColorTable* getGrayColortable() { 361 static SkColorTable* grayColortable = NULL; 362 if (grayColortable == NULL) { 363 SkPMColor* colors = new SkPMColor[256]; 364 for (int i = 0 ; i < 256; i++) { 365 colors[i] = SkPreMultiplyARGB(255, i, i, i); 366 } 367 grayColortable = new SkColorTable(colors, 256); 368 } 369 return grayColortable; 370 } 371 372 static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream, 373 size_t uncompressedStreamLength, 374 int width, int height, int bytesPerLine, 375 int bpc, const SkString& colorSpace, 376 bool transparencyMask) { 377 SkBitmap* bitmap = new SkBitmap(); 378 379 //int components = GetColorSpaceComponents(colorSpace); 380 //#define MAX_COMPONENTS 10 381 382 // TODO(edisonn): assume start of lines are aligned at 32 bits? 383 // Is there a faster way to load the uncompressed stream into a bitmap? 384 385 // minimal support for now 386 if ((colorSpace.equals("DeviceRGB") || colorSpace.equals("RGB")) && bpc == 8) { 387 uint32_t* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(uint32_t)); 388 389 for (int h = 0 ; h < height; h++) { 390 long i = width * (h); 391 for (int w = 0 ; w < width; w++) { 392 uncompressedStreamArgb[i] = SkPackARGB32(0xFF, 393 uncompressedStream[3 * w], 394 uncompressedStream[3 * w + 1], 395 uncompressedStream[3 * w + 2]); 396 i++; 397 } 398 uncompressedStream += bytesPerLine; 399 } 400 401 const SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 402 bitmap->installPixels(info, uncompressedStreamArgb, info.minRowBytes()); 403 } 404 else if ((colorSpace.equals("DeviceGray") || colorSpace.equals("Gray")) && bpc == 8) { 405 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height); 406 407 for (int h = 0 ; h < height; h++) { 408 long i = width * (h); 409 for (int w = 0 ; w < width; w++) { 410 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] : 411 uncompressedStream[w]; 412 i++; 413 } 414 uncompressedStream += bytesPerLine; 415 } 416 417 const SkColorType ct = transparencyMask ? kAlpha_8_SkColorType : kIndex_8_SkColorType; 418 const SkImageInfo info = SkImageInfo::Make(width, height, ct, kPremul_SkAlphaType); 419 bitmap->installPixels(info, uncompressedStreamA8, info.minRowBytes(), 420 transparencyMask ? NULL : getGrayColortable(), NULL, NULL); 421 } 422 423 // TODO(edisonn): pass color space and context here? 424 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI", NULL, NULL); 425 return bitmap; 426 } 427 // TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest 428 // skia format. 429 430 // This functions returns the image, it does not look at the smask. 431 static SkBitmap* getImageFromObjectCore(SkPdfContext* pdfContext, 432 SkPdfImageDictionary* image, bool transparencyMask) { 433 if (image == NULL || !image->hasStream()) { 434 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", image, 435 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext); 436 return NULL; 437 } 438 439 int bpc = (int)image->BitsPerComponent(pdfContext->fPdfDoc); 440 int width = (int)image->Width(pdfContext->fPdfDoc); 441 int height = (int)image->Height(pdfContext->fPdfDoc); 442 SkString colorSpace("DeviceRGB"); 443 444 bool indexed = false; 445 SkPMColor colors[256]; 446 int cnt = 0; 447 448 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) { 449 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc); 450 } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) { 451 SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc); 452 if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") && 453 (array->objAtAIndex(1)->isName("DeviceRGB") || 454 array->objAtAIndex(1)->isName("RGB")) && 455 array->objAtAIndex(2)->isInteger() && 456 array->objAtAIndex(3)->isHexString() 457 ) { 458 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI", 459 image, pdfContext); 460 indexed = true; 461 cnt = (int)array->objAtAIndex(2)->intValue() + 1; 462 if (cnt > 256) { 463 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, 464 "Color space feature NYI, cnt > 256", image, pdfContext); 465 return NULL; 466 } 467 NotOwnedString data = array->objAtAIndex(3)->strRef(); 468 if (data.fBytes != (unsigned int)cnt * 3) { 469 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, 470 "Image color table mismatch color space specs", array, pdfContext); 471 return NULL; 472 } 473 for (int i = 0 ; i < cnt; i++) { 474 colors[i] = SkPreMultiplyARGB(0xff, 475 data.fBuffer[3 * i], 476 data.fBuffer[3 * i + 1], 477 data.fBuffer[3 * i + 2]); 478 } 479 } 480 } 481 482 // TODO(edisonn): implement image masks. 483 /* bool imageMask = image->imageMask(); 484 if (imageMask) { 485 if (bpc != 0 && bpc != 1) { 486 // TODO(edisonn): report warning to be used in testing. 487 return SkBitmap(); 488 } 489 bpc = 1; 490 } 491 */ 492 493 const unsigned char* uncompressedStream = NULL; 494 size_t uncompressedStreamLength = 0; 495 496 SkPdfStream* stream = (SkPdfStream*)image; 497 498 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) || 499 uncompressedStream == NULL || uncompressedStreamLength == 0) { 500 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", stream, 501 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext); 502 return NULL; 503 } 504 505 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream; 506 507 if (streamDict->has_Filter() && 508 ((streamDict->isFilterAName(NULL) && 509 streamDict->getFilterAsName(NULL).equals("DCTDecode")) || 510 (streamDict->isFilterAArray(NULL) && 511 streamDict->getFilterAsArray(NULL)->size() > 0 && 512 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() && 513 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() 514 .equals("DCTDecode")))) { 515 SkBitmap* bitmap = new SkBitmap(); 516 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap); 517 return bitmap; 518 } 519 520 // TODO(edisonn): assumes RGB for now, since it is the only one implemented 521 if (indexed) { 522 SkBitmap* bitmap = new SkBitmap(); 523 const SkImageInfo info = SkImageInfo::Make(width, height, kIndex_8_SkColorType, 524 kPremul_SkAlphaType); 525 SkAutoTUnref<SkColorTable> colorTable(new SkColorTable(colors, cnt)); 526 bitmap->installPixels(info, (void*)uncompressedStream, info.minRowBytes(), colorTable, 527 NULL, NULL); 528 return bitmap; 529 } 530 531 int bytesPerLine = (int)(uncompressedStreamLength / height); 532 #ifdef PDF_TRACE 533 if (uncompressedStreamLength % height != 0) { 534 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n"); 535 } 536 #endif 537 538 SkBitmap* bitmap = transferImageStreamToBitmap( 539 (unsigned char*)uncompressedStream, uncompressedStreamLength, 540 (int)width, (int)height, bytesPerLine, 541 (int)bpc, colorSpace, 542 transparencyMask); 543 544 return bitmap; 545 } 546 547 static SkBitmap* getImageFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* image, 548 bool transparencyMask) { 549 if (!transparencyMask) { 550 if (!image->hasData(SkPdfNativeObject::kBitmap_Data)) { 551 SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask); 552 image->setData(bitmap, SkPdfNativeObject::kBitmap_Data); 553 } 554 return (SkBitmap*) image->data(SkPdfNativeObject::kBitmap_Data); 555 } else { 556 return getImageFromObjectCore(pdfContext, image, transparencyMask); 557 } 558 } 559 560 static SkBitmap* getSmaskFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* obj) { 561 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc); 562 563 if (sMask) { 564 return getImageFromObject(pdfContext, sMask, true); 565 } 566 567 // TODO(edisonn): implement GS SMask. Default to empty right now. 568 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, 569 "implement GS SMask. Default to empty right now.", obj, pdfContext); 570 571 return pdfContext->fGraphicsState.fSMask; 572 } 573 574 static SkPdfResult doXObject_Image(SkPdfContext* pdfContext, SkCanvas* canvas, 575 SkPdfImageDictionary* skpdfimage) { 576 if (skpdfimage == NULL) { 577 return kIgnoreError_SkPdfResult; 578 } 579 580 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false); 581 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage); 582 583 canvas->save(); 584 canvas->setMatrix(pdfContext->fGraphicsState.fCTM); 585 586 SkScalar z = SkIntToScalar(0); 587 SkScalar one = SkIntToScalar(1); 588 589 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), 590 SkPoint::Make(one, one), SkPoint::Make(z, one)}; 591 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), 592 SkPoint::Make(one, z), SkPoint::Make(z, z)}; 593 SkMatrix flip; 594 SkAssertResult(flip.setPolyToPoly(from, to, 4)); 595 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM; 596 solveImageFlip.preConcat(flip); 597 canvas->setMatrix(solveImageFlip); 598 599 #ifdef PDF_TRACE 600 SkPoint final[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), 601 SkPoint::Make(one, one), SkPoint::Make(z, one)}; 602 solveImageFlip.mapPoints(final, 4); 603 printf("IMAGE rect = "); 604 for (int i = 0; i < 4; i++) { 605 printf("(%f %f) ", SkScalarToDouble(final[i].x()), SkScalarToDouble(final[i].y())); 606 } 607 printf("\n"); 608 #endif // PDF_TRACE 609 610 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), 611 SkDoubleToScalar(1.0), SkDoubleToScalar(1.0)); 612 613 // TODO(edisonn): soft mask type? alpha/luminosity. 614 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, 615 "implement soft mask type", skpdfimage, pdfContext); 616 617 SkPaint paint; 618 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); 619 620 if (!sMask || sMask->empty()) { 621 canvas->drawBitmapRect(*image, dst, &paint); 622 } else { 623 canvas->saveLayer(&dst, &paint); 624 canvas->drawBitmapRect(*image, dst, NULL); 625 SkPaint xfer; 626 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); 627 canvas->drawBitmapRect(*sMask, dst, &xfer); 628 canvas->restore(); 629 } 630 631 canvas->restore(); 632 633 return kPartial_SkPdfResult; 634 } 635 636 //TODO(edisonn): options for implementing isolation and knockout 637 // 1) emulate them (current solution) 638 // PRO: simple 639 // CON: will need to use readPixels, which means serious perf issues 640 // 2) Compile a plan for an array of matrixes, compose the result at the end 641 // PRO: might be faster then 1, no need to readPixels 642 // CON: multiple drawings (but on smaller areas), pay a price at loading pdf to 643 // compute a pdf draw plan 644 // on average, a load with empty draw is 100ms on all the skps we have, for complete sites 645 // 3) support them natively in SkCanvas 646 // PRO: simple 647 // CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1) 648 // 4) compile a plan using pathops, and render once without any fancy rules with backdrop 649 // PRO: simple, fast 650 // CON: pathops must be bug free first + time to compute new paths 651 // pay a price at loading pdf to compute a pdf draw plan 652 // on average, a load with empty draw is 100ms on all the skps we have, for complete sites 653 // 5) for knockout, render the objects in reverse order, and add every object to the clip, and any 654 // new draw will be cliped 655 656 static void doGroup_before(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, 657 SkPdfTransparencyGroupDictionary* tgroup, bool page) { 658 SkRect bboxOrig = bbox; 659 SkBitmap backdrop; 660 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc); 661 // bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc); 662 SkPaint paint; 663 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); 664 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL); 665 } 666 667 // TODO(edisonn): non isolation should probably be implemented in skia 668 //static void doGroup_after(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox, 669 // SkPdfTransparencyGroupDictionary* tgroup) { 670 // if not isolated 671 // canvas->drawBitmapRect(backdrop, bboxOrig, NULL); 672 //} 673 674 static SkPdfResult doXObject_Form(SkPdfContext* pdfContext, SkCanvas* canvas, 675 SkPdfType1FormDictionary* skobj) { 676 if (!skobj || !skobj->hasStream()) { 677 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj, 678 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext); 679 return kIgnoreError_SkPdfResult; 680 } 681 682 if (!skobj->has_BBox()) { 683 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox", 684 skobj, pdfContext); 685 return kIgnoreError_SkPdfResult; 686 } 687 688 PdfOp_q(pdfContext, canvas, NULL); 689 690 691 if (skobj->Resources(pdfContext->fPdfDoc)) { 692 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc); 693 } 694 695 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix"); 696 697 if (skobj->has_Matrix()) { 698 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc)); 699 SkMatrix matrix = pdfContext->fGraphicsState.fCTM; 700 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1)); 701 pdfContext->fGraphicsState.fMatrixTm = matrix; 702 pdfContext->fGraphicsState.fMatrixTlm = matrix; 703 // TODO(edisonn): text matrixes mosltly NYI 704 } 705 706 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix"); 707 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM; 708 709 canvas->setMatrix(pdfContext->fGraphicsState.fCTM); 710 711 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc); 712 // TODO(edisonn): constants (AA) from settings. 713 canvas->clipRect(bbox, SkRegion::kIntersect_Op, false); 714 715 // This is a group? 716 if (skobj->has_Group()) { 717 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc); 718 doGroup_before(pdfContext, canvas, bbox, tgroup, false); 719 } 720 721 SkPdfStream* stream = (SkPdfStream*)skobj; 722 723 pdfContext->parseStream(stream, canvas); 724 725 if (skobj->has_Group()) { 726 canvas->restore(); 727 } 728 729 PdfOp_Q(pdfContext, canvas, NULL); 730 return kPartial_SkPdfResult; 731 } 732 733 static SkPdfResult doXObject_Pattern(SkPdfContext* pdfContext, SkCanvas* canvas, 734 SkPdfType1PatternDictionary* skobj) { 735 if (!skobj || !skobj->hasStream()) { 736 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", 737 skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext); 738 return kIgnoreError_SkPdfResult; 739 } 740 741 if (!skobj->has_BBox()) { 742 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox", 743 skobj, pdfContext); 744 return kIgnoreError_SkPdfResult; 745 } 746 747 PdfOp_q(pdfContext, canvas, NULL); 748 749 750 if (skobj->Resources(pdfContext->fPdfDoc)) { 751 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc); 752 } 753 754 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix"); 755 756 if (skobj->has_Matrix()) { 757 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat( 758 skobj->Matrix(pdfContext->fPdfDoc)); 759 } 760 761 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix"); 762 763 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix); 764 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix; 765 766 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc); 767 // TODO(edisonn): constants (AA) from settings. 768 canvas->clipRect(bbox, SkRegion::kIntersect_Op, false); 769 770 SkPdfStream* stream = (SkPdfStream*)skobj; 771 772 pdfContext->parseStream(stream, canvas); 773 774 PdfOp_Q(pdfContext, canvas, NULL); 775 return kPartial_SkPdfResult; 776 } 777 778 // TODO(edisonn): PS NYI 779 //static SkPdfResult doXObject_PS(SkPdfContext* pdfContext, SkCanvas* canvas, 780 // const SkPdfNativeObject* obj) { 781 // return kNYI_SkPdfResult; 782 //} 783 784 SkPdfResult doType3Char(SkPdfContext* pdfContext, SkCanvas* canvas, const SkPdfNativeObject* skobj, 785 SkRect bBox, SkMatrix matrix, double textSize) { 786 if (!skobj || !skobj->hasStream()) { 787 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj, 788 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext); 789 return kIgnoreError_SkPdfResult; 790 } 791 792 PdfOp_q(pdfContext, canvas, NULL); 793 794 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); 795 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), 796 SkDoubleToScalar(textSize)); 797 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrixTm; 798 799 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm; 800 pdfContext->fGraphicsState.fCTM.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1)); 801 802 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix"); 803 804 canvas->setMatrix(pdfContext->fGraphicsState.fCTM); 805 806 SkRect rm = bBox; 807 pdfContext->fGraphicsState.fCTM.mapRect(&rm); 808 809 SkTraceRect(rm, "bbox mapped"); 810 811 // TODO(edisonn): constants (AA) from settings. 812 canvas->clipRect(bBox, SkRegion::kIntersect_Op, false); 813 814 SkPdfStream* stream = (SkPdfStream*)skobj; 815 816 pdfContext->parseStream(stream, canvas); 817 818 PdfOp_Q(pdfContext, canvas, NULL); 819 820 return kPartial_SkPdfResult; 821 } 822 823 // The PDF could be corrupted so a dict refers recursively to the same dict, if this happens 824 // we end up with a stack overflow and crash. 825 class CheckRecursiveRendering { 826 SkPdfNativeObject* fObj; 827 public: 828 CheckRecursiveRendering(SkPdfNativeObject* obj) : fObj(obj) { 829 SkASSERT(!obj->inRendering()); 830 obj->startRendering(); 831 } 832 833 ~CheckRecursiveRendering() { 834 SkASSERT(fObj->inRendering()); 835 fObj->doneRendering(); 836 } 837 838 static bool IsInRendering(const SkPdfNativeObject* obj) { 839 return obj->inRendering(); 840 } 841 }; 842 843 static SkPdfResult doXObject(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfNativeObject* obj) { 844 if (CheckRecursiveRendering::IsInRendering(obj)) { 845 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kRecursiveReferencing_SkPdfIssue, 846 "Recursive reverencing is invalid in draw objects", obj, pdfContext); 847 return kIgnoreError_SkPdfResult; 848 } 849 850 CheckRecursiveRendering checkRecursion(obj); 851 852 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj)) 853 { 854 case kImageDictionary_SkPdfNativeObjectType: 855 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj); 856 case kType1FormDictionary_SkPdfNativeObjectType: 857 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj); 858 //case kObjectDictionaryXObjectPS_SkPdfNativeObjectType: 859 //return doXObject_PS(skxobj.asPS()); 860 default: { 861 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != 862 kNone_SkPdfNativeObjectType) { 863 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj; 864 return doXObject_Pattern(pdfContext, canvas, pattern); 865 } 866 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "doXObject", 867 obj, pdfContext); 868 } 869 } 870 return kIgnoreError_SkPdfResult; 871 } 872 873 static SkPdfResult doPage(SkPdfContext* pdfContext, SkCanvas* canvas, 874 SkPdfPageObjectDictionary* skobj) { 875 if (!skobj || !skobj->isContentsAStream(pdfContext->fPdfDoc)) { 876 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj, 877 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext); 878 return kNYI_SkPdfResult; 879 } 880 881 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc); 882 883 if (!stream) { 884 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", 885 skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext); 886 return kIgnoreError_SkPdfResult; 887 } 888 889 // FIXME (scroggo): renderPage also sets fResources. Are these redundant? 890 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc); 891 892 if (!pdfContext->fGraphicsState.fResources) { 893 // It might be null because we have not implemented yet inheritance. 894 return kIgnoreError_SkPdfResult; 895 } 896 897 if (CheckRecursiveRendering::IsInRendering(skobj)) { 898 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kRecursiveReferencing_SkPdfIssue, 899 "Recursive reverencing is invalid in draw objects", skobj, pdfContext); 900 return kIgnoreError_SkPdfResult; 901 } 902 CheckRecursiveRendering checkRecursion(skobj); 903 904 905 // FIXME (scroggo): Is this save necessary? May be needed for rendering a nested PDF. 906 PdfOp_q(pdfContext, canvas, NULL); 907 908 // TODO(edisonn): MediaBox can be inherited!!!! 909 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MediaBox inheritance NYI", 910 NULL, pdfContext); 911 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc); 912 if (skobj->has_Group()) { 913 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc); 914 doGroup_before(pdfContext, canvas, bbox, tgroup, true); 915 } else { 916 canvas->save(); 917 } 918 919 pdfContext->parseStream(stream, canvas); 920 921 canvas->restore(); 922 PdfOp_Q(pdfContext, canvas, NULL); 923 return kPartial_SkPdfResult; 924 } 925 926 SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 927 pdfContext->fStateStack.push(pdfContext->fGraphicsState); 928 canvas->save(); 929 pdfContext->fObjectStack.nest(); 930 return kOK_SkPdfResult; 931 } 932 933 SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 934 if (pdfContext->fStateStack.count() > 0) { 935 pdfContext->fGraphicsState = pdfContext->fStateStack.top(); 936 pdfContext->fStateStack.pop(); 937 canvas->restore(); 938 939 if (pdfContext->fObjectStack.nestingLevel() == 0) { 940 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackNestingOverflow_SkPdfIssue, 941 "stack nesting overflow (q/Q)", NULL, pdfContext); 942 return kIgnoreError_SkPdfResult; 943 } else { 944 pdfContext->fObjectStack.unnest(); 945 } 946 } else { 947 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackOverflow_SkPdfIssue, 948 "stack overflow (q/Q)", NULL, pdfContext); 949 return kIgnoreError_SkPdfResult; 950 } 951 952 return kOK_SkPdfResult; 953 } 954 955 static SkPdfResult PdfOp_cm(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 956 EXPECT_OPERANDS("cm", pdfContext, 6); 957 POP_NUMBER(pdfContext, f); 958 POP_NUMBER(pdfContext, e); 959 POP_NUMBER(pdfContext, d); 960 POP_NUMBER(pdfContext, c); 961 POP_NUMBER(pdfContext, b); 962 POP_NUMBER(pdfContext, a); 963 CHECK_PARAMETERS(); 964 double array[6] = {a, b, c, d, e, f}; 965 966 // a b 967 // c d 968 // e f 969 970 // 0 1 971 // 2 3 972 // 4 5 973 974 // sx ky 975 // kx sy 976 // tx ty 977 SkMatrix matrix = SkMatrixFromPdfMatrix(array); 978 979 pdfContext->fGraphicsState.fCTM.preConcat(matrix); 980 981 #ifdef PDF_TRACE 982 printf("cm "); 983 for (int i = 0 ; i < 6 ; i++) { 984 printf("%f ", array[i]); 985 } 986 printf("\n"); 987 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm"); 988 #endif 989 990 return kOK_SkPdfResult; 991 } 992 993 //leading TL Set the text leading, Tl 994 //, to leading, which is a number expressed in unscaled text 995 //space units. Text leading is used only by the T*, ', and " operators. Initial value: 0. 996 static SkPdfResult PdfOp_TL(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 997 EXPECT_OPERANDS("TL", pdfContext, 1); 998 POP_NUMBER(pdfContext, ty); 999 CHECK_PARAMETERS(); 1000 1001 pdfContext->fGraphicsState.fTextLeading = ty; 1002 1003 return kOK_SkPdfResult; 1004 } 1005 1006 static SkPdfResult PdfOp_Td(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1007 EXPECT_OPERANDS("Td", pdfContext, 2); 1008 POP_NUMBER(pdfContext, ty); 1009 POP_NUMBER(pdfContext, tx); 1010 CHECK_PARAMETERS(); 1011 1012 double array[6] = {1, 0, 0, 1, tx, -ty}; 1013 SkMatrix matrix = SkMatrixFromPdfMatrix(array); 1014 1015 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); 1016 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix); 1017 1018 return kPartial_SkPdfResult; 1019 } 1020 1021 static SkPdfResult PdfOp_TD(SkPdfContext* pdfContext, SkCanvas* canvas, 1022 SkPdfTokenLooper* parentLooper) { 1023 EXPECT_OPERANDS("TD", pdfContext, 2) 1024 POP_NUMBER(pdfContext, ty); 1025 POP_NUMBER(pdfContext, tx); 1026 CHECK_PARAMETERS(); 1027 1028 // TODO(edisonn): Create factory methods or constructors so native is hidden 1029 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty); 1030 pdfContext->fObjectStack.push(_ty); 1031 1032 PdfOp_TL(pdfContext, canvas, parentLooper); 1033 1034 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx); 1035 pdfContext->fObjectStack.push(vtx); 1036 1037 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty); 1038 pdfContext->fObjectStack.push(vty); 1039 1040 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, parentLooper); 1041 1042 return ret; 1043 } 1044 1045 static SkPdfResult PdfOp_Tm(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1046 EXPECT_OPERANDS("Tm", pdfContext, 6); 1047 POP_NUMBER(pdfContext, f); 1048 POP_NUMBER(pdfContext, e); 1049 POP_NUMBER(pdfContext, d); 1050 POP_NUMBER(pdfContext, c); 1051 POP_NUMBER(pdfContext, b); 1052 POP_NUMBER(pdfContext, a); 1053 CHECK_PARAMETERS(); 1054 1055 double array[6]; 1056 array[0] = a; 1057 array[1] = b; 1058 array[2] = c; 1059 array[3] = d; 1060 array[4] = e; 1061 array[5] = f; 1062 1063 SkMatrix matrix = SkMatrixFromPdfMatrix(array); 1064 matrix.postConcat(pdfContext->fGraphicsState.fCTM); 1065 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1)); 1066 1067 // TODO(edisonn): NYI - Text positioning. 1068 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, 1069 "Text positioning not implemented for 2+ chars", NULL, pdfContext); 1070 1071 pdfContext->fGraphicsState.fMatrixTm = matrix; 1072 pdfContext->fGraphicsState.fMatrixTlm = matrix; 1073 1074 return kPartial_SkPdfResult; 1075 } 1076 1077 // T* Move to the start of the next line. This operator has the same effect as the code 1078 //0 Tl Td 1079 //where Tl is the current leading parameter in the text state 1080 static SkPdfResult PdfOp_T_star(SkPdfContext* pdfContext, SkCanvas* canvas, 1081 SkPdfTokenLooper* parentLooper) { 1082 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0); 1083 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading); 1084 1085 pdfContext->fObjectStack.push(zero); 1086 pdfContext->fObjectStack.push(tl); 1087 1088 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, parentLooper); 1089 1090 return ret; 1091 } 1092 1093 static SkPdfResult PdfOp_m(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1094 if (pdfContext->fGraphicsState.fPathClosed) { 1095 pdfContext->fGraphicsState.fPath.reset(); 1096 pdfContext->fGraphicsState.fPathClosed = false; 1097 } 1098 1099 EXPECT_OPERANDS("m", pdfContext, 2); 1100 POP_NUMBER(pdfContext, y); 1101 POP_NUMBER(pdfContext, x); 1102 CHECK_PARAMETERS(); 1103 1104 pdfContext->fGraphicsState.fCurPosY = y; 1105 pdfContext->fGraphicsState.fCurPosX = x; 1106 1107 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX), 1108 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY)); 1109 1110 return kOK_SkPdfResult; 1111 } 1112 1113 static SkPdfResult PdfOp_l(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1114 if (pdfContext->fGraphicsState.fPathClosed) { 1115 pdfContext->fGraphicsState.fPath.reset(); 1116 pdfContext->fGraphicsState.fPathClosed = false; 1117 } 1118 1119 EXPECT_OPERANDS("l", pdfContext, 2); 1120 POP_NUMBER(pdfContext, y); 1121 POP_NUMBER(pdfContext, x); 1122 CHECK_PARAMETERS(); 1123 1124 pdfContext->fGraphicsState.fCurPosY = y; 1125 pdfContext->fGraphicsState.fCurPosX = x; 1126 1127 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX), 1128 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY)); 1129 1130 return kOK_SkPdfResult; 1131 } 1132 1133 static SkPdfResult PdfOp_c(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1134 if (pdfContext->fGraphicsState.fPathClosed) { 1135 pdfContext->fGraphicsState.fPath.reset(); 1136 pdfContext->fGraphicsState.fPathClosed = false; 1137 } 1138 1139 EXPECT_OPERANDS("c", pdfContext, 6); 1140 POP_NUMBER(pdfContext, y3); 1141 POP_NUMBER(pdfContext, x3); 1142 POP_NUMBER(pdfContext, y2); 1143 POP_NUMBER(pdfContext, x2); 1144 POP_NUMBER(pdfContext, y1); 1145 POP_NUMBER(pdfContext, x1); 1146 CHECK_PARAMETERS(); 1147 1148 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1), 1149 SkDoubleToScalar(x2), SkDoubleToScalar(y2), 1150 SkDoubleToScalar(x3), SkDoubleToScalar(y3)); 1151 1152 pdfContext->fGraphicsState.fCurPosX = x3; 1153 pdfContext->fGraphicsState.fCurPosY = y3; 1154 1155 return kOK_SkPdfResult; 1156 } 1157 1158 static SkPdfResult PdfOp_v(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1159 if (pdfContext->fGraphicsState.fPathClosed) { 1160 pdfContext->fGraphicsState.fPath.reset(); 1161 pdfContext->fGraphicsState.fPathClosed = false; 1162 } 1163 1164 EXPECT_OPERANDS("v", pdfContext, 4); 1165 POP_NUMBER(pdfContext, y3); 1166 POP_NUMBER(pdfContext, x3); 1167 POP_NUMBER(pdfContext, y2); 1168 POP_NUMBER(pdfContext, x2); 1169 CHECK_PARAMETERS(); 1170 1171 double y1 = pdfContext->fGraphicsState.fCurPosY; 1172 double x1 = pdfContext->fGraphicsState.fCurPosX; 1173 1174 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1), 1175 SkDoubleToScalar(x2), SkDoubleToScalar(y2), 1176 SkDoubleToScalar(x3), SkDoubleToScalar(y3)); 1177 1178 pdfContext->fGraphicsState.fCurPosX = x3; 1179 pdfContext->fGraphicsState.fCurPosY = y3; 1180 1181 return kOK_SkPdfResult; 1182 } 1183 1184 static SkPdfResult PdfOp_y(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1185 if (pdfContext->fGraphicsState.fPathClosed) { 1186 pdfContext->fGraphicsState.fPath.reset(); 1187 pdfContext->fGraphicsState.fPathClosed = false; 1188 } 1189 1190 EXPECT_OPERANDS("y", pdfContext, 4); 1191 POP_NUMBER(pdfContext, y3); 1192 POP_NUMBER(pdfContext, x3); 1193 POP_NUMBER(pdfContext, y1); 1194 POP_NUMBER(pdfContext, x1); 1195 CHECK_PARAMETERS(); 1196 1197 double y2 = pdfContext->fGraphicsState.fCurPosY; 1198 double x2 = pdfContext->fGraphicsState.fCurPosX; 1199 1200 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1), 1201 SkDoubleToScalar(x2), SkDoubleToScalar(y2), 1202 SkDoubleToScalar(x3), SkDoubleToScalar(y3)); 1203 1204 pdfContext->fGraphicsState.fCurPosX = x3; 1205 pdfContext->fGraphicsState.fCurPosY = y3; 1206 1207 return kOK_SkPdfResult; 1208 } 1209 1210 static SkPdfResult PdfOp_re(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1211 if (pdfContext->fGraphicsState.fPathClosed) { 1212 pdfContext->fGraphicsState.fPath.reset(); 1213 pdfContext->fGraphicsState.fPathClosed = false; 1214 } 1215 1216 EXPECT_OPERANDS("re", pdfContext, 4); 1217 POP_NUMBER(pdfContext, height); 1218 POP_NUMBER(pdfContext, width); 1219 POP_NUMBER(pdfContext, y); 1220 POP_NUMBER(pdfContext, x); 1221 CHECK_PARAMETERS(); 1222 1223 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), 1224 SkDoubleToScalar(y), 1225 SkDoubleToScalar(x + width), 1226 SkDoubleToScalar(y + height)); 1227 1228 pdfContext->fGraphicsState.fCurPosX = x; 1229 pdfContext->fGraphicsState.fCurPosY = y + height; 1230 1231 return kOK_SkPdfResult; 1232 } 1233 1234 static SkPdfResult PdfOp_h(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1235 pdfContext->fGraphicsState.fPath.close(); 1236 return kOK_SkPdfResult; 1237 } 1238 1239 static SkPdfResult PdfOp_fillAndStroke(SkPdfContext* pdfContext, SkCanvas* canvas, 1240 bool fill, bool stroke, bool close, bool evenOdd) { 1241 SkPath path = pdfContext->fGraphicsState.fPath; 1242 1243 if (close) { 1244 path.close(); 1245 } 1246 1247 canvas->setMatrix(pdfContext->fGraphicsState.fCTM); 1248 1249 SkPaint paint; 1250 1251 SkPoint line[2]; 1252 if (fill && !stroke && path.isLine(line)) { 1253 paint.setStyle(SkPaint::kStroke_Style); 1254 1255 // TODO(edisonn): implement this with patterns 1256 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); 1257 paint.setStrokeWidth(SkDoubleToScalar(0)); 1258 1259 canvas->drawPath(path, paint); 1260 } else { 1261 if (fill) { 1262 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, 1263 "Pattern", strlen("Pattern")) == 0 && 1264 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) { 1265 1266 // TODO(edisonn): we can use a shader here, like imageshader to draw fast. 1267 1268 PdfOp_q(pdfContext, canvas, NULL); 1269 1270 if (evenOdd) { 1271 path.setFillType(SkPath::kEvenOdd_FillType); 1272 } 1273 canvas->clipPath(path); 1274 1275 if (pdfContext->fPdfDoc 1276 ->mapper() 1277 ->mapType1PatternDictionary(pdfContext->fGraphicsState 1278 .fNonStroking 1279 .fPattern) 1280 != kNone_SkPdfNativeObjectType) { 1281 SkPdfType1PatternDictionary* pattern 1282 = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState 1283 .fNonStroking 1284 .fPattern; 1285 1286 // TODO(edisonn): make PaintType constants 1287 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) { 1288 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect 1289 // it will change the result iterating in reverse 1290 // remove then the following bounds.sort(); 1291 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc)); 1292 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc)); 1293 1294 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, 1295 "paterns x/y step is forced to positive number", 1296 pattern, pdfContext); 1297 1298 SkRect bounds = path.getBounds(); 1299 bounds.sort(); 1300 1301 SkScalar x; 1302 SkScalar y; 1303 1304 y = bounds.top(); 1305 int totalx = 0; 1306 int totaly = 0; 1307 while (y < bounds.bottom()) { 1308 x = bounds.left(); 1309 totalx = 0; 1310 1311 while (x < bounds.right()) { 1312 doXObject(pdfContext, canvas, pattern); 1313 1314 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate( 1315 SkIntToScalar(xStep), SkIntToScalar(0)); 1316 totalx += xStep; 1317 x += SkIntToScalar(xStep); 1318 } 1319 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate( 1320 SkIntToScalar(-totalx), SkIntToScalar(0)); 1321 1322 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate( 1323 SkIntToScalar(0), SkIntToScalar(-yStep)); 1324 totaly += yStep; 1325 y += SkIntToScalar(yStep); 1326 } 1327 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate( 1328 SkIntToScalar(0), SkIntToScalar(totaly)); 1329 } 1330 } 1331 1332 PdfOp_Q(pdfContext, canvas, NULL); 1333 } else { 1334 paint.setStyle(SkPaint::kFill_Style); 1335 if (evenOdd) { 1336 path.setFillType(SkPath::kEvenOdd_FillType); 1337 } 1338 1339 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); 1340 1341 canvas->drawPath(path, paint); 1342 } 1343 } 1344 1345 if (stroke) { 1346 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, 1347 "Pattern", strlen("Pattern")) == 0) { 1348 // TODO(edisonn): implement Pattern for strokes 1349 paint.setStyle(SkPaint::kStroke_Style); 1350 1351 paint.setColor(SK_ColorGREEN); 1352 1353 // reset it, just in case it messes up the stroke 1354 path.setFillType(SkPath::kWinding_FillType); 1355 canvas->drawPath(path, paint); 1356 } else { 1357 paint.setStyle(SkPaint::kStroke_Style); 1358 1359 pdfContext->fGraphicsState.applyGraphicsState(&paint, true); 1360 1361 // reset it, just in case it messes up the stroke 1362 path.setFillType(SkPath::kWinding_FillType); 1363 canvas->drawPath(path, paint); 1364 } 1365 } 1366 } 1367 1368 pdfContext->fGraphicsState.fPath.reset(); 1369 // TODO(edisonn): implement scale/zoom 1370 1371 if (pdfContext->fGraphicsState.fHasClipPathToApply) { 1372 #ifndef PDF_DEBUG_NO_CLIPING 1373 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true); 1374 #endif 1375 } 1376 1377 //pdfContext->fGraphicsState.fClipPath.reset(); 1378 pdfContext->fGraphicsState.fHasClipPathToApply = false; 1379 1380 return kOK_SkPdfResult; 1381 1382 } 1383 1384 static SkPdfResult PdfOp_S(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1385 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false); 1386 } 1387 1388 static SkPdfResult PdfOp_s(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1389 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false); 1390 } 1391 1392 static SkPdfResult PdfOp_F(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1393 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false); 1394 } 1395 1396 static SkPdfResult PdfOp_f(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1397 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false); 1398 } 1399 1400 static SkPdfResult PdfOp_f_star(SkPdfContext* pdfContext, SkCanvas* canvas, 1401 SkPdfTokenLooper*) { 1402 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true); 1403 } 1404 1405 static SkPdfResult PdfOp_B(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1406 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false); 1407 } 1408 1409 static SkPdfResult PdfOp_B_star(SkPdfContext* pdfContext, SkCanvas* canvas, 1410 SkPdfTokenLooper*) { 1411 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true); 1412 } 1413 1414 static SkPdfResult PdfOp_b(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1415 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false); 1416 } 1417 1418 static SkPdfResult PdfOp_b_star(SkPdfContext* pdfContext, SkCanvas* canvas, 1419 SkPdfTokenLooper*) { 1420 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true); 1421 } 1422 1423 static SkPdfResult PdfOp_n(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1424 canvas->setMatrix(pdfContext->fGraphicsState.fCTM); 1425 if (pdfContext->fGraphicsState.fHasClipPathToApply) { 1426 #ifndef PDF_DEBUG_NO_CLIPING 1427 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true); 1428 #endif 1429 } 1430 1431 pdfContext->fGraphicsState.fHasClipPathToApply = false; 1432 1433 pdfContext->fGraphicsState.fPathClosed = true; 1434 1435 return kOK_SkPdfResult; 1436 } 1437 1438 static SkPdfResult PdfOp_BT(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1439 pdfContext->fGraphicsState.fTextBlock = true; 1440 SkMatrix matrix = pdfContext->fGraphicsState.fCTM; 1441 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1)); 1442 pdfContext->fGraphicsState.fMatrixTm = matrix; 1443 pdfContext->fGraphicsState.fMatrixTlm = matrix; 1444 1445 return kPartial_SkPdfResult; 1446 } 1447 1448 static SkPdfResult PdfOp_ET(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1449 if (!pdfContext->fGraphicsState.fTextBlock) { 1450 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "ET without BT", NULL, 1451 pdfContext); 1452 1453 return kIgnoreError_SkPdfResult; 1454 } 1455 1456 pdfContext->fGraphicsState.fTextBlock = false; 1457 1458 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack? 1459 return kOK_SkPdfResult; 1460 } 1461 1462 static SkPdfResult skpdfGraphicsStateApplyFontCore(SkPdfContext* pdfContext, 1463 const SkPdfNativeObject* fontName, double fontSize) { 1464 #ifdef PDF_TRACE 1465 printf("font name: %s\n", fontName->nameValue2().c_str()); 1466 #endif 1467 1468 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) { 1469 // TODO(edisonn): try to recover and draw it any way? 1470 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingFont_SkPdfIssue, 1471 "No font", fontName, pdfContext); 1472 return kIgnoreError_SkPdfResult; 1473 } 1474 1475 SkPdfNativeObject* objFont 1476 = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName); 1477 objFont = pdfContext->fPdfDoc->resolveReference(objFont); 1478 if (kNone_SkPdfNativeObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) { 1479 // TODO(edisonn): try to recover and draw it any way? 1480 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kInvalidFont_SkPdfIssue, 1481 "Invalid font", objFont, pdfContext); 1482 return kIgnoreError_SkPdfResult; 1483 } 1484 1485 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont; 1486 1487 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd); 1488 1489 if (skfont) { 1490 pdfContext->fGraphicsState.fSkFont = skfont; 1491 } 1492 pdfContext->fGraphicsState.fCurFontSize = fontSize; 1493 return kOK_SkPdfResult; 1494 } 1495 1496 //font size Tf Set the text font, Tf 1497 //, to font and the text font size, Tfs, to size. font is the name of a 1498 //font resource in the Fontsubdictionary of the current resource dictionary; size is 1499 //a number representing a scale factor. There is no initial value for either font or 1500 //size; they must be specied explicitly using Tf before any text is shown. 1501 static SkPdfResult PdfOp_Tf(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1502 EXPECT_OPERANDS("Tf", pdfContext, 2); 1503 POP_NUMBER(pdfContext, fontSize); 1504 POP_NAME(pdfContext, fontName); 1505 CHECK_PARAMETERS(); 1506 1507 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize); 1508 } 1509 1510 static SkPdfResult PdfOp_Tj(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1511 EXPECT_OPERANDS("Tj", pdfContext, 1); 1512 POP_STRING(pdfContext, str); 1513 CHECK_PARAMETERS(); 1514 1515 if (!pdfContext->fGraphicsState.fTextBlock) { 1516 // TODO(edisonn): try to recover and draw it any way? 1517 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "Tj without BT", NULL, 1518 pdfContext); 1519 return kIgnoreError_SkPdfResult; 1520 } 1521 1522 SkPdfResult ret = DrawText(pdfContext, str, canvas); 1523 1524 return ret; 1525 } 1526 1527 static SkPdfResult PdfOp_quote(SkPdfContext* pdfContext, SkCanvas* canvas, 1528 SkPdfTokenLooper* parentLooper) { 1529 if (!pdfContext->fGraphicsState.fTextBlock) { 1530 // TODO(edisonn): try to recover and draw it any way? 1531 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, 1532 "' without BT", NULL, pdfContext); 1533 return kIgnoreError_SkPdfResult; 1534 } 1535 1536 PdfOp_T_star(pdfContext, canvas, parentLooper); 1537 // Do not pop, and push, just transfer the param to Tj 1538 return PdfOp_Tj(pdfContext, canvas, parentLooper); 1539 } 1540 1541 static SkPdfResult PdfOp_doublequote(SkPdfContext* pdfContext, SkCanvas* canvas, 1542 SkPdfTokenLooper* parentLooper) { 1543 if (!pdfContext->fGraphicsState.fTextBlock) { 1544 // TODO(edisonn): try to recover and draw it any way? 1545 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, 1546 "\" without BT", NULL, pdfContext); 1547 return kIgnoreError_SkPdfResult; 1548 } 1549 1550 EXPECT_OPERANDS("\"", pdfContext, 3); 1551 POP_OBJ(pdfContext, str); 1552 POP_OBJ(pdfContext, ac); 1553 POP_OBJ(pdfContext, aw); 1554 CHECK_PARAMETERS(); 1555 1556 pdfContext->fObjectStack.push(aw); 1557 PdfOp_Tw(pdfContext, canvas, parentLooper); 1558 1559 pdfContext->fObjectStack.push(ac); 1560 PdfOp_Tc(pdfContext, canvas, parentLooper); 1561 1562 pdfContext->fObjectStack.push(str); 1563 PdfOp_quote(pdfContext, canvas, parentLooper); 1564 1565 return kPartial_SkPdfResult; 1566 } 1567 1568 static SkPdfResult PdfOp_TJ(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1569 EXPECT_OPERANDS("Tf", pdfContext, 1); 1570 POP_ARRAY(pdfContext, array); 1571 CHECK_PARAMETERS(); 1572 1573 if (!pdfContext->fGraphicsState.fTextBlock) { 1574 // TODO(edisonn): try to recover and draw it any way? 1575 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "TJ without BT", NULL, 1576 pdfContext); 1577 return kIgnoreError_SkPdfResult; 1578 } 1579 1580 if (!array->isArray()) { 1581 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, array, 1582 SkPdfNativeObject::kArray_PdfObjectType, pdfContext); 1583 return kIgnoreError_SkPdfResult; 1584 } 1585 1586 for( int i=0; i<static_cast<int>(array->size()); i++ ) 1587 { 1588 if (!(*array)[i]) { 1589 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, 1590 "element [i] is null, no element should be null", 1591 array, 1592 SkPdfNativeObject::_kAnyString_PdfObjectType | 1593 SkPdfNativeObject::_kNumber_PdfObjectType, 1594 pdfContext); 1595 } else if( (*array)[i]->isAnyString()) { 1596 SkPdfNativeObject* obj = (*array)[i]; 1597 DrawText(pdfContext, obj, canvas); 1598 } else if ((*array)[i]->isNumber()) { 1599 double dx = (*array)[i]->numberValue(); 1600 SkMatrix matrix; 1601 matrix.setAll(SkDoubleToScalar(1), 1602 SkDoubleToScalar(0), 1603 // TODO(edisonn): use writing mode, vertical/horizontal. 1604 SkDoubleToScalar(-dx), // amount is substracted!!! 1605 SkDoubleToScalar(0), 1606 SkDoubleToScalar(1), 1607 SkDoubleToScalar(0), 1608 SkDoubleToScalar(0), 1609 SkDoubleToScalar(0), 1610 SkDoubleToScalar(1)); 1611 1612 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); 1613 } else { 1614 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", (*array)[i], 1615 SkPdfNativeObject::kArray_PdfObjectType | 1616 SkPdfNativeObject::_kNumber_PdfObjectType, 1617 pdfContext); 1618 } 1619 } 1620 return kPartial_SkPdfResult; // TODO(edisonn): Implement fully DrawText before returing OK. 1621 } 1622 1623 static SkPdfResult PdfOp_CS_cs(SkPdfContext* pdfContext, SkCanvas* canvas, 1624 SkPdfColorOperator* colorOperator) { 1625 EXPECT_OPERANDS("CS/cs", pdfContext, 1); 1626 POP_NAME(pdfContext, name); 1627 CHECK_PARAMETERS(); 1628 1629 //Next, get the ColorSpace Dictionary from the Resource Dictionary: 1630 SkPdfDictionary* colorSpaceResource 1631 = pdfContext->fGraphicsState.fResources->ColorSpace(pdfContext->fPdfDoc); 1632 1633 SkPdfNativeObject* colorSpace 1634 = colorSpaceResource ? pdfContext->fPdfDoc 1635 ->resolveReference(colorSpaceResource->get(name)) : 1636 name; 1637 1638 if (colorSpace == NULL) { 1639 colorOperator->fColorSpace = name->strRef(); 1640 } else { 1641 #ifdef PDF_TRACE 1642 printf("CS = %s\n", colorSpace->toString(0, 0).c_str()); 1643 #endif // PDF_TRACE 1644 if (colorSpace->isName()) { 1645 colorOperator->fColorSpace = colorSpace->strRef(); 1646 } else if (colorSpace->isArray()) { 1647 size_t cnt = colorSpace->size(); 1648 if (cnt == 0) { 1649 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, 1650 "color space has length 0", colorSpace, pdfContext); 1651 return kIgnoreError_SkPdfResult; 1652 } 1653 SkPdfNativeObject* type = colorSpace->objAtAIndex(0); 1654 type = pdfContext->fPdfDoc->resolveReference(type); 1655 1656 if (type->isName("ICCBased")) { 1657 if (cnt != 2) { 1658 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, 1659 "ICCBased color space must have an array with 2 elements", 1660 colorSpace, pdfContext); 1661 return kIgnoreError_SkPdfResult; 1662 } 1663 SkPdfNativeObject* prop = colorSpace->objAtAIndex(1); 1664 prop = pdfContext->fPdfDoc->resolveReference(prop); 1665 #ifdef PDF_TRACE 1666 printf("ICCBased prop = %s\n", prop->toString(0, 0).c_str()); 1667 #endif // PDF_TRACE 1668 // TODO(edisonn): hack 1669 if (prop && prop->isDictionary() && prop->get("N") && 1670 prop->get("N")->isInteger() && prop->get("N")->intValue() == 3) { 1671 colorOperator->setColorSpace(&strings_DeviceRGB); 1672 return kPartial_SkPdfResult; 1673 } 1674 return kNYI_SkPdfResult; 1675 } 1676 } 1677 } 1678 1679 return kPartial_SkPdfResult; 1680 } 1681 1682 static SkPdfResult PdfOp_CS(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1683 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); 1684 } 1685 1686 static SkPdfResult PdfOp_cs(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1687 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking); 1688 } 1689 1690 static SkPdfResult PdfOp_SC_sc(SkPdfContext* pdfContext, SkCanvas* canvas, 1691 SkPdfColorOperator* colorOperator) { 1692 double c[4]; 1693 // int64_t v[4]; 1694 1695 int n = GetColorSpaceComponents(colorOperator->fColorSpace); 1696 1697 bool doubles = true; 1698 if (colorOperator->fColorSpace.equals("Indexed")) { 1699 doubles = false; 1700 } 1701 1702 #ifdef PDF_TRACE 1703 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n); 1704 #endif 1705 1706 EXPECT_OPERANDS("SC/sc", pdfContext, n); 1707 1708 for (int i = n - 1; i >= 0 ; i--) { 1709 if (doubles) { 1710 POP_NUMBER_INTO(pdfContext, c[i]); 1711 // } else { 1712 // v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop(); 1713 } 1714 } 1715 CHECK_PARAMETERS(); 1716 1717 // TODO(edisonn): Now, set that color. Only DeviceRGB supported. 1718 // TODO(edisonn): do possible field values to enum at parsing time! 1719 // TODO(edisonn): support also abbreviations /DeviceRGB == /RGB 1720 if (colorOperator->fColorSpace.equals("DeviceRGB") || 1721 colorOperator->fColorSpace.equals("RGB")) { 1722 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]), 1723 (U8CPU)(255*c[1]), 1724 (U8CPU)(255*c[2]))); 1725 } 1726 return kPartial_SkPdfResult; 1727 } 1728 1729 static SkPdfResult PdfOp_SC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1730 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); 1731 } 1732 1733 static SkPdfResult PdfOp_sc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1734 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking); 1735 } 1736 1737 static SkPdfResult PdfOp_SCN_scn(SkPdfContext* pdfContext, SkCanvas* canvas, 1738 SkPdfColorOperator* colorOperator) { 1739 if (pdfContext->fObjectStack.count() > 0 && pdfContext->fObjectStack.top()->isName()) { 1740 SkPdfNativeObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop(); 1741 1742 SkPdfDictionary* patternResources 1743 = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc); 1744 1745 if (patternResources == NULL) { 1746 #ifdef PDF_TRACE 1747 printf("ExtGState is NULL!\n"); 1748 #endif 1749 return kIgnoreError_SkPdfResult; 1750 } 1751 1752 colorOperator->setPatternColorSpace( 1753 pdfContext->fPdfDoc->resolveReference(patternResources->get(name))); 1754 } 1755 1756 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec. 1757 PdfOp_SC_sc(pdfContext, canvas, colorOperator); 1758 1759 return kPartial_SkPdfResult; 1760 } 1761 1762 static SkPdfResult PdfOp_SCN(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1763 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); 1764 } 1765 1766 static SkPdfResult PdfOp_scn(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1767 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking); 1768 } 1769 1770 static SkPdfResult PdfOp_G_g(SkPdfContext* pdfContext, SkCanvas* canvas, 1771 SkPdfColorOperator* colorOperator) { 1772 EXPECT_OPERANDS("G/g", pdfContext, 1); 1773 POP_NUMBER(pdfContext, gray); 1774 CHECK_PARAMETERS(); 1775 1776 // TODO(edisonn): limit gray in [0, 1] 1777 1778 // TODO(edisonn): HACK - it should be device gray, but not suported right now 1779 colorOperator->fColorSpace = strings_DeviceRGB; 1780 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255 * gray), 1781 (U8CPU)(255 * gray), 1782 (U8CPU)(255 * gray))); 1783 1784 return kPartial_SkPdfResult; 1785 } 1786 1787 static SkPdfResult PdfOp_G(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1788 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); 1789 } 1790 1791 static SkPdfResult PdfOp_g(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1792 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking); 1793 } 1794 1795 static SkPdfResult PdfOp_RG_rg(SkPdfContext* pdfContext, SkCanvas* canvas, 1796 SkPdfColorOperator* colorOperator) { 1797 EXPECT_OPERANDS("RG/rg", pdfContext, 3); 1798 POP_NUMBER(pdfContext, b); 1799 POP_NUMBER(pdfContext, g); 1800 POP_NUMBER(pdfContext, r); 1801 CHECK_PARAMETERS(); 1802 1803 colorOperator->fColorSpace = strings_DeviceRGB; 1804 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b))); 1805 return kOK_SkPdfResult; 1806 } 1807 1808 static SkPdfResult PdfOp_RG(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1809 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); 1810 } 1811 1812 static SkPdfResult PdfOp_rg(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1813 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking); 1814 } 1815 1816 static SkPdfResult PdfOp_K_k(SkPdfContext* pdfContext, SkCanvas* canvas, 1817 SkPdfColorOperator* colorOperator) { 1818 // TODO(edisonn): spec has some rules about overprint, implement them. 1819 EXPECT_OPERANDS("K/k", pdfContext, 4); 1820 POP_NUMBER(pdfContext, k); 1821 POP_NUMBER(pdfContext, y); 1822 POP_NUMBER(pdfContext, m); 1823 POP_NUMBER(pdfContext, c); 1824 CHECK_PARAMETERS(); 1825 1826 // TODO(edisonn): really silly quick way to remove compiler warning 1827 if (k + y + m + c == 0) { 1828 return kNYI_SkPdfResult; 1829 } 1830 1831 //colorOperator->fColorSpace = strings_DeviceCMYK; 1832 // TODO(edisonn): Set color. 1833 return kNYI_SkPdfResult; 1834 } 1835 1836 static SkPdfResult PdfOp_K(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1837 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); 1838 } 1839 1840 static SkPdfResult PdfOp_k(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1841 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking); 1842 } 1843 1844 static SkPdfResult PdfOp_W(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1845 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath; 1846 pdfContext->fGraphicsState.fHasClipPathToApply = true; 1847 1848 return kOK_SkPdfResult; 1849 } 1850 1851 static SkPdfResult PdfOp_W_star(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1852 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath; 1853 1854 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType); 1855 pdfContext->fGraphicsState.fHasClipPathToApply = true; 1856 1857 return kOK_SkPdfResult; 1858 } 1859 1860 static SkPdfResult PdfOp_BX(SkPdfContext* pdfContext, SkCanvas* canvas, 1861 SkPdfTokenLooper* parentLooper) { 1862 PdfCompatibilitySectionLooper looper(parentLooper); 1863 looper.loop(); 1864 return kOK_SkPdfResult; 1865 } 1866 1867 static SkPdfResult PdfOp_EX(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1868 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue, 1869 "EX operator should not be called, it is handled in a looper, " 1870 "unless the file is corrupted, we should assert", 1871 NULL, pdfContext); 1872 1873 return kIgnoreError_SkPdfResult; 1874 } 1875 1876 static SkPdfResult PdfOp_BI(SkPdfContext* pdfContext, SkCanvas* canvas, 1877 SkPdfTokenLooper* parentLooper) { 1878 PdfInlineImageLooper looper(parentLooper); 1879 looper.loop(); 1880 return kOK_SkPdfResult; 1881 } 1882 1883 static SkPdfResult PdfOp_ID(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1884 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue, 1885 "ID operator should not be called, it is habdled in a looper, " 1886 "unless the file is corrupted, we should assert", 1887 NULL, pdfContext); 1888 return kIgnoreError_SkPdfResult; 1889 } 1890 1891 static SkPdfResult PdfOp_EI(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 1892 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue, 1893 "EI operator should not be called, it is habdled in a looper, " 1894 "unless the file is corrupted, we should assert", 1895 NULL, pdfContext); 1896 return kIgnoreError_SkPdfResult; 1897 } 1898 1899 static SkPdfResult skpdfGraphicsStateApply_ca(SkPdfContext* pdfContext, double ca) { 1900 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca; 1901 return kOK_SkPdfResult; 1902 } 1903 1904 static SkPdfResult skpdfGraphicsStateApply_CA(SkPdfContext* pdfContext, double CA) { 1905 pdfContext->fGraphicsState.fStroking.fOpacity = CA; 1906 return kOK_SkPdfResult; 1907 } 1908 1909 static SkPdfResult skpdfGraphicsStateApplyLW(SkPdfContext* pdfContext, double lineWidth) { 1910 pdfContext->fGraphicsState.fLineWidth = lineWidth; 1911 return kOK_SkPdfResult; 1912 } 1913 1914 static SkPdfResult skpdfGraphicsStateApplyLC(SkPdfContext* pdfContext, int64_t lineCap) { 1915 pdfContext->fGraphicsState.fLineCap = (int)lineCap; 1916 return kOK_SkPdfResult; 1917 } 1918 1919 static SkPdfResult skpdfGraphicsStateApplyLJ(SkPdfContext* pdfContext, int64_t lineJoin) { 1920 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin; 1921 return kOK_SkPdfResult; 1922 } 1923 1924 static SkPdfResult skpdfGraphicsStateApplyML(SkPdfContext* pdfContext, double miterLimit) { 1925 pdfContext->fGraphicsState.fMiterLimit = miterLimit; 1926 return kOK_SkPdfResult; 1927 } 1928 1929 // TODO(edisonn): test all dashing rules, not sure if they work as in skia. 1930 /* 1931 1) [ ] 0 No dash; solid, unbroken lines 1932 2) [3] 0 3 units on, 3 units off, 1933 3) [2] 1 1 on, 2 off, 2 on, 2 off, 1934 4) [2 1] 0 2 on, 1 off, 2 on, 1 off, 1935 5) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, 1936 6) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, 1937 */ 1938 1939 static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* intervals, 1940 SkPdfNativeObject* phase) { 1941 if (intervals == NULL) { 1942 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, intervals, 1943 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext); 1944 return kIgnoreError_SkPdfResult; 1945 } 1946 1947 if (phase == NULL) { 1948 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, phase, 1949 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext); 1950 return kIgnoreError_SkPdfResult; 1951 } 1952 1953 int cnt = (int) intervals->size(); 1954 if (cnt >= 256) { 1955 // TODO(edisonn): alloc memory 1956 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, 1957 "dash array size unssuported, cnt > 256", intervals, pdfContext); 1958 return kIgnoreError_SkPdfResult; 1959 } 1960 for (int i = 0; i < cnt; i++) { 1961 if (!intervals->objAtAIndex(i) || !intervals->objAtAIndex(i)->isNumber()) { 1962 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, 1963 intervals->objAtAIndex(i), 1964 SkPdfNativeObject::_kNumber_PdfObjectType, NULL); 1965 return kIgnoreError_SkPdfResult; 1966 } 1967 } 1968 1969 double total = 0; 1970 for (int i = 0 ; i < cnt; i++) { 1971 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue(); 1972 total += pdfContext->fGraphicsState.fDashArray[i]; 1973 } 1974 if (cnt & 1) { 1975 if (cnt == 1) { 1976 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0]; 1977 cnt++; 1978 } else { 1979 // TODO(edisonn): report error/warning 1980 return kNYI_SkPdfResult; 1981 } 1982 } 1983 pdfContext->fGraphicsState.fDashArrayLength = cnt; 1984 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue(); 1985 if (pdfContext->fGraphicsState.fDashPhase == 0) { 1986 // other rules, changes? 1987 pdfContext->fGraphicsState.fDashPhase = SkDoubleToScalar(total); 1988 } 1989 1990 return kOK_SkPdfResult; 1991 } 1992 1993 static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* dash) { 1994 if (!dash || dash->isArray()) { 1995 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash, 1996 SkPdfNativeObject::kArray_PdfObjectType, pdfContext); 1997 return kIgnoreError_SkPdfResult; 1998 } 1999 2000 if (dash->size() != 2) { 2001 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, 2002 "hash array must have 2 elements", dash, pdfContext); 2003 return kIgnoreError_SkPdfResult; 2004 } 2005 2006 if (!dash->objAtAIndex(0) || !dash->objAtAIndex(0)->isArray()) { 2007 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash->objAtAIndex(0), 2008 SkPdfNativeObject::kArray_PdfObjectType, pdfContext); 2009 return kIgnoreError_SkPdfResult; 2010 } 2011 2012 if (!dash->objAtAIndex(1) || !dash->objAtAIndex(1)->isNumber()) { 2013 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash->objAtAIndex(1), 2014 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext); 2015 return kIgnoreError_SkPdfResult; 2016 } 2017 2018 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0), 2019 dash->objAtAIndex(1)); 2020 } 2021 2022 static void skpdfGraphicsStateApplyFont(SkPdfContext* pdfContext, SkPdfArray* fontAndSize) { 2023 if (!fontAndSize || !fontAndSize->isArray()) { 2024 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, fontAndSize, 2025 SkPdfNativeObject::kArray_PdfObjectType, pdfContext); 2026 return; 2027 } 2028 2029 if (fontAndSize->size() != 2) { 2030 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, 2031 "font array must have 2 elements", fontAndSize, pdfContext); 2032 return; 2033 } 2034 2035 if (!fontAndSize->objAtAIndex(0) || !fontAndSize->objAtAIndex(0)->isName()) { 2036 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, 2037 fontAndSize->objAtAIndex(0), 2038 SkPdfNativeObject::kName_PdfObjectType, pdfContext); 2039 return; 2040 } 2041 2042 2043 if (!fontAndSize->objAtAIndex(1) || !fontAndSize->objAtAIndex(1)->isNumber()) { 2044 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, 2045 fontAndSize->objAtAIndex(0), 2046 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext); 2047 return; 2048 } 2049 2050 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), 2051 fontAndSize->objAtAIndex(1)->numberValue()); 2052 } 2053 2054 2055 //lineWidth w Set the line width in the graphics state (see Line Width on page 152). 2056 static SkPdfResult PdfOp_w(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2057 EXPECT_OPERANDS("w", pdfContext, 1); 2058 POP_NUMBER(pdfContext, lw); 2059 CHECK_PARAMETERS(); 2060 2061 return skpdfGraphicsStateApplyLW(pdfContext, lw); 2062 } 2063 2064 //lineCap J Set the line cap style in the graphics state (see Line Cap Style on page 153). 2065 static SkPdfResult PdfOp_J(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2066 // TODO(edisonn): round/ceil to int? 2067 EXPECT_OPERANDS("J", pdfContext, 1); 2068 POP_NUMBER(pdfContext, lc); 2069 CHECK_PARAMETERS(); 2070 2071 return skpdfGraphicsStateApplyLC(pdfContext, (int)lc); 2072 } 2073 2074 //lineJoin j Set the line join style in the graphics state (see Line Join Style on page 153). 2075 static SkPdfResult PdfOp_j(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2076 // TODO(edisonn): round/ceil to int? 2077 EXPECT_OPERANDS("j", pdfContext, 1); 2078 POP_NUMBER(pdfContext, lj); 2079 CHECK_PARAMETERS(); 2080 2081 return skpdfGraphicsStateApplyLJ(pdfContext, (int)lj); 2082 } 2083 2084 //miterLimit M Set the miter limit in the graphics state (see Miter Limit on page 153). 2085 static SkPdfResult PdfOp_M(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2086 EXPECT_OPERANDS("M", pdfContext, 1); 2087 POP_NUMBER(pdfContext, ml); 2088 CHECK_PARAMETERS(); 2089 return skpdfGraphicsStateApplyML(pdfContext, ml); 2090 } 2091 2092 //dashArray dashPhase d Set the line dash pattern in the graphics state (see Line Dash Pattern on 2093 //page 155). 2094 static SkPdfResult PdfOp_d(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2095 EXPECT_OPERANDS("d", pdfContext, 2); 2096 POP_OBJ(pdfContext, phase); 2097 POP_ARRAY(pdfContext, array); 2098 CHECK_PARAMETERS(); 2099 2100 return skpdfGraphicsStateApplyD(pdfContext, array, phase); 2101 } 2102 2103 //intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see Rendering Intents 2104 // on page 197). 2105 static SkPdfResult PdfOp_ri(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2106 pdfContext->fObjectStack.pop(); 2107 2108 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "render intent NYI", NULL, 2109 pdfContext); 2110 2111 return kNYI_SkPdfResult; 2112 } 2113 2114 //atness i Set the atness tolerance in the graphics state (see Section 6.5.1, Flatness 2115 //Tolerance). atness is a number in the range 0 to 100; a value of 0 speci- 2116 //es the output devices default atness tolerance. 2117 static SkPdfResult PdfOp_i(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2118 EXPECT_OPERANDS("i", pdfContext, 1); 2119 POP_NUMBER(pdfContext, flatness); 2120 CHECK_PARAMETERS(); 2121 2122 if (flatness < 0 || flatness > 100) { 2123 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, 2124 "flatness must be a real in [0, 100] range", flatness_obj, pdfContext); 2125 return kIgnoreError_SkPdfResult; 2126 } 2127 2128 return kNYI_SkPdfResult; 2129 } 2130 2131 SkTDict<SkXfermode::Mode> gPdfBlendModes(20); 2132 2133 class InitBlendModes { 2134 public: 2135 InitBlendModes() { 2136 // TODO(edisonn): use the python code generator? 2137 // TABLE 7.2 Standard separable blend modes 2138 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode); 2139 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode); 2140 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode); 2141 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode); 2142 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode); 2143 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode); 2144 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode); 2145 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode); 2146 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode); 2147 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode); 2148 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode); 2149 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode); 2150 2151 // TABLE 7.3 Standard nonseparable blend modes 2152 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode); 2153 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode); 2154 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode); 2155 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode); 2156 } 2157 }; 2158 2159 InitBlendModes _gDummyInniter; 2160 2161 static SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) { 2162 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1); 2163 if (gPdfBlendModes.find(blendMode, len, &mode)) { 2164 return mode; 2165 } 2166 2167 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1); 2168 } 2169 2170 static void skpdfGraphicsStateApplyBM_name(SkPdfContext* pdfContext, const SkString& blendMode) { 2171 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.size()); 2172 if (mode <= SkXfermode::kLastMode) { 2173 pdfContext->fGraphicsState.fBlendModesLength = 1; 2174 pdfContext->fGraphicsState.fBlendModes[0] = mode; 2175 } else { 2176 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue, 2177 blendMode.c_str(), NULL, pdfContext); 2178 } 2179 } 2180 2181 static void skpdfGraphicsStateApplyBM_array(SkPdfContext* pdfContext, SkPdfArray* blendModes) { 2182 if (!blendModes || !blendModes->isArray()) { 2183 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, blendModes, 2184 SkPdfNativeObject::kArray_PdfObjectType, pdfContext); 2185 return; 2186 } 2187 2188 if (blendModes->size() == 0 || blendModes->size() > 256) { 2189 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue, 2190 "length of blendmodes, 0, is an erro, 256+, is NYI", blendModes, pdfContext); 2191 return; 2192 } 2193 2194 SkXfermode::Mode modes[256]; 2195 int cnt = (int) blendModes->size(); 2196 for (int i = 0; i < cnt; i++) { 2197 SkPdfNativeObject* name = blendModes->objAtAIndex(i); 2198 if (!name || !name->isName()) { 2199 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name, 2200 SkPdfNativeObject::kName_PdfObjectType, pdfContext); 2201 return; 2202 } 2203 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr()); 2204 if (mode > SkXfermode::kLastMode) { 2205 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue, NULL, name, 2206 pdfContext); 2207 return; 2208 } 2209 } 2210 2211 pdfContext->fGraphicsState.fBlendModesLength = cnt; 2212 for (int i = 0; i < cnt; i++) { 2213 pdfContext->fGraphicsState.fBlendModes[i] = modes[i]; 2214 } 2215 } 2216 2217 static void skpdfGraphicsStateApplySMask_dict(SkPdfContext* pdfContext, SkPdfDictionary* sMask) { 2218 if (!sMask || !sMask->isName()) { 2219 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, sMask, 2220 SkPdfNativeObject::kArray_PdfObjectType, pdfContext); 2221 return; 2222 } 2223 2224 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) { 2225 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask; 2226 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) { 2227 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask; 2228 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true); 2229 } else { 2230 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, 2231 "Dictionary must be SoftMask, or SoftMaskImage", 2232 sMask, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext); 2233 } 2234 } 2235 2236 static void skpdfGraphicsStateApplySMask_name(SkPdfContext* pdfContext, const SkString& sMask) { 2237 if (sMask.equals("None")) { 2238 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL; 2239 pdfContext->fGraphicsState.fSMask = NULL; 2240 return; 2241 } 2242 2243 SkPdfDictionary* extGStateDictionary 2244 = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc); 2245 2246 if (extGStateDictionary == NULL) { 2247 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL, 2248 pdfContext->fGraphicsState.fResources, pdfContext); 2249 return; 2250 } 2251 2252 SkPdfNativeObject* obj 2253 = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str())); 2254 if (!obj || !obj->isDictionary()) { 2255 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, obj, 2256 SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext); 2257 return; 2258 } 2259 2260 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL; 2261 pdfContext->fGraphicsState.fSMask = NULL; 2262 2263 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary()); 2264 } 2265 2266 static void skpdfGraphicsStateApplyAIS(SkPdfContext* pdfContext, bool alphaSource) { 2267 pdfContext->fGraphicsState.fAlphaSource = alphaSource; 2268 } 2269 2270 2271 //dictName gs (PDF 1.2) Set the specied parameters in the graphics state. dictName is 2272 //the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current 2273 //resource dictionary (see the next section). 2274 static SkPdfResult PdfOp_gs(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2275 EXPECT_OPERANDS("gs", pdfContext, 1); 2276 POP_NAME(pdfContext, name); 2277 CHECK_PARAMETERS(); 2278 2279 SkPdfDictionary* extGStateDictionary 2280 = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc); 2281 2282 if (extGStateDictionary == NULL) { 2283 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL, 2284 pdfContext->fGraphicsState.fResources, pdfContext); 2285 return kIgnoreError_SkPdfResult; 2286 } 2287 2288 SkPdfNativeObject* value 2289 = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name)); 2290 2291 if (kNone_SkPdfNativeObjectType == 2292 pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) { 2293 return kIgnoreError_SkPdfResult; 2294 } 2295 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value; 2296 2297 if (gs == NULL) { 2298 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, 2299 gs, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext); 2300 return kIgnoreError_SkPdfResult; 2301 } 2302 2303 if (gs->has_LW()) { 2304 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc)); 2305 } 2306 2307 if (gs->has_LC()) { 2308 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc)); 2309 } 2310 2311 if (gs->has_LJ()) { 2312 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc)); 2313 } 2314 2315 if (gs->has_ML()) { 2316 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc)); 2317 } 2318 2319 if (gs->has_D()) { 2320 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc)); 2321 } 2322 2323 if (gs->has_Font()) { 2324 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc)); 2325 } 2326 2327 if (gs->has_BM()) { 2328 if (gs->isBMAName(pdfContext->fPdfDoc)) { 2329 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc)); 2330 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) { 2331 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc)); 2332 } else { 2333 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", gs->get("BM"), 2334 SkPdfNativeObject::kArray_PdfObjectType | 2335 SkPdfNativeObject::kName_PdfObjectType, pdfContext); 2336 } 2337 } 2338 2339 if (gs->has_SMask()) { 2340 if (gs->isSMaskAName(pdfContext->fPdfDoc)) { 2341 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc)); 2342 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) { 2343 skpdfGraphicsStateApplySMask_dict(pdfContext, 2344 gs->getSMaskAsDictionary(pdfContext->fPdfDoc)); 2345 } else { 2346 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, 2347 "wrong type", 2348 gs->get("BM"), 2349 SkPdfNativeObject::kDictionary_PdfObjectType | 2350 SkPdfNativeObject::kName_PdfObjectType, 2351 pdfContext); 2352 } 2353 } 2354 2355 if (gs->has_ca()) { 2356 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc)); 2357 } 2358 2359 if (gs->has_CA()) { 2360 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc)); 2361 } 2362 2363 if (gs->has_AIS()) { 2364 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc)); 2365 } 2366 2367 // TODO(edisonn): make sure we loaded all those properties in graphic state. 2368 2369 return kOK_SkPdfResult; 2370 } 2371 2372 //charSpace Tc Set the character spacing, Tc 2373 //, to charSpace, which is a number expressed in unscaled text space units. 2374 // Character spacing is used by the Tj, TJ, and ' operators. 2375 //Initial value: 0. 2376 SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2377 EXPECT_OPERANDS("Tc", pdfContext, 1); 2378 POP_NUMBER(pdfContext, charSpace); 2379 CHECK_PARAMETERS(); 2380 2381 pdfContext->fGraphicsState.fCharSpace = charSpace; 2382 2383 return kOK_SkPdfResult; 2384 } 2385 2386 //wordSpace Tw Set the word spacing, T 2387 //w 2388 //, to wordSpace, which is a number expressed in unscaled 2389 //text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial 2390 //value: 0. 2391 SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2392 EXPECT_OPERANDS("Tw", pdfContext, 1); 2393 POP_NUMBER(pdfContext, wordSpace); 2394 CHECK_PARAMETERS(); 2395 2396 pdfContext->fGraphicsState.fWordSpace = wordSpace; 2397 2398 return kOK_SkPdfResult; 2399 } 2400 2401 //scale Tz Set the horizontal scaling, Th 2402 //, to (scale 100). scale is a number specifying the 2403 //percentage of the normal width. Initial value: 100 (normal width). 2404 static SkPdfResult PdfOp_Tz(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2405 EXPECT_OPERANDS("Tz", pdfContext, 1); 2406 POP_NUMBER(pdfContext, scale); 2407 CHECK_PARAMETERS(); 2408 2409 if (scale < 0) { 2410 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, 2411 "scale must a positive real number", scale_obj, pdfContext); 2412 return kError_SkPdfResult; 2413 } 2414 2415 return kNYI_SkPdfResult; 2416 } 2417 2418 //render Tr Set the text rendering mode, T 2419 //mode, to render, which is an integer. Initial value: 0. 2420 static SkPdfResult PdfOp_Tr(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2421 EXPECT_OPERANDS("Tr", pdfContext, 1); 2422 POP_INTEGER(pdfContext, mode); 2423 CHECK_PARAMETERS(); 2424 2425 if (mode < 0) { // TODO(edisonn): function/enums with supported modes 2426 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, 2427 "mode must a positive integer or 0", mode_obj, pdfContext); 2428 return kError_SkPdfResult; 2429 } 2430 2431 return kNYI_SkPdfResult; 2432 } 2433 //rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space 2434 //units. Initial value: 0. 2435 static SkPdfResult PdfOp_Ts(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2436 EXPECT_OPERANDS("Ts", pdfContext, 1); 2437 POP_NUMBER(pdfContext, rise); 2438 CHECK_PARAMETERS(); 2439 2440 if (rise < 0) { 2441 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, 2442 "rise must a positive real number", rise_obj, pdfContext); 2443 return kNYI_SkPdfResult; 2444 } 2445 2446 return kNYI_SkPdfResult; 2447 } 2448 2449 //wx wy d0 2450 static SkPdfResult PdfOp_d0(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2451 EXPECT_OPERANDS("d0", pdfContext, 2); 2452 POP_NUMBER(pdfContext, wy); 2453 POP_NUMBER(pdfContext, wx); 2454 CHECK_PARAMETERS(); 2455 2456 if (wx < 0) { 2457 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, 2458 "wx must a positive real number", wx_obj, pdfContext); 2459 return kError_SkPdfResult; 2460 } 2461 2462 if (wy < 0) { 2463 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue, 2464 "wy must a positive real number", wy_obj, pdfContext); 2465 return kError_SkPdfResult; 2466 } 2467 2468 return kNYI_SkPdfResult; 2469 } 2470 2471 //wx wy llx lly urx ury d1 2472 static SkPdfResult PdfOp_d1(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2473 EXPECT_OPERANDS("d1", pdfContext, 6); 2474 POP_NUMBER(pdfContext, ury); 2475 POP_NUMBER(pdfContext, urx); 2476 POP_NUMBER(pdfContext, lly); 2477 POP_NUMBER(pdfContext, llx); 2478 POP_NUMBER(pdfContext, wy); 2479 POP_NUMBER(pdfContext, wx); 2480 CHECK_PARAMETERS(); 2481 2482 // TODO(edisonn): really silly quick way to remove warning 2483 if (wx + wy + llx + lly + urx + ury) { 2484 return kNYI_SkPdfResult; 2485 } 2486 2487 return kNYI_SkPdfResult; 2488 } 2489 2490 //name sh 2491 static SkPdfResult PdfOp_sh(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2492 EXPECT_OPERANDS("sh", pdfContext, 1); 2493 POP_NAME(pdfContext, name); 2494 CHECK_PARAMETERS(); 2495 2496 if (name == NULL) { 2497 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name, 2498 SkPdfNativeObject::kName_PdfObjectType, pdfContext); 2499 return kError_SkPdfResult; 2500 } 2501 2502 return kNYI_SkPdfResult; 2503 } 2504 2505 //name Do 2506 static SkPdfResult PdfOp_Do(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2507 EXPECT_OPERANDS("Do", pdfContext, 1); 2508 POP_NAME(pdfContext, name); 2509 CHECK_PARAMETERS(); 2510 2511 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc); 2512 2513 if (xObject == NULL) { 2514 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingXObject_SkPdfIssue, NULL, 2515 pdfContext->fGraphicsState.fResources, pdfContext); 2516 return kIgnoreError_SkPdfResult; 2517 } 2518 2519 SkPdfNativeObject* value = xObject->get(name); 2520 value = pdfContext->fPdfDoc->resolveReference(value); 2521 2522 return doXObject(pdfContext, canvas, value); 2523 } 2524 2525 //tag MP Designate a marked-content point. tag is a name object indicating the role or 2526 //signicance of the point. 2527 static SkPdfResult PdfOp_MP(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2528 EXPECT_OPERANDS("MP", pdfContext, 1); 2529 POP_OBJ(pdfContext, tag); 2530 CHECK_PARAMETERS(); 2531 2532 if (tag == NULL) { 2533 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag, 2534 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext); 2535 return kNYI_SkPdfResult; 2536 } 2537 2538 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MP NYI", NULL, NULL); 2539 return kNYI_SkPdfResult; 2540 } 2541 2542 //tag properties DP Designate a marked-content point with an associated property list. tag is a 2543 //name object indicating the role or signicance of the point; properties is 2544 //either an inline dictionary containing the property list or a name object 2545 //associated with it in the Properties subdictionary of the current resource 2546 //dictionary (see Section 9.5.1, Property Lists). 2547 static SkPdfResult PdfOp_DP(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2548 EXPECT_OPERANDS("DP", pdfContext, 2); 2549 POP_OBJ(pdfContext, properties); 2550 POP_OBJ(pdfContext, tag); 2551 CHECK_PARAMETERS(); 2552 2553 if (tag == NULL) { 2554 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag, 2555 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext); 2556 return kNYI_SkPdfResult; 2557 } 2558 2559 if (properties == NULL) { 2560 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties, 2561 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext); 2562 return kNYI_SkPdfResult; 2563 } 2564 2565 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "DP NYI", NULL, NULL); 2566 return kNYI_SkPdfResult; 2567 } 2568 2569 //tag BMC Begin a marked-content sequence terminated by a balancing EMC operator. 2570 //tag is a name object indicating the role or signicance of the sequence. 2571 static SkPdfResult PdfOp_BMC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2572 EXPECT_OPERANDS("BMC", pdfContext, 1); 2573 POP_OBJ(pdfContext, tag); 2574 CHECK_PARAMETERS(); 2575 2576 if (tag == NULL) { 2577 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag, 2578 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext); 2579 return kNYI_SkPdfResult; 2580 } 2581 2582 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BMC NYI", NULL, NULL); 2583 return kNYI_SkPdfResult; 2584 } 2585 2586 //tag properties BDC Begin a marked-content sequence with an associated property list, terminated 2587 //by a balancing EMCoperator. tag is a name object indicating the role or significance of the 2588 // sequence; propertiesis either an inline dictionary containing the 2589 //property list or a name object associated with it in the Properties subdictionary of the current 2590 //resource dictionary (see Section 9.5.1, Property Lists). 2591 static SkPdfResult PdfOp_BDC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2592 EXPECT_OPERANDS("BDC", pdfContext, 2); 2593 POP_OBJ(pdfContext, properties); 2594 POP_OBJ(pdfContext, tag); 2595 CHECK_PARAMETERS(); 2596 2597 if (tag == NULL) { 2598 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag, 2599 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext); 2600 return kNYI_SkPdfResult; 2601 } 2602 2603 if (properties == NULL) { 2604 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties, 2605 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext); 2606 return kNYI_SkPdfResult; 2607 } 2608 2609 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BDC NYI", NULL, NULL); 2610 return kNYI_SkPdfResult; 2611 } 2612 2613 // EMC End a marked-content sequence begun by a BMC or BDC operator. 2614 static SkPdfResult PdfOp_EMC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) { 2615 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "EMC NYI", NULL, NULL); 2616 return kNYI_SkPdfResult; 2617 } 2618 2619 #include "SkPdfOps.h" 2620 2621 SkTDict<PdfOperatorRenderer> gPdfOps(100); 2622 2623 static void initPdfOperatorRenderes() { 2624 static bool gInitialized = false; 2625 if (gInitialized) { 2626 return; 2627 } 2628 2629 gPdfOps.set("q", PdfOp_q); 2630 gPdfOps.set("Q", PdfOp_Q); 2631 gPdfOps.set("cm", PdfOp_cm); 2632 2633 gPdfOps.set("TD", PdfOp_TD); 2634 gPdfOps.set("Td", PdfOp_Td); 2635 gPdfOps.set("Tm", PdfOp_Tm); 2636 gPdfOps.set("T*", PdfOp_T_star); 2637 2638 gPdfOps.set("m", PdfOp_m); 2639 gPdfOps.set("l", PdfOp_l); 2640 gPdfOps.set("c", PdfOp_c); 2641 gPdfOps.set("v", PdfOp_v); 2642 gPdfOps.set("y", PdfOp_y); 2643 gPdfOps.set("h", PdfOp_h); 2644 gPdfOps.set("re", PdfOp_re); 2645 2646 gPdfOps.set("S", PdfOp_S); 2647 gPdfOps.set("s", PdfOp_s); 2648 gPdfOps.set("f", PdfOp_f); 2649 gPdfOps.set("F", PdfOp_F); 2650 gPdfOps.set("f*", PdfOp_f_star); 2651 gPdfOps.set("B", PdfOp_B); 2652 gPdfOps.set("B*", PdfOp_B_star); 2653 gPdfOps.set("b", PdfOp_b); 2654 gPdfOps.set("b*", PdfOp_b_star); 2655 gPdfOps.set("n", PdfOp_n); 2656 2657 gPdfOps.set("BT", PdfOp_BT); 2658 gPdfOps.set("ET", PdfOp_ET); 2659 2660 gPdfOps.set("Tj", PdfOp_Tj); 2661 gPdfOps.set("'", PdfOp_quote); 2662 gPdfOps.set("\"", PdfOp_doublequote); 2663 gPdfOps.set("TJ", PdfOp_TJ); 2664 2665 gPdfOps.set("CS", PdfOp_CS); 2666 gPdfOps.set("cs", PdfOp_cs); 2667 gPdfOps.set("SC", PdfOp_SC); 2668 gPdfOps.set("SCN", PdfOp_SCN); 2669 gPdfOps.set("sc", PdfOp_sc); 2670 gPdfOps.set("scn", PdfOp_scn); 2671 gPdfOps.set("G", PdfOp_G); 2672 gPdfOps.set("g", PdfOp_g); 2673 gPdfOps.set("RG", PdfOp_RG); 2674 gPdfOps.set("rg", PdfOp_rg); 2675 gPdfOps.set("K", PdfOp_K); 2676 gPdfOps.set("k", PdfOp_k); 2677 2678 gPdfOps.set("W", PdfOp_W); 2679 gPdfOps.set("W*", PdfOp_W_star); 2680 2681 gPdfOps.set("BX", PdfOp_BX); 2682 gPdfOps.set("EX", PdfOp_EX); 2683 2684 gPdfOps.set("BI", PdfOp_BI); 2685 gPdfOps.set("ID", PdfOp_ID); 2686 gPdfOps.set("EI", PdfOp_EI); 2687 2688 gPdfOps.set("w", PdfOp_w); 2689 gPdfOps.set("J", PdfOp_J); 2690 gPdfOps.set("j", PdfOp_j); 2691 gPdfOps.set("M", PdfOp_M); 2692 gPdfOps.set("d", PdfOp_d); 2693 gPdfOps.set("ri", PdfOp_ri); 2694 gPdfOps.set("i", PdfOp_i); 2695 gPdfOps.set("gs", PdfOp_gs); 2696 2697 gPdfOps.set("Tc", PdfOp_Tc); 2698 gPdfOps.set("Tw", PdfOp_Tw); 2699 gPdfOps.set("Tz", PdfOp_Tz); 2700 gPdfOps.set("TL", PdfOp_TL); 2701 gPdfOps.set("Tf", PdfOp_Tf); 2702 gPdfOps.set("Tr", PdfOp_Tr); 2703 gPdfOps.set("Ts", PdfOp_Ts); 2704 2705 gPdfOps.set("d0", PdfOp_d0); 2706 gPdfOps.set("d1", PdfOp_d1); 2707 2708 gPdfOps.set("sh", PdfOp_sh); 2709 2710 gPdfOps.set("Do", PdfOp_Do); 2711 2712 gPdfOps.set("MP", PdfOp_MP); 2713 gPdfOps.set("DP", PdfOp_DP); 2714 gPdfOps.set("BMC", PdfOp_BMC); 2715 gPdfOps.set("BDC", PdfOp_BDC); 2716 gPdfOps.set("EMC", PdfOp_EMC); 2717 2718 gInitialized = true; 2719 } 2720 2721 class InitPdfOps { 2722 public: 2723 InitPdfOps() { 2724 initPdfOperatorRenderes(); 2725 } 2726 }; 2727 2728 InitPdfOps gInitPdfOps; 2729 2730 SkPdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) { 2731 SkASSERT(false); 2732 return kIgnoreError_SkPdfResult; 2733 } 2734 2735 void PdfInlineImageLooper::loop() { 2736 // FIXME (scroggo): Does this need to be looper? It does not consumeTokens, 2737 // nor does it loop. The one thing it does is provide access to the 2738 // protected members of SkPdfTokenLooper. 2739 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage()); 2740 } 2741 2742 SkPdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) { 2743 return fParent->consumeToken(token); 2744 } 2745 2746 void PdfCompatibilitySectionLooper::loop() { 2747 PdfOp_q(fPdfContext, fCanvas, NULL); 2748 2749 PdfToken token; 2750 while (fTokenizer->readToken(&token)) { 2751 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) { 2752 PdfCompatibilitySectionLooper looper(this); 2753 looper.loop(); 2754 } else { 2755 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) { 2756 break; 2757 } 2758 fParent->consumeToken(token); 2759 } 2760 } 2761 2762 PdfOp_Q(fPdfContext, fCanvas, NULL); 2763 } 2764 2765 // TODO(edisonn): for debugging - remove or put it in a #ifdef 2766 SkPdfContext* gPdfContext = NULL; 2767 2768 bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const { 2769 if (!fPdfDoc) { 2770 return false; 2771 } 2772 2773 if (page < 0 || page >= pages()) { 2774 return false; 2775 } 2776 2777 SkPdfContext pdfContext(fPdfDoc); 2778 2779 // FIXME (scroggo): Is this matrix needed? 2780 pdfContext.fOriginalMatrix = SkMatrix::I(); 2781 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page); 2782 2783 gPdfContext = &pdfContext; 2784 2785 SkScalar z = SkIntToScalar(0); 2786 SkScalar w = dst.width(); 2787 SkScalar h = dst.height(); 2788 2789 if (SkScalarTruncToInt(w) <= 0 || SkScalarTruncToInt(h) <= 0) { 2790 return true; 2791 } 2792 2793 // FIXME (scroggo): The media box may not be anchored at 0,0. Is this okay? 2794 SkScalar wp = fPdfDoc->MediaBox(page).width(); 2795 SkScalar hp = fPdfDoc->MediaBox(page).height(); 2796 2797 SkPoint pdfSpace[4] = {SkPoint::Make(z, z), 2798 SkPoint::Make(wp, z), 2799 SkPoint::Make(wp, hp), 2800 SkPoint::Make(z, hp)}; 2801 2802 #ifdef PDF_DEBUG_3X 2803 // Use larger image to make sure we do not draw anything outside of page 2804 // could be used in tests. 2805 SkPoint skiaSpace[4] = {SkPoint::Make(w+z, h+h), 2806 SkPoint::Make(w+w, h+h), 2807 SkPoint::Make(w+w, h+z), 2808 SkPoint::Make(w+z, h+z)}; 2809 #else 2810 SkPoint skiaSpace[4] = {SkPoint::Make(z, h), 2811 SkPoint::Make(w, h), 2812 SkPoint::Make(w, z), 2813 SkPoint::Make(z, z)}; 2814 #endif 2815 2816 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4)); 2817 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix"); 2818 2819 // FIXME (scroggo): Do we need to translate to account for the fact that 2820 // the media box (or the destination rect) may not be anchored at 0,0? 2821 pdfContext.fOriginalMatrix.postConcat(canvas->getTotalMatrix()); 2822 2823 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix; 2824 pdfContext.fGraphicsState.fContentStreamMatrix = pdfContext.fOriginalMatrix; 2825 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM; 2826 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM; 2827 2828 #ifndef PDF_DEBUG_NO_PAGE_CLIPING 2829 canvas->clipRect(dst, SkRegion::kIntersect_Op, true); 2830 #endif 2831 2832 // FIXME (scroggo): This concat may not be necessary, since we generally 2833 // call SkCanvas::setMatrix() before using the canvas. 2834 canvas->concat(pdfContext.fOriginalMatrix); 2835 2836 doPage(&pdfContext, canvas, fPdfDoc->page(page)); 2837 2838 // TODO(edisonn:) erase with white before draw? Right now the caller is responsible. 2839 // SkPaint paint; 2840 // paint.setColor(SK_ColorWHITE); 2841 // canvas->drawRect(rect, paint); 2842 2843 2844 canvas->flush(); 2845 return true; 2846 } 2847 2848 SkPdfRenderer* SkPdfRenderer::CreateFromFile(const char* inputFileName) { 2849 // FIXME: SkPdfNativeDoc should have a similar Create function. 2850 SkPdfNativeDoc* pdfDoc = SkNEW_ARGS(SkPdfNativeDoc, (inputFileName)); 2851 if (pdfDoc->pages() == 0) { 2852 SkDELETE(pdfDoc); 2853 return NULL; 2854 } 2855 2856 return SkNEW_ARGS(SkPdfRenderer, (pdfDoc)); 2857 } 2858 2859 SkPdfRenderer* SkPdfRenderer::CreateFromStream(SkStream* stream) { 2860 // TODO(edisonn): create static function that could return NULL if there are errors 2861 SkPdfNativeDoc* pdfDoc = SkNEW_ARGS(SkPdfNativeDoc, (stream)); 2862 if (pdfDoc->pages() == 0) { 2863 SkDELETE(pdfDoc); 2864 return NULL; 2865 } 2866 2867 return SkNEW_ARGS(SkPdfRenderer, (pdfDoc)); 2868 } 2869 2870 SkPdfRenderer::SkPdfRenderer(SkPdfNativeDoc* doc) 2871 :fPdfDoc(doc) { 2872 } 2873 2874 SkPdfRenderer::~SkPdfRenderer() { 2875 SkDELETE(fPdfDoc); 2876 } 2877 2878 int SkPdfRenderer::pages() const { 2879 SkASSERT(fPdfDoc != NULL); 2880 return fPdfDoc->pages(); 2881 } 2882 2883 SkRect SkPdfRenderer::MediaBox(int page) const { 2884 SkASSERT(fPdfDoc != NULL); 2885 return fPdfDoc->MediaBox(page); 2886 } 2887 2888 size_t SkPdfRenderer::bytesUsed() const { 2889 SkASSERT(fPdfDoc != NULL); 2890 return fPdfDoc->bytesUsed(); 2891 } 2892 2893 bool SkPDFNativeRenderToBitmap(SkStream* stream, 2894 SkBitmap* output, 2895 int page, 2896 SkPdfContent unused, 2897 double dpi) { 2898 SkASSERT(page >= 0); 2899 SkPdfRenderer* renderer = SkPdfRenderer::CreateFromStream(stream); 2900 if (NULL == renderer) { 2901 return false; 2902 } 2903 2904 SkRect rect = renderer->MediaBox(page < 0 ? 0 :page); 2905 2906 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(dpi / 72.0)); 2907 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(dpi / 72.0)); 2908 2909 rect = SkRect::MakeWH(width, height); 2910 2911 setup_bitmap(output, SkScalarCeilToInt(width), SkScalarCeilToInt(height)); 2912 2913 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (*output))); 2914 SkCanvas canvas(device); 2915 2916 return renderer->renderPage(page, &canvas, rect); 2917 } 2918