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