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