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