| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2013 Google Inc. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license that can be | |
| 5 * found in the LICENSE file. | |
| 6 */ | |
| 7 | |
| 8 #include "SkCanvas.h" | |
| 9 #include "SkDevice.h" | |
| 10 #include "SkForceLinking.h" | |
| 11 #include "SkGraphics.h" | |
| 12 #include "SkImageDecoder.h" | |
| 13 #include "SkImageEncoder.h" | |
| 14 #include "SkOSFile.h" | |
| 15 #include "SkPicture.h" | |
| 16 #include "SkStream.h" | |
| 17 #include "SkTypeface.h" | |
| 18 #include "SkTArray.h" | |
| 19 #include "picture_utils.h" | |
| 20 | |
| 21 #include <iostream> | |
| 22 #include <cstdio> | |
| 23 #include <stack> | |
| 24 #include <set> | |
| 25 | |
| 26 __SK_FORCE_IMAGE_DECODER_LINKING; | |
| 27 | |
| 28 // TODO(edisonn): tool, show what objects were read at least, show the ones not
even read | |
| 29 // keep for each object pos in file | |
| 30 // plug in for VS? syntax coloring, show selected object ... from the text, or f
rom rendered x,y | |
| 31 | |
| 32 // TODO(edisonn): security - validate all the user input, all pdf! | |
| 33 | |
| 34 // TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor cha
nges in text positioning and font | |
| 35 // this way, we look more at other features and layout in diffs | |
| 36 | |
| 37 // TODO(edisonn): move trace dump in the get functions, and mapper ones too so i
t ghappens automatically | |
| 38 /* | |
| 39 #ifdef PDF_TRACE | |
| 40 std::string str; | |
| 41 pdfContext->fGraphicsState.fResources->native()->ToString(str); | |
| 42 printf("Print Tf Resources: %s\n", str.c_str()); | |
| 43 #endif | |
| 44 */ | |
| 45 | |
| 46 #include "SkPdfHeaders_autogen.h" | |
| 47 #include "SkPdfMapper_autogen.h" | |
| 48 #include "SkPdfParser.h" | |
| 49 | |
| 50 #include "SkPdfBasics.h" | |
| 51 #include "SkPdfUtils.h" | |
| 52 | |
| 53 #include "SkPdfFont.h" | |
| 54 | |
| 55 /* | |
| 56 * TODO(edisonn): | |
| 57 * - all font types and all ppdf font features | |
| 58 * - word spacing | |
| 59 * - load font for baidu.pdf | |
| 60 * - load font for youtube.pdf | |
| 61 * - parser for pdf from the definition already available in pdfspec_autoge
n.py | |
| 62 * - all docs from ~/work | |
| 63 * - encapsulate native in the pdf api so the skpdf does not know anything about
native ... in progress | |
| 64 * - load gs/ especially smask and already known prop (skp) ... in progress | |
| 65 * - wrapper on classes for customizations? e.g. | |
| 66 * SkPdfPageObjectVanila - has only the basic loaders/getters | |
| 67 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add custom
izations here | |
| 68 * need to find a nice object model for all this with constructors and factories | |
| 69 * - deal with inheritable automatically ? | |
| 70 * - deal with specific type in spec directly, add all dictionary types to known
types | |
| 71 */ | |
| 72 | |
| 73 using namespace std; | |
| 74 | |
| 75 // Utilities | |
| 76 static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color
= SK_ColorWHITE) { | |
| 77 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
| 78 | |
| 79 bitmap->allocPixels(); | |
| 80 bitmap->eraseColor(color); | |
| 81 } | |
| 82 | |
| 83 // TODO(edisonn): synonyms? DeviceRGB and RGB ... | |
| 84 static int GetColorSpaceComponents(const std::string& colorSpace) { | |
| 85 if (colorSpace == "DeviceCMYK") { | |
| 86 return 4; | |
| 87 } else if (colorSpace == "DeviceGray" || | |
| 88 colorSpace == "CalGray" || | |
| 89 colorSpace == "Indexed") { | |
| 90 return 1; | |
| 91 } else if (colorSpace == "DeviceRGB" || | |
| 92 colorSpace == "CalRGB" || | |
| 93 colorSpace == "Lab") { | |
| 94 return 3; | |
| 95 } else { | |
| 96 return 0; | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 SkMatrix SkMatrixFromPdfMatrix(double array[6]) { | |
| 101 SkMatrix matrix; | |
| 102 matrix.setAll(SkDoubleToScalar(array[0]), | |
| 103 SkDoubleToScalar(array[2]), | |
| 104 SkDoubleToScalar(array[4]), | |
| 105 SkDoubleToScalar(array[1]), | |
| 106 SkDoubleToScalar(array[3]), | |
| 107 SkDoubleToScalar(array[5]), | |
| 108 SkDoubleToScalar(0), | |
| 109 SkDoubleToScalar(0), | |
| 110 SkDoubleToScalar(1)); | |
| 111 | |
| 112 return matrix; | |
| 113 } | |
| 114 | |
| 115 SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) { | |
| 116 double array[6]; | |
| 117 | |
| 118 // TODO(edisonn): security issue, ret if size() != 6 | |
| 119 for (int i = 0; i < 6; i++) { | |
| 120 const SkPdfObject* elem = pdfArray->operator [](i); | |
| 121 if (elem == NULL || !elem->isNumber()) { | |
| 122 return SkMatrix::I(); // TODO(edisonn): report issue | |
| 123 } | |
| 124 array[i] = elem->numberValue(); | |
| 125 } | |
| 126 | |
| 127 return SkMatrixFromPdfMatrix(array); | |
| 128 } | |
| 129 | |
| 130 SkBitmap* gDumpBitmap = NULL; | |
| 131 SkCanvas* gDumpCanvas = NULL; | |
| 132 char gLastKeyword[100] = ""; | |
| 133 int gLastOpKeyword = -1; | |
| 134 char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh
,EI,Do,EX,"; | |
| 135 int gReadOp = 0; | |
| 136 | |
| 137 | |
| 138 #ifdef PDF_TRACE_DIFF_IN_PNG | |
| 139 static bool hasVisualEffect(const char* pdfOp) { | |
| 140 return true; | |
| 141 if (*pdfOp == '\0') return false; | |
| 142 | |
| 143 char markedPdfOp[100] = ","; | |
| 144 strcat(markedPdfOp, pdfOp); | |
| 145 strcat(markedPdfOp, ","); | |
| 146 | |
| 147 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL); | |
| 148 } | |
| 149 #endif // PDF_TRACE_DIFF_IN_PNG | |
| 150 | |
| 151 // TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrum
entation. | |
| 152 static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) { | |
| 153 bool ret = fTokenizer->readToken(token); | |
| 154 | |
| 155 gReadOp++; | |
| 156 | |
| 157 #ifdef PDF_TRACE_DIFF_IN_PNG | |
| 158 // TODO(edisonn): compare with old bitmap, and save only new bits are availa
ble, and save | |
| 159 // the numbar and name of last operation, so the file name will reflect op t
hat changed. | |
| 160 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits. | |
| 161 gDumpCanvas->flush(); | |
| 162 | |
| 163 SkBitmap bitmap; | |
| 164 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height()); | |
| 165 | |
| 166 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSiz
e()); | |
| 167 | |
| 168 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap))); | |
| 169 SkCanvas canvas(device); | |
| 170 | |
| 171 // draw context stuff here | |
| 172 SkPaint blueBorder; | |
| 173 blueBorder.setColor(SK_ColorBLUE); | |
| 174 blueBorder.setStyle(SkPaint::kStroke_Style); | |
| 175 blueBorder.setTextSize(SkDoubleToScalar(20)); | |
| 176 | |
| 177 SkString str; | |
| 178 | |
| 179 const SkClipStack* clipStack = gDumpCanvas->getClipStack(); | |
| 180 if (clipStack) { | |
| 181 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterSt
art); | |
| 182 const SkClipStack::Element* elem; | |
| 183 double y = 0; | |
| 184 int total = 0; | |
| 185 while (elem = iter.next()) { | |
| 186 total++; | |
| 187 y += 30; | |
| 188 | |
| 189 switch (elem->getType()) { | |
| 190 case SkClipStack::Element::kRect_Type: | |
| 191 canvas.drawRect(elem->getRect(), blueBorder); | |
| 192 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoub
leToScalar(10), SkDoubleToScalar(y), blueBorder); | |
| 193 break; | |
| 194 case SkClipStack::Element::kPath_Type: | |
| 195 canvas.drawPath(elem->getPath(), blueBorder); | |
| 196 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoub
leToScalar(10), SkDoubleToScalar(y), blueBorder); | |
| 197 break; | |
| 198 case SkClipStack::Element::kEmpty_Type: | |
| 199 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!")
, SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder); | |
| 200 break; | |
| 201 default: | |
| 202 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!
"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder); | |
| 203 break; | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 y += 30; | |
| 208 str.printf("Number of clips in stack: %i", total); | |
| 209 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDou
bleToScalar(y), blueBorder); | |
| 210 } | |
| 211 | |
| 212 const SkRegion& clipRegion = gDumpCanvas->getTotalClip(); | |
| 213 SkPath clipPath; | |
| 214 if (clipRegion.getBoundaryPath(&clipPath)) { | |
| 215 SkPaint redBorder; | |
| 216 redBorder.setColor(SK_ColorRED); | |
| 217 redBorder.setStyle(SkPaint::kStroke_Style); | |
| 218 canvas.drawPath(clipPath, redBorder); | |
| 219 } | |
| 220 | |
| 221 canvas.flush(); | |
| 222 | |
| 223 SkString out; | |
| 224 | |
| 225 // TODO(edisonn): get the image, and overlay on top of it, the clip , gr
afic state, teh stack, | |
| 226 // ... and other properties, to be able to debug th code easily | |
| 227 | |
| 228 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", g
LastOpKeyword, gLastKeyword); | |
| 229 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Typ
e, 100); | |
| 230 } | |
| 231 | |
| 232 if (token->fType == kKeyword_TokenType) { | |
| 233 strcpy(gLastKeyword, token->fKeyword); | |
| 234 gLastOpKeyword = gReadOp; | |
| 235 } else { | |
| 236 strcpy(gLastKeyword, ""); | |
| 237 } | |
| 238 #endif | |
| 239 | |
| 240 return ret; | |
| 241 } | |
| 242 | |
| 243 | |
| 244 | |
| 245 typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper*
*); | |
| 246 | |
| 247 map<std::string, PdfOperatorRenderer> gPdfOps; | |
| 248 | |
| 249 map<std::string, int> gRenderStats[kCount_PdfResult]; | |
| 250 | |
| 251 const char* gRenderStatsNames[kCount_PdfResult] = { | |
| 252 "Success", | |
| 253 "Partially implemented", | |
| 254 "Not yet implemented", | |
| 255 "Ignore Error", | |
| 256 "Error", | |
| 257 "Unsupported/Unknown" | |
| 258 }; | |
| 259 | |
| 260 static PdfResult DrawText(PdfContext* pdfContext, | |
| 261 const SkPdfObject* _str, | |
| 262 SkCanvas* canvas) | |
| 263 { | |
| 264 | |
| 265 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont; | |
| 266 if (skfont == NULL) { | |
| 267 skfont = SkPdfFont::Default(); | |
| 268 } | |
| 269 | |
| 270 | |
| 271 if (_str == NULL || !_str->isAnyString()) { | |
| 272 // TODO(edisonn): report warning | |
| 273 return kIgnoreError_PdfResult; | |
| 274 } | |
| 275 const SkPdfString* str = (const SkPdfString*)_str; | |
| 276 | |
| 277 SkUnencodedText binary(str); | |
| 278 | |
| 279 SkDecodedText decoded; | |
| 280 | |
| 281 if (skfont->encoding() == NULL) { | |
| 282 // TODO(edisonn): report warning | |
| 283 return kNYI_PdfResult; | |
| 284 } | |
| 285 | |
| 286 skfont->encoding()->decodeText(binary, &decoded); | |
| 287 | |
| 288 SkPaint paint; | |
| 289 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCur
FontSize == 0? | |
| 290 // Or maybe just not call setTextSize at all? | |
| 291 if (pdfContext->fGraphicsState.fCurFontSize != 0) { | |
| 292 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSi
ze)); | |
| 293 } | |
| 294 | |
| 295 // if (fCurFont && fCurFont->GetFontScale() != 0) { | |
| 296 // paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0))
; | |
| 297 // } | |
| 298 | |
| 299 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); | |
| 300 | |
| 301 canvas->save(); | |
| 302 | |
| 303 #if 1 | |
| 304 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm; | |
| 305 | |
| 306 SkPoint point1; | |
| 307 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0
), &point1); | |
| 308 | |
| 309 SkMatrix mirror; | |
| 310 mirror.setTranslate(0, -point1.y()); | |
| 311 // TODO(edisonn): fix rotated text, and skewed too | |
| 312 mirror.postScale(SK_Scalar1, -SK_Scalar1); | |
| 313 // TODO(edisonn): post rotate, skew | |
| 314 mirror.postTranslate(0, point1.y()); | |
| 315 | |
| 316 matrix.postConcat(mirror); | |
| 317 | |
| 318 canvas->setMatrix(matrix); | |
| 319 | |
| 320 SkTraceMatrix(matrix, "mirrored"); | |
| 321 #endif | |
| 322 | |
| 323 skfont->drawText(decoded, &paint, pdfContext, canvas); | |
| 324 canvas->restore(); | |
| 325 | |
| 326 return kPartial_PdfResult; | |
| 327 } | |
| 328 | |
| 329 // TODO(edisonn): create header files with declarations! | |
| 330 PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo
per); | |
| 331 PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo
per); | |
| 332 PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo
oper); | |
| 333 PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo
oper); | |
| 334 | |
| 335 // TODO(edisonn): perf!!! | |
| 336 | |
| 337 static SkColorTable* getGrayColortable() { | |
| 338 static SkColorTable* grayColortable = NULL; | |
| 339 if (grayColortable == NULL) { | |
| 340 SkPMColor* colors = new SkPMColor[256]; | |
| 341 for (int i = 0 ; i < 256; i++) { | |
| 342 colors[i] = SkPreMultiplyARGB(255, i, i, i); | |
| 343 } | |
| 344 grayColortable = new SkColorTable(colors, 256); | |
| 345 } | |
| 346 return grayColortable; | |
| 347 } | |
| 348 | |
| 349 static SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, s
ize_t uncompressedStreamLength, | |
| 350 int width, int height, int bytesPerLine, | |
| 351 int bpc, const std::string& colorSpace, | |
| 352 bool transparencyMask) { | |
| 353 SkBitmap bitmap; | |
| 354 | |
| 355 //int components = GetColorSpaceComponents(colorSpace); | |
| 356 //#define MAX_COMPONENTS 10 | |
| 357 | |
| 358 // TODO(edisonn): assume start of lines are aligned at 32 bits? | |
| 359 // Is there a faster way to load the uncompressed stream into a bitmap? | |
| 360 | |
| 361 // minimal support for now | |
| 362 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) { | |
| 363 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * size
of(SkColor)); | |
| 364 | |
| 365 for (int h = 0 ; h < height; h++) { | |
| 366 long i = width * (h); | |
| 367 for (int w = 0 ; w < width; w++) { | |
| 368 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 *
w], | |
| 369 uncompressedStream[3 *
w + 1], | |
| 370 uncompressedStream[3 *
w + 2]); | |
| 371 i++; | |
| 372 } | |
| 373 uncompressedStream += bytesPerLine; | |
| 374 } | |
| 375 | |
| 376 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
| 377 bitmap.setPixels(uncompressedStreamArgb); | |
| 378 } | |
| 379 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) { | |
| 380 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * hei
ght); | |
| 381 | |
| 382 for (int h = 0 ; h < height; h++) { | |
| 383 long i = width * (h); | |
| 384 for (int w = 0 ; w < width; w++) { | |
| 385 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedS
tream[w] : | |
| 386 uncompressedStream[
w]; | |
| 387 i++; | |
| 388 } | |
| 389 uncompressedStream += bytesPerLine; | |
| 390 } | |
| 391 | |
| 392 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIn
dex8_Config, | |
| 393 width, height); | |
| 394 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGray
Colortable()); | |
| 395 } | |
| 396 | |
| 397 // TODO(edisonn): Report Warning, NYI, or error | |
| 398 return bitmap; | |
| 399 } | |
| 400 | |
| 401 // utils | |
| 402 | |
| 403 // TODO(edisonn): add cache, or put the bitmap property directly on the PdfObjec
t | |
| 404 // TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config | |
| 405 // TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 2
22, 444 to closest | |
| 406 // skia format, through a table | |
| 407 | |
| 408 // this functions returns the image, it does not look at the smask. | |
| 409 | |
| 410 static SkBitmap getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary*
image, bool transparencyMask) { | |
| 411 if (image == NULL || !image->hasStream()) { | |
| 412 // TODO(edisonn): report warning to be used in testing. | |
| 413 return SkBitmap(); | |
| 414 } | |
| 415 | |
| 416 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc); | |
| 417 int64_t width = image->Width(pdfContext->fPdfDoc); | |
| 418 int64_t height = image->Height(pdfContext->fPdfDoc); | |
| 419 std::string colorSpace = "DeviceRGB"; | |
| 420 | |
| 421 // TODO(edisonn): color space can be an array too! | |
| 422 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) { | |
| 423 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc); | |
| 424 } | |
| 425 | |
| 426 /* | |
| 427 bool imageMask = image->imageMask(); | |
| 428 | |
| 429 if (imageMask) { | |
| 430 if (bpc != 0 && bpc != 1) { | |
| 431 // TODO(edisonn): report warning to be used in testing. | |
| 432 return SkBitmap(); | |
| 433 } | |
| 434 bpc = 1; | |
| 435 } | |
| 436 */ | |
| 437 | |
| 438 unsigned char* uncompressedStream = NULL; | |
| 439 size_t uncompressedStreamLength = 0; | |
| 440 | |
| 441 SkPdfStream* stream = (SkPdfStream*)image; | |
| 442 | |
| 443 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompres
sedStreamLength, pdfContext->fPdfDoc->allocator()) || | |
| 444 uncompressedStream == NULL || uncompressedStreamLength == 0) { | |
| 445 // TODO(edisonn): report warning to be used in testing. | |
| 446 return SkBitmap(); | |
| 447 } | |
| 448 | |
| 449 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stre
am; | |
| 450 | |
| 451 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) && | |
| 452 streamDict->getFilterAsName(NULL) == "
DCTDecode") || | |
| 453 (streamDict->isFilterAArray(NULL) && | |
| 454 streamDict->getFilterAsArray(NULL)->si
ze() > 0 && | |
| 455 streamDict->getFilterAsArray(NULL)->ob
jAtAIndex(0)->isName() && | |
| 456 streamDict->getFilterAsArray(NULL)->ob
jAtAIndex(0)->nameValue2() == "DCTDecode"))) { | |
| 457 SkBitmap bitmap; | |
| 458 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLengt
h, &bitmap); | |
| 459 return bitmap; | |
| 460 } | |
| 461 | |
| 462 | |
| 463 | |
| 464 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw
... | |
| 465 // PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc, | |
| 466 // obj.GetDictionary().GetKey(PdfNa
me("Filter"))); | |
| 467 // if (value && value->IsArray() && value->GetArray().GetSize() == 1) { | |
| 468 // value = resolveReferenceObject(pdfContext->fPdfDoc, | |
| 469 // &value->GetArray()[0]); | |
| 470 // } | |
| 471 // if (value && value->IsName() && value->GetName().GetName() == "DCTDecode")
{ | |
| 472 // SkStream stream = SkStream:: | |
| 473 // SkImageDecoder::Factory() | |
| 474 // } | |
| 475 | |
| 476 int bytesPerLine = uncompressedStreamLength / height; | |
| 477 #ifdef PDF_TRACE | |
| 478 if (uncompressedStreamLength % height != 0) { | |
| 479 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n"); | |
| 480 } | |
| 481 #endif | |
| 482 | |
| 483 SkBitmap bitmap = transferImageStreamToBitmap( | |
| 484 (unsigned char*)uncompressedStream, uncompressedStreamLength, | |
| 485 (int)width, (int)height, bytesPerLine, | |
| 486 (int)bpc, colorSpace, | |
| 487 transparencyMask); | |
| 488 | |
| 489 return bitmap; | |
| 490 } | |
| 491 | |
| 492 static SkBitmap getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary*
obj) { | |
| 493 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc); | |
| 494 | |
| 495 if (sMask) { | |
| 496 return getImageFromObject(pdfContext, sMask, true); | |
| 497 } | |
| 498 | |
| 499 // TODO(edisonn): implement GS SMask. Default to empty right now. | |
| 500 return pdfContext->fGraphicsState.fSMask; | |
| 501 } | |
| 502 | |
| 503 static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdf
ImageDictionary* skpdfimage) { | |
| 504 if (skpdfimage == NULL) { | |
| 505 return kIgnoreError_PdfResult; | |
| 506 } | |
| 507 | |
| 508 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false); | |
| 509 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage); | |
| 510 | |
| 511 canvas->save(); | |
| 512 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
| 513 | |
| 514 #if 1 | |
| 515 SkScalar z = SkIntToScalar(0); | |
| 516 SkScalar one = SkIntToScalar(1); | |
| 517 | |
| 518 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make
(one, one), SkPoint::Make(z, one)}; | |
| 519 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Ma
ke(one, z), SkPoint::Make(z, z)}; | |
| 520 SkMatrix flip; | |
| 521 SkAssertResult(flip.setPolyToPoly(from, to, 4)); | |
| 522 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fMatrix; | |
| 523 solveImageFlip.preConcat(flip); | |
| 524 canvas->setMatrix(solveImageFlip); | |
| 525 #endif | |
| 526 | |
| 527 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0),
SkDoubleToScalar(1.0), SkDoubleToScalar(1.0)); | |
| 528 | |
| 529 if (sMask.empty()) { | |
| 530 canvas->drawBitmapRect(image, dst, NULL); | |
| 531 } else { | |
| 532 canvas->saveLayer(&dst, NULL); | |
| 533 canvas->drawBitmapRect(image, dst, NULL); | |
| 534 SkPaint xfer; | |
| 535 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false); | |
| 536 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_M
ode | |
| 537 canvas->drawBitmapRect(sMask, dst, &xfer); | |
| 538 canvas->restore(); | |
| 539 } | |
| 540 | |
| 541 canvas->restore(); | |
| 542 | |
| 543 return kPartial_PdfResult; | |
| 544 } | |
| 545 | |
| 546 | |
| 547 | |
| 548 | |
| 549 static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfT
ype1FormDictionary* skobj) { | |
| 550 if (!skobj || !skobj->hasStream()) { | |
| 551 return kIgnoreError_PdfResult; | |
| 552 } | |
| 553 | |
| 554 PdfOp_q(pdfContext, canvas, NULL); | |
| 555 canvas->save(); | |
| 556 | |
| 557 | |
| 558 if (skobj->Resources(pdfContext->fPdfDoc)) { | |
| 559 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPd
fDoc); | |
| 560 } | |
| 561 | |
| 562 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix"); | |
| 563 | |
| 564 if (skobj->has_Matrix()) { | |
| 565 pdfContext->fGraphicsState.fMatrix.preConcat(skobj->Matrix(pdfContext->f
PdfDoc)); | |
| 566 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatri
x; | |
| 567 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatr
ix; | |
| 568 // TODO(edisonn) reset matrixTm and matricTlm also? | |
| 569 } | |
| 570 | |
| 571 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix"); | |
| 572 | |
| 573 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
| 574 | |
| 575 if (skobj->has_BBox()) { | |
| 576 canvas->clipRect(skobj->BBox(pdfContext->fPdfDoc), SkRegion::kIntersect_
Op, true); // TODO(edisonn): AA from settings. | |
| 577 } | |
| 578 | |
| 579 // TODO(edisonn): iterate smart on the stream even if it is compressed, toke
nize it as we go. | |
| 580 // For this PdfContentsTokenizer needs to be extended. | |
| 581 | |
| 582 SkPdfStream* stream = (SkPdfStream*)skobj; | |
| 583 | |
| 584 SkPdfNativeTokenizer* tokenizer = pdfContext->fPdfDoc->tokenizerOfStream(str
eam); | |
| 585 if (tokenizer != NULL) { | |
| 586 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); | |
| 587 looper.loop(); | |
| 588 delete tokenizer; | |
| 589 } | |
| 590 | |
| 591 // TODO(edisonn): should we restore the variable stack at the same state? | |
| 592 // There could be operands left, that could be consumed by a parent tokenize
r when we pop. | |
| 593 canvas->restore(); | |
| 594 PdfOp_Q(pdfContext, canvas, NULL); | |
| 595 return kPartial_PdfResult; | |
| 596 } | |
| 597 | |
| 598 //static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const
SkPdfObject* obj) { | |
| 599 // return kNYI_PdfResult; | |
| 600 //} | |
| 601 | |
| 602 PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObjec
t* skobj, SkRect bBox, SkMatrix matrix, double textSize) { | |
| 603 if (!skobj || !skobj->hasStream()) { | |
| 604 return kIgnoreError_PdfResult; | |
| 605 } | |
| 606 | |
| 607 PdfOp_q(pdfContext, canvas, NULL); | |
| 608 canvas->save(); | |
| 609 | |
| 610 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); | |
| 611 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), Sk
DoubleToScalar(textSize)); | |
| 612 | |
| 613 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm; | |
| 614 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix; | |
| 615 | |
| 616 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix"); | |
| 617 | |
| 618 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
| 619 | |
| 620 SkRect rm = bBox; | |
| 621 pdfContext->fGraphicsState.fMatrix.mapRect(&rm); | |
| 622 | |
| 623 SkTraceRect(rm, "bbox mapped"); | |
| 624 | |
| 625 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA
from settings. | |
| 626 | |
| 627 // TODO(edisonn): iterate smart on the stream even if it is compressed, toke
nize it as we go. | |
| 628 // For this PdfContentsTokenizer needs to be extended. | |
| 629 | |
| 630 SkPdfStream* stream = (SkPdfStream*)skobj; | |
| 631 | |
| 632 SkPdfNativeTokenizer* tokenizer = pdfContext->fPdfDoc->tokenizerOfStream(str
eam); | |
| 633 if (tokenizer != NULL) { | |
| 634 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); | |
| 635 looper.loop(); | |
| 636 delete tokenizer; | |
| 637 } | |
| 638 | |
| 639 // TODO(edisonn): should we restore the variable stack at the same state? | |
| 640 // There could be operands left, that could be consumed by a parent tokenize
r when we pop. | |
| 641 canvas->restore(); | |
| 642 PdfOp_Q(pdfContext, canvas, NULL); | |
| 643 | |
| 644 return kPartial_PdfResult; | |
| 645 } | |
| 646 | |
| 647 | |
| 648 // TODO(edisonn): make sure the pointer is unique | |
| 649 std::set<const SkPdfObject*> gInRendering; | |
| 650 | |
| 651 class CheckRecursiveRendering { | |
| 652 const SkPdfObject* fUniqueData; | |
| 653 public: | |
| 654 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) { | |
| 655 gInRendering.insert(obj); | |
| 656 } | |
| 657 | |
| 658 ~CheckRecursiveRendering() { | |
| 659 //SkASSERT(fObj.fInRendering); | |
| 660 gInRendering.erase(fUniqueData); | |
| 661 } | |
| 662 | |
| 663 static bool IsInRendering(const SkPdfObject* obj) { | |
| 664 return gInRendering.find(obj) != gInRendering.end(); | |
| 665 } | |
| 666 }; | |
| 667 | |
| 668 static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdf
Object* obj) { | |
| 669 if (CheckRecursiveRendering::IsInRendering(obj)) { | |
| 670 // Oops, corrupt PDF! | |
| 671 return kIgnoreError_PdfResult; | |
| 672 } | |
| 673 | |
| 674 CheckRecursiveRendering checkRecursion(obj); | |
| 675 | |
| 676 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj)) | |
| 677 { | |
| 678 case kImageDictionary_SkPdfObjectType: | |
| 679 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)ob
j); | |
| 680 case kType1FormDictionary_SkPdfObjectType: | |
| 681 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*
)obj); | |
| 682 //case kObjectDictionaryXObjectPS_SkPdfObjectType: | |
| 683 //return doXObject_PS(skxobj.asPS()); | |
| 684 default: | |
| 685 return kIgnoreError_PdfResult; | |
| 686 } | |
| 687 } | |
| 688 | |
| 689 PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo
per) { | |
| 690 pdfContext->fStateStack.push(pdfContext->fGraphicsState); | |
| 691 canvas->save(); | |
| 692 return kOK_PdfResult; | |
| 693 } | |
| 694 | |
| 695 PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo
per) { | |
| 696 pdfContext->fGraphicsState = pdfContext->fStateStack.top(); | |
| 697 pdfContext->fStateStack.pop(); | |
| 698 canvas->restore(); | |
| 699 return kOK_PdfResult; | |
| 700 } | |
| 701 | |
| 702 static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 703 double array[6]; | |
| 704 for (int i = 0 ; i < 6 ; i++) { | |
| 705 array[5 - i] = pdfContext->fObjectStack.top()->numberValue(); | |
| 706 pdfContext->fObjectStack.pop(); | |
| 707 } | |
| 708 | |
| 709 // a b | |
| 710 // c d | |
| 711 // e f | |
| 712 | |
| 713 // 0 1 | |
| 714 // 2 3 | |
| 715 // 4 5 | |
| 716 | |
| 717 // sx ky | |
| 718 // kx sy | |
| 719 // tx ty | |
| 720 SkMatrix matrix = SkMatrixFromPdfMatrix(array); | |
| 721 | |
| 722 pdfContext->fGraphicsState.fMatrix.preConcat(matrix); | |
| 723 | |
| 724 #ifdef PDF_TRACE | |
| 725 printf("cm "); | |
| 726 for (int i = 0 ; i < 6 ; i++) { | |
| 727 printf("%f ", array[i]); | |
| 728 } | |
| 729 printf("\n"); | |
| 730 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "cm"); | |
| 731 #endif | |
| 732 | |
| 733 return kOK_PdfResult; | |
| 734 } | |
| 735 | |
| 736 //leading TL Set the text leading, Tl | |
| 737 //, to leading, which is a number expressed in unscaled text | |
| 738 //space units. Text leading is used only by the T*, ', and " operators. Initial
value: 0. | |
| 739 static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 740 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fOb
jectStack.pop(); | |
| 741 | |
| 742 pdfContext->fGraphicsState.fTextLeading = ty; | |
| 743 | |
| 744 return kOK_PdfResult; | |
| 745 } | |
| 746 | |
| 747 static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 748 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObje
ctStack.pop(); | |
| 749 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObje
ctStack.pop(); | |
| 750 | |
| 751 double array[6] = {1, 0, 0, 1, tx, ty}; | |
| 752 SkMatrix matrix = SkMatrixFromPdfMatrix(array); | |
| 753 | |
| 754 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); | |
| 755 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix); | |
| 756 | |
| 757 return kPartial_PdfResult; | |
| 758 } | |
| 759 | |
| 760 static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 761 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObje
ctStack.pop(); | |
| 762 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObje
ctStack.pop(); | |
| 763 | |
| 764 // TODO(edisonn): Create factory methods or constructors so native is hidden | |
| 765 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty); | |
| 766 pdfContext->fObjectStack.push(_ty); | |
| 767 | |
| 768 PdfOp_TL(pdfContext, canvas, looper); | |
| 769 | |
| 770 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx); | |
| 771 pdfContext->fObjectStack.push(vtx); | |
| 772 | |
| 773 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty); | |
| 774 pdfContext->fObjectStack.push(vty); | |
| 775 | |
| 776 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper); | |
| 777 | |
| 778 // TODO(edisonn): delete all the objects after rendering was complete, in th
is way pdf is rendered faster | |
| 779 // and the cleanup can happen while the user looks at the image | |
| 780 delete _ty; | |
| 781 delete vtx; | |
| 782 delete vty; | |
| 783 | |
| 784 return ret; | |
| 785 } | |
| 786 | |
| 787 static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 788 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
| 789 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
| 790 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
| 791 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
| 792 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
| 793 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
| 794 | |
| 795 double array[6]; | |
| 796 array[0] = a; | |
| 797 array[1] = b; | |
| 798 array[2] = c; | |
| 799 array[3] = d; | |
| 800 array[4] = e; | |
| 801 array[5] = f; | |
| 802 | |
| 803 SkMatrix matrix = SkMatrixFromPdfMatrix(array); | |
| 804 matrix.postConcat(pdfContext->fGraphicsState.fMatrix); | |
| 805 | |
| 806 // TODO(edisonn): Text positioning. | |
| 807 pdfContext->fGraphicsState.fMatrixTm = matrix; | |
| 808 pdfContext->fGraphicsState.fMatrixTlm = matrix;; | |
| 809 | |
| 810 return kPartial_PdfResult; | |
| 811 } | |
| 812 | |
| 813 //— T* Move to the start of the next line. This operator has the same effect as
the code | |
| 814 //0 Tl Td | |
| 815 //where Tl is the current leading parameter in the text state | |
| 816 static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
| 817 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0); | |
| 818 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.f
TextLeading); | |
| 819 | |
| 820 pdfContext->fObjectStack.push(zero); | |
| 821 pdfContext->fObjectStack.push(tl); | |
| 822 | |
| 823 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper); | |
| 824 | |
| 825 delete zero; // TODO(edisonn): do not alocate and delete constants! | |
| 826 delete tl; | |
| 827 | |
| 828 return ret; | |
| 829 } | |
| 830 | |
| 831 static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 832 if (pdfContext->fGraphicsState.fPathClosed) { | |
| 833 pdfContext->fGraphicsState.fPath.reset(); | |
| 834 pdfContext->fGraphicsState.fPathClosed = false; | |
| 835 } | |
| 836 | |
| 837 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->number
Value(); pdfContext->fObjectStack.pop(); | |
| 838 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->number
Value(); pdfContext->fObjectStack.pop(); | |
| 839 | |
| 840 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphi
csState.fCurPosX), | |
| 841 SkDoubleToScalar(pdfContext->fGraphics
State.fCurPosY)); | |
| 842 | |
| 843 return kOK_PdfResult; | |
| 844 } | |
| 845 | |
| 846 static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 847 if (pdfContext->fGraphicsState.fPathClosed) { | |
| 848 pdfContext->fGraphicsState.fPath.reset(); | |
| 849 pdfContext->fGraphicsState.fPathClosed = false; | |
| 850 } | |
| 851 | |
| 852 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->number
Value(); pdfContext->fObjectStack.pop(); | |
| 853 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->number
Value(); pdfContext->fObjectStack.pop(); | |
| 854 | |
| 855 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphi
csState.fCurPosX), | |
| 856 SkDoubleToScalar(pdfContext->fGraphics
State.fCurPosY)); | |
| 857 | |
| 858 return kOK_PdfResult; | |
| 859 } | |
| 860 | |
| 861 static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 862 if (pdfContext->fGraphicsState.fPathClosed) { | |
| 863 pdfContext->fGraphicsState.fPath.reset(); | |
| 864 pdfContext->fGraphicsState.fPathClosed = false; | |
| 865 } | |
| 866 | |
| 867 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 868 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 869 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 870 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 871 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 872 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 873 | |
| 874 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca
lar(y1), | |
| 875 SkDoubleToScalar(x2), SkDoubleToScal
ar(y2), | |
| 876 SkDoubleToScalar(x3), SkDoubleToScal
ar(y3)); | |
| 877 | |
| 878 pdfContext->fGraphicsState.fCurPosX = x3; | |
| 879 pdfContext->fGraphicsState.fCurPosY = y3; | |
| 880 | |
| 881 return kOK_PdfResult; | |
| 882 } | |
| 883 | |
| 884 static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 885 if (pdfContext->fGraphicsState.fPathClosed) { | |
| 886 pdfContext->fGraphicsState.fPath.reset(); | |
| 887 pdfContext->fGraphicsState.fPathClosed = false; | |
| 888 } | |
| 889 | |
| 890 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 891 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 892 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 893 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 894 double y1 = pdfContext->fGraphicsState.fCurPosY; | |
| 895 double x1 = pdfContext->fGraphicsState.fCurPosX; | |
| 896 | |
| 897 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca
lar(y1), | |
| 898 SkDoubleToScalar(x2), SkDoubleToScal
ar(y2), | |
| 899 SkDoubleToScalar(x3), SkDoubleToScal
ar(y3)); | |
| 900 | |
| 901 pdfContext->fGraphicsState.fCurPosX = x3; | |
| 902 pdfContext->fGraphicsState.fCurPosY = y3; | |
| 903 | |
| 904 return kOK_PdfResult; | |
| 905 } | |
| 906 | |
| 907 static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 908 if (pdfContext->fGraphicsState.fPathClosed) { | |
| 909 pdfContext->fGraphicsState.fPath.reset(); | |
| 910 pdfContext->fGraphicsState.fPathClosed = false; | |
| 911 } | |
| 912 | |
| 913 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 914 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 915 double y2 = pdfContext->fGraphicsState.fCurPosY; | |
| 916 double x2 = pdfContext->fGraphicsState.fCurPosX; | |
| 917 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 918 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 919 | |
| 920 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca
lar(y1), | |
| 921 SkDoubleToScalar(x2), SkDoubleToScal
ar(y2), | |
| 922 SkDoubleToScalar(x3), SkDoubleToScal
ar(y3)); | |
| 923 | |
| 924 pdfContext->fGraphicsState.fCurPosX = x3; | |
| 925 pdfContext->fGraphicsState.fCurPosY = y3; | |
| 926 | |
| 927 return kOK_PdfResult; | |
| 928 } | |
| 929 | |
| 930 static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 931 if (pdfContext->fGraphicsState.fPathClosed) { | |
| 932 pdfContext->fGraphicsState.fPath.reset(); | |
| 933 pdfContext->fGraphicsState.fPathClosed = false; | |
| 934 } | |
| 935 | |
| 936 double height = pdfContext->fObjectStack.top()->numberValue(); pdfConte
xt->fObjectStack.pop(); | |
| 937 double width = pdfContext->fObjectStack.top()->numberValue(); pdfConte
xt->fObjectStack.pop(); | |
| 938 double y = pdfContext->fObjectStack.top()->numberValue(); pdfConte
xt->fObjectStack.pop(); | |
| 939 double x = pdfContext->fObjectStack.top()->numberValue(); pdfConte
xt->fObjectStack.pop(); | |
| 940 | |
| 941 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScal
ar(y), | |
| 942 SkDoubleToScalar(x + width), SkDouble
ToScalar(y + height)); | |
| 943 | |
| 944 pdfContext->fGraphicsState.fCurPosX = x; | |
| 945 pdfContext->fGraphicsState.fCurPosY = y + height; | |
| 946 | |
| 947 return kOK_PdfResult; | |
| 948 } | |
| 949 | |
| 950 static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 951 pdfContext->fGraphicsState.fPath.close(); | |
| 952 return kOK_PdfResult; | |
| 953 } | |
| 954 | |
| 955 static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, b
ool fill, bool stroke, bool close, bool evenOdd) { | |
| 956 SkPath path = pdfContext->fGraphicsState.fPath; | |
| 957 | |
| 958 if (close) { | |
| 959 path.close(); | |
| 960 } | |
| 961 | |
| 962 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
| 963 | |
| 964 SkPaint paint; | |
| 965 | |
| 966 SkPoint line[2]; | |
| 967 if (fill && !stroke && path.isLine(line)) { | |
| 968 paint.setStyle(SkPaint::kStroke_Style); | |
| 969 | |
| 970 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); | |
| 971 paint.setStrokeWidth(SkDoubleToScalar(0)); | |
| 972 | |
| 973 canvas->drawPath(path, paint); | |
| 974 } else { | |
| 975 if (fill) { | |
| 976 paint.setStyle(SkPaint::kFill_Style); | |
| 977 if (evenOdd) { | |
| 978 path.setFillType(SkPath::kEvenOdd_FillType); | |
| 979 } | |
| 980 | |
| 981 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); | |
| 982 | |
| 983 canvas->drawPath(path, paint); | |
| 984 } | |
| 985 | |
| 986 if (stroke) { | |
| 987 paint.setStyle(SkPaint::kStroke_Style); | |
| 988 | |
| 989 pdfContext->fGraphicsState.applyGraphicsState(&paint, true); | |
| 990 | |
| 991 path.setFillType(SkPath::kWinding_FillType); // reset it, just in c
ase it messes up the stroke | |
| 992 canvas->drawPath(path, paint); | |
| 993 } | |
| 994 } | |
| 995 | |
| 996 pdfContext->fGraphicsState.fPath.reset(); | |
| 997 // todo zoom ... other stuff ? | |
| 998 | |
| 999 if (pdfContext->fGraphicsState.fHasClipPathToApply) { | |
| 1000 #ifndef PDF_DEBUG_NO_CLIPING | |
| 1001 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kInters
ect_Op, true); | |
| 1002 #endif | |
| 1003 } | |
| 1004 | |
| 1005 //pdfContext->fGraphicsState.fClipPath.reset(); | |
| 1006 pdfContext->fGraphicsState.fHasClipPathToApply = false; | |
| 1007 | |
| 1008 return kPartial_PdfResult; | |
| 1009 | |
| 1010 } | |
| 1011 | |
| 1012 static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1013 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false); | |
| 1014 } | |
| 1015 | |
| 1016 static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1017 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false); | |
| 1018 } | |
| 1019 | |
| 1020 static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1021 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false); | |
| 1022 } | |
| 1023 | |
| 1024 static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1025 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false); | |
| 1026 } | |
| 1027 | |
| 1028 static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
| 1029 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true); | |
| 1030 } | |
| 1031 | |
| 1032 static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1033 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false); | |
| 1034 } | |
| 1035 | |
| 1036 static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
| 1037 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true); | |
| 1038 } | |
| 1039 | |
| 1040 static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1041 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false); | |
| 1042 } | |
| 1043 | |
| 1044 static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
| 1045 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true); | |
| 1046 } | |
| 1047 | |
| 1048 static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1049 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
| 1050 if (pdfContext->fGraphicsState.fHasClipPathToApply) { | |
| 1051 #ifndef PDF_DEBUG_NO_CLIPING | |
| 1052 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kInters
ect_Op, true); | |
| 1053 #endif | |
| 1054 } | |
| 1055 | |
| 1056 //pdfContext->fGraphicsState.fClipPath.reset(); | |
| 1057 pdfContext->fGraphicsState.fHasClipPathToApply = false; | |
| 1058 | |
| 1059 pdfContext->fGraphicsState.fPathClosed = true; | |
| 1060 | |
| 1061 return kOK_PdfResult; | |
| 1062 } | |
| 1063 | |
| 1064 static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1065 pdfContext->fGraphicsState.fTextBlock = true; | |
| 1066 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix; | |
| 1067 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix; | |
| 1068 | |
| 1069 return kPartial_PdfResult; | |
| 1070 } | |
| 1071 | |
| 1072 static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1073 if (!pdfContext->fGraphicsState.fTextBlock) { | |
| 1074 return kIgnoreError_PdfResult; | |
| 1075 } | |
| 1076 // TODO(edisonn): anything else to be done once we are done with draw text?
Like restore stack? | |
| 1077 return kPartial_PdfResult; | |
| 1078 } | |
| 1079 | |
| 1080 //font size Tf Set the text font, Tf | |
| 1081 //, to font and the text font size, Tfs, to size. font is the name of a | |
| 1082 //font resource in the Fontsubdictionary of the current resource dictionary; siz
e is | |
| 1083 //a number representing a scale factor. There is no initial value for either fon
t or | |
| 1084 //size; they must be specified explicitly using Tf before any text is shown. | |
| 1085 static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1086 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->nu
mberValue(); pdfContext->fObjectStack.pop(); | |
| 1087 const char* fontName = pdfContext->fObjectStack.top()->nameValue();
pdfContext->fObjectStack.pop(); | |
| 1088 | |
| 1089 #ifdef PDF_TRACE | |
| 1090 printf("font name: %s\n", fontName); | |
| 1091 #endif | |
| 1092 | |
| 1093 if (pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) { | |
| 1094 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfCo
ntext->fPdfDoc)->get(fontName); | |
| 1095 objFont = pdfContext->fPdfDoc->resolveReference(objFont); | |
| 1096 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDicti
onary(objFont)) { | |
| 1097 // TODO(edisonn): try to recover and draw it any way? | |
| 1098 return kIgnoreError_PdfResult; | |
| 1099 } | |
| 1100 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont; | |
| 1101 | |
| 1102 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc
, fd); | |
| 1103 | |
| 1104 if (skfont) { | |
| 1105 pdfContext->fGraphicsState.fSkFont = skfont; | |
| 1106 } | |
| 1107 } | |
| 1108 return kIgnoreError_PdfResult; | |
| 1109 } | |
| 1110 | |
| 1111 static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1112 if (!pdfContext->fGraphicsState.fTextBlock) { | |
| 1113 // TODO(edisonn): try to recover and draw it any way? | |
| 1114 return kIgnoreError_PdfResult; | |
| 1115 } | |
| 1116 | |
| 1117 PdfResult ret = DrawText(pdfContext, | |
| 1118 pdfContext->fObjectStack.top(), | |
| 1119 canvas); | |
| 1120 pdfContext->fObjectStack.pop(); | |
| 1121 | |
| 1122 return ret; | |
| 1123 } | |
| 1124 | |
| 1125 static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenL
ooper** looper) { | |
| 1126 if (!pdfContext->fGraphicsState.fTextBlock) { | |
| 1127 // TODO(edisonn): try to recover and draw it any way? | |
| 1128 return kIgnoreError_PdfResult; | |
| 1129 } | |
| 1130 | |
| 1131 PdfOp_T_star(pdfContext, canvas, looper); | |
| 1132 // Do not pop, and push, just transfer the param to Tj | |
| 1133 return PdfOp_Tj(pdfContext, canvas, looper); | |
| 1134 } | |
| 1135 | |
| 1136 static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, Pdf
TokenLooper** looper) { | |
| 1137 if (!pdfContext->fGraphicsState.fTextBlock) { | |
| 1138 // TODO(edisonn): try to recover and draw it any way? | |
| 1139 return kIgnoreError_PdfResult; | |
| 1140 } | |
| 1141 | |
| 1142 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObject
Stack.pop(); | |
| 1143 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObject
Stack.pop(); | |
| 1144 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObject
Stack.pop(); | |
| 1145 | |
| 1146 pdfContext->fObjectStack.push(aw); | |
| 1147 PdfOp_Tw(pdfContext, canvas, looper); | |
| 1148 | |
| 1149 pdfContext->fObjectStack.push(ac); | |
| 1150 PdfOp_Tc(pdfContext, canvas, looper); | |
| 1151 | |
| 1152 pdfContext->fObjectStack.push(str); | |
| 1153 PdfOp_quote(pdfContext, canvas, looper); | |
| 1154 | |
| 1155 return kPartial_PdfResult; | |
| 1156 } | |
| 1157 | |
| 1158 static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1159 if (!pdfContext->fGraphicsState.fTextBlock) { | |
| 1160 // TODO(edisonn): try to recover and draw it any way? | |
| 1161 return kIgnoreError_PdfResult; | |
| 1162 } | |
| 1163 | |
| 1164 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top(); | |
| 1165 pdfContext->fObjectStack.pop(); | |
| 1166 | |
| 1167 if (!array->isArray()) { | |
| 1168 return kIgnoreError_PdfResult; | |
| 1169 } | |
| 1170 | |
| 1171 for( int i=0; i<static_cast<int>(array->size()); i++ ) | |
| 1172 { | |
| 1173 if( (*array)[i]->isAnyString()) { | |
| 1174 SkPdfObject* obj = (*array)[i]; | |
| 1175 DrawText(pdfContext, | |
| 1176 obj, | |
| 1177 canvas); | |
| 1178 } else if ((*array)[i]->isNumber()) { | |
| 1179 double dx = (*array)[i]->numberValue(); | |
| 1180 SkMatrix matrix; | |
| 1181 matrix.setAll(SkDoubleToScalar(1), | |
| 1182 SkDoubleToScalar(0), | |
| 1183 // TODO(edisonn): use writing mode, vertical/horizonta
l. | |
| 1184 SkDoubleToScalar(-dx), // amount is substracted!!! | |
| 1185 SkDoubleToScalar(0), | |
| 1186 SkDoubleToScalar(1), | |
| 1187 SkDoubleToScalar(0), | |
| 1188 SkDoubleToScalar(0), | |
| 1189 SkDoubleToScalar(0), | |
| 1190 SkDoubleToScalar(1)); | |
| 1191 | |
| 1192 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); | |
| 1193 } | |
| 1194 } | |
| 1195 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText befor
e returing OK. | |
| 1196 } | |
| 1197 | |
| 1198 static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColo
rOperator* colorOperator) { | |
| 1199 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->nameValue();
pdfContext->fObjectStack.pop(); | |
| 1200 return kOK_PdfResult; | |
| 1201 } | |
| 1202 | |
| 1203 static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1204 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking
); | |
| 1205 } | |
| 1206 | |
| 1207 static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1208 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok
ing); | |
| 1209 } | |
| 1210 | |
| 1211 static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColo
rOperator* colorOperator) { | |
| 1212 double c[4]; | |
| 1213 // int64_t v[4]; | |
| 1214 | |
| 1215 int n = GetColorSpaceComponents(colorOperator->fColorSpace); | |
| 1216 | |
| 1217 bool doubles = true; | |
| 1218 if (strcmp(colorOperator->fColorSpace, "Indexed") == 0) { | |
| 1219 doubles = false; | |
| 1220 } | |
| 1221 | |
| 1222 #ifdef PDF_TRACE | |
| 1223 printf("color space = %s, N = %i\n", colorOperator->fColorSpace, n); | |
| 1224 #endif | |
| 1225 | |
| 1226 for (int i = n - 1; i >= 0 ; i--) { | |
| 1227 if (doubles) { | |
| 1228 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
| 1229 // } else { | |
| 1230 // v[i] = pdfContext->fObjectStack.top()->intValue(); pdfConte
xt->fObjectStack.pop(); | |
| 1231 } | |
| 1232 } | |
| 1233 | |
| 1234 // TODO(edisonn): Now, set that color. Only DeviceRGB supported. | |
| 1235 // TODO(edisonn): do possible field values to enum at parsing time! | |
| 1236 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB | |
| 1237 if (strcmp(colorOperator->fColorSpace, "DeviceRGB") == 0 || strcmp(colorOper
ator->fColorSpace, "RGB") == 0) { | |
| 1238 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2])); | |
| 1239 } | |
| 1240 return kPartial_PdfResult; | |
| 1241 } | |
| 1242 | |
| 1243 static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1244 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking
); | |
| 1245 } | |
| 1246 | |
| 1247 static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1248 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok
ing); | |
| 1249 } | |
| 1250 | |
| 1251 static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfCo
lorOperator* colorOperator) { | |
| 1252 //SkPdfString* name; | |
| 1253 if (pdfContext->fObjectStack.top()->isName()) { | |
| 1254 // TODO(edisonn): get name, pass it | |
| 1255 pdfContext->fObjectStack.pop(); | |
| 1256 } | |
| 1257 | |
| 1258 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implemen
t spec. | |
| 1259 PdfOp_SC_sc(pdfContext, canvas, colorOperator); | |
| 1260 | |
| 1261 return kPartial_PdfResult; | |
| 1262 } | |
| 1263 | |
| 1264 static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
| 1265 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroki
ng); | |
| 1266 } | |
| 1267 | |
| 1268 static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
| 1269 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStr
oking); | |
| 1270 } | |
| 1271 | |
| 1272 static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorO
perator* colorOperator) { | |
| 1273 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfCont
ext->fObjectStack.pop(); | |
| 1274 return kNYI_PdfResult; | |
| 1275 } | |
| 1276 | |
| 1277 static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1278 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); | |
| 1279 } | |
| 1280 | |
| 1281 static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1282 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrokin
g); | |
| 1283 } | |
| 1284 | |
| 1285 static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColo
rOperator* colorOperator) { | |
| 1286 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 1287 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 1288 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
| 1289 | |
| 1290 colorOperator->fColorSpace = "DeviceRGB"; | |
| 1291 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b)); | |
| 1292 return kOK_PdfResult; | |
| 1293 } | |
| 1294 | |
| 1295 static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1296 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking
); | |
| 1297 } | |
| 1298 | |
| 1299 static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1300 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok
ing); | |
| 1301 } | |
| 1302 | |
| 1303 static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorO
perator* colorOperator) { | |
| 1304 // TODO(edisonn): spec has some rules about overprint, implement them. | |
| 1305 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext
->fObjectStack.pop(); | |
| 1306 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext
->fObjectStack.pop(); | |
| 1307 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext
->fObjectStack.pop(); | |
| 1308 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext
->fObjectStack.pop(); | |
| 1309 | |
| 1310 colorOperator->fColorSpace = "DeviceCMYK"; | |
| 1311 // TODO(edisonn): Set color. | |
| 1312 return kNYI_PdfResult; | |
| 1313 } | |
| 1314 | |
| 1315 static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1316 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); | |
| 1317 } | |
| 1318 | |
| 1319 static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1320 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrokin
g); | |
| 1321 } | |
| 1322 | |
| 1323 static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1324 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath; | |
| 1325 pdfContext->fGraphicsState.fHasClipPathToApply = true; | |
| 1326 | |
| 1327 return kOK_PdfResult; | |
| 1328 } | |
| 1329 | |
| 1330 static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
| 1331 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath; | |
| 1332 | |
| 1333 #ifdef PDF_TRACE | |
| 1334 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) { | |
| 1335 printf("CLIP IS RECT\n"); | |
| 1336 } | |
| 1337 #endif | |
| 1338 | |
| 1339 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even o
dd. | |
| 1340 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType); | |
| 1341 pdfContext->fGraphicsState.fHasClipPathToApply = true; | |
| 1342 | |
| 1343 return kPartial_PdfResult; | |
| 1344 } | |
| 1345 | |
| 1346 static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1347 *looper = new PdfCompatibilitySectionLooper(); | |
| 1348 return kOK_PdfResult; | |
| 1349 } | |
| 1350 | |
| 1351 static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1352 #ifdef ASSERT_BAD_PDF_OPS | |
| 1353 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, b
ut let's | |
| 1354 // have the assert when testing good pdfs. | |
| 1355 #endif | |
| 1356 return kIgnoreError_PdfResult; | |
| 1357 } | |
| 1358 | |
| 1359 static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1360 *looper = new PdfInlineImageLooper(); | |
| 1361 return kOK_PdfResult; | |
| 1362 } | |
| 1363 | |
| 1364 static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1365 #ifdef ASSERT_BAD_PDF_OPS | |
| 1366 SkASSERT(false); // must be processed in inline image looper, but let's | |
| 1367 // have the assert when testing good pdfs. | |
| 1368 #endif | |
| 1369 return kIgnoreError_PdfResult; | |
| 1370 } | |
| 1371 | |
| 1372 static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1373 #ifdef ASSERT_BAD_PDF_OPS | |
| 1374 SkASSERT(false); // must be processed in inline image looper, but let's | |
| 1375 // have the assert when testing good pdfs. | |
| 1376 #endif | |
| 1377 return kIgnoreError_PdfResult; | |
| 1378 } | |
| 1379 | |
| 1380 //lineWidth w Set the line width in the graphics state (see “Line Width” on page
152). | |
| 1381 static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1382 double lineWidth = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
| 1383 pdfContext->fGraphicsState.fLineWidth = lineWidth; | |
| 1384 | |
| 1385 return kOK_PdfResult; | |
| 1386 } | |
| 1387 | |
| 1388 //lineCap J Set the line cap style in the graphics state (see “Line Cap Style” o
n page 153). | |
| 1389 static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1390 pdfContext->fObjectStack.pop(); | |
| 1391 //double lineCap = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
| 1392 | |
| 1393 return kNYI_PdfResult; | |
| 1394 } | |
| 1395 | |
| 1396 //lineJoin j Set the line join style in the graphics state (see “Line Join Style
” on page 153). | |
| 1397 static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1398 pdfContext->fObjectStack.pop(); | |
| 1399 //double lineJoin = pdfContext->fObjectStack.top()->numberValue(); pdfCo
ntext->fObjectStack.pop(); | |
| 1400 | |
| 1401 return kNYI_PdfResult; | |
| 1402 } | |
| 1403 | |
| 1404 //miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on p
age 153). | |
| 1405 static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1406 pdfContext->fObjectStack.pop(); | |
| 1407 //double miterLimit = pdfContext->fObjectStack.top()->numberValue(); pdf
Context->fObjectStack.pop(); | |
| 1408 | |
| 1409 return kNYI_PdfResult; | |
| 1410 } | |
| 1411 | |
| 1412 //dashArray dashPhase d Set the line dash pattern in the graphics state (see “Li
ne Dash Pattern” on | |
| 1413 //page 155). | |
| 1414 static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1415 pdfContext->fObjectStack.pop(); | |
| 1416 pdfContext->fObjectStack.pop(); | |
| 1417 | |
| 1418 return kNYI_PdfResult; | |
| 1419 } | |
| 1420 | |
| 1421 //intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see
“Rendering Intents” on page 197). | |
| 1422 static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1423 pdfContext->fObjectStack.pop(); | |
| 1424 | |
| 1425 return kNYI_PdfResult; | |
| 1426 } | |
| 1427 | |
| 1428 //flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1,
“Flatness | |
| 1429 //Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci- | |
| 1430 //fies the output device’s default flatness tolerance. | |
| 1431 static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
| 1432 pdfContext->fObjectStack.pop(); | |
| 1433 | |
| 1434 return kNYI_PdfResult; | |
| 1435 } | |
| 1436 | |
| 1437 //dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictN
ame is | |
| 1438 //the name of a graphics state parameter dictionary in the ExtGState subdictiona
ry of the current resource dictionary (see the next section). | |
| 1439 static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1440 const char* name = pdfContext->fObjectStack.top()->nameValue(); pdfContex
t->fObjectStack.pop(); | |
| 1441 | |
| 1442 #ifdef PDF_TRACE | |
| 1443 std::string str; | |
| 1444 #endif | |
| 1445 | |
| 1446 //Next, get the ExtGState Dictionary from the Resource Dictionary: | |
| 1447 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources
->ExtGState(pdfContext->fPdfDoc); | |
| 1448 | |
| 1449 if (extGStateDictionary == NULL) { | |
| 1450 #ifdef PDF_TRACE | |
| 1451 printf("ExtGState is NULL!\n"); | |
| 1452 #endif | |
| 1453 return kIgnoreError_PdfResult; | |
| 1454 } | |
| 1455 | |
| 1456 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictiona
ry->get(name)); | |
| 1457 | |
| 1458 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsState
Dictionary(value)) { | |
| 1459 return kIgnoreError_PdfResult; | |
| 1460 } | |
| 1461 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value; | |
| 1462 | |
| 1463 // TODO(edisonn): now load all those properties in graphic state. | |
| 1464 if (gs == NULL) { | |
| 1465 return kIgnoreError_PdfResult; | |
| 1466 } | |
| 1467 | |
| 1468 if (gs->has_CA()) { | |
| 1469 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA(pdfContext->fPdfD
oc); | |
| 1470 } | |
| 1471 | |
| 1472 if (gs->has_ca()) { | |
| 1473 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca(pdfContext->fP
dfDoc); | |
| 1474 } | |
| 1475 | |
| 1476 if (gs->has_LW()) { | |
| 1477 pdfContext->fGraphicsState.fLineWidth = gs->LW(pdfContext->fPdfDoc); | |
| 1478 } | |
| 1479 | |
| 1480 return kNYI_PdfResult; | |
| 1481 } | |
| 1482 | |
| 1483 //charSpace Tc Set the character spacing, Tc | |
| 1484 //, to charSpace, which is a number expressed in unscaled text space units. Char
acter spacing is used by the Tj, TJ, and ' operators. | |
| 1485 //Initial value: 0. | |
| 1486 PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo
oper) { | |
| 1487 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
| 1488 pdfContext->fGraphicsState.fCharSpace = charSpace; | |
| 1489 | |
| 1490 return kOK_PdfResult; | |
| 1491 } | |
| 1492 | |
| 1493 //wordSpace Tw Set the word spacing, T | |
| 1494 //w | |
| 1495 //, to wordSpace, which is a number expressed in unscaled | |
| 1496 //text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial | |
| 1497 //value: 0. | |
| 1498 PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo
oper) { | |
| 1499 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
| 1500 pdfContext->fGraphicsState.fWordSpace = wordSpace; | |
| 1501 | |
| 1502 return kOK_PdfResult; | |
| 1503 } | |
| 1504 | |
| 1505 //scale Tz Set the horizontal scaling, Th | |
| 1506 //, to (scale ˜ 100). scale is a number specifying the | |
| 1507 //percentage of the normal width. Initial value: 100 (normal width). | |
| 1508 static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1509 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
| 1510 | |
| 1511 return kNYI_PdfResult; | |
| 1512 } | |
| 1513 | |
| 1514 //render Tr Set the text rendering mode, T | |
| 1515 //mode, to render, which is an integer. Initial value: 0. | |
| 1516 static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1517 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfCo
ntext->fObjectStack.pop(); | |
| 1518 | |
| 1519 return kNYI_PdfResult; | |
| 1520 } | |
| 1521 //rise Ts Set the text rise, Trise, to rise, which is a number expressed in unsc
aled text space | |
| 1522 //units. Initial value: 0. | |
| 1523 static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1524 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfCont
ext->fObjectStack.pop(); | |
| 1525 | |
| 1526 return kNYI_PdfResult; | |
| 1527 } | |
| 1528 | |
| 1529 //wx wy d0 | |
| 1530 static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1531 pdfContext->fObjectStack.pop(); | |
| 1532 pdfContext->fObjectStack.pop(); | |
| 1533 | |
| 1534 return kNYI_PdfResult; | |
| 1535 } | |
| 1536 | |
| 1537 //wx wy llx lly urx ury d1 | |
| 1538 static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1539 pdfContext->fObjectStack.pop(); | |
| 1540 pdfContext->fObjectStack.pop(); | |
| 1541 pdfContext->fObjectStack.pop(); | |
| 1542 pdfContext->fObjectStack.pop(); | |
| 1543 pdfContext->fObjectStack.pop(); | |
| 1544 pdfContext->fObjectStack.pop(); | |
| 1545 | |
| 1546 return kNYI_PdfResult; | |
| 1547 } | |
| 1548 | |
| 1549 //name sh | |
| 1550 static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1551 pdfContext->fObjectStack.pop(); | |
| 1552 | |
| 1553 return kNYI_PdfResult; | |
| 1554 } | |
| 1555 | |
| 1556 //name Do | |
| 1557 static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1558 const char* name = pdfContext->fObjectStack.top()->nameValue(); pdfContex
t->fObjectStack.pop(); | |
| 1559 | |
| 1560 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(p
dfContext->fPdfDoc); | |
| 1561 | |
| 1562 if (xObject == NULL) { | |
| 1563 #ifdef PDF_TRACE | |
| 1564 printf("XObject is NULL!\n"); | |
| 1565 #endif | |
| 1566 return kIgnoreError_PdfResult; | |
| 1567 } | |
| 1568 | |
| 1569 SkPdfObject* value = xObject->get(name); | |
| 1570 value = pdfContext->fPdfDoc->resolveReference(value); | |
| 1571 | |
| 1572 #ifdef PDF_TRACE | |
| 1573 // value->ToString(str); | |
| 1574 // printf("Do object value: %s\n", str); | |
| 1575 #endif | |
| 1576 | |
| 1577 return doXObject(pdfContext, canvas, value); | |
| 1578 } | |
| 1579 | |
| 1580 //tag MP Designate a marked-content point. tag is a name object indicating the r
ole or | |
| 1581 //significance of the point. | |
| 1582 static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1583 pdfContext->fObjectStack.pop(); | |
| 1584 | |
| 1585 return kNYI_PdfResult; | |
| 1586 } | |
| 1587 | |
| 1588 //tag properties DP Designate a marked-content point with an associated property
list. tag is a | |
| 1589 //name object indicating the role or significance of the point; properties is | |
| 1590 //either an inline dictionary containing the property list or a name object | |
| 1591 //associated with it in the Properties subdictionary of the current resource | |
| 1592 //dictionary (see Section 9.5.1, “Property Lists”). | |
| 1593 static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
| 1594 pdfContext->fObjectStack.pop(); | |
| 1595 pdfContext->fObjectStack.pop(); | |
| 1596 | |
| 1597 return kNYI_PdfResult; | |
| 1598 } | |
| 1599 | |
| 1600 //tag BMC Begin a marked-content sequence terminated by a balancing EMC operator
. | |
| 1601 //tag is a name object indicating the role or significance of the sequence. | |
| 1602 static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
| 1603 pdfContext->fObjectStack.pop(); | |
| 1604 | |
| 1605 return kNYI_PdfResult; | |
| 1606 } | |
| 1607 | |
| 1608 //tag properties BDC Begin a marked-content sequence with an associated property
list, terminated | |
| 1609 //by a balancing EMCoperator. tag is a name object indicating the role or signif
icance of the sequence; propertiesis either an inline dictionary containing the | |
| 1610 //property list or a name object associated with it in the Properties subdiction
ary of the current resource dictionary (see Section 9.5.1, “Property Lists”). | |
| 1611 static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
| 1612 pdfContext->fObjectStack.pop(); | |
| 1613 pdfContext->fObjectStack.pop(); | |
| 1614 | |
| 1615 return kNYI_PdfResult; | |
| 1616 } | |
| 1617 | |
| 1618 //— EMC End a marked-content sequence begun by a BMC or BDC operator. | |
| 1619 static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
| 1620 return kNYI_PdfResult; | |
| 1621 } | |
| 1622 | |
| 1623 static void initPdfOperatorRenderes() { | |
| 1624 static bool gInitialized = false; | |
| 1625 if (gInitialized) { | |
| 1626 return; | |
| 1627 } | |
| 1628 | |
| 1629 gPdfOps["q"] = PdfOp_q; | |
| 1630 gPdfOps["Q"] = PdfOp_Q; | |
| 1631 gPdfOps["cm"] = PdfOp_cm; | |
| 1632 | |
| 1633 gPdfOps["TD"] = PdfOp_TD; | |
| 1634 gPdfOps["Td"] = PdfOp_Td; | |
| 1635 gPdfOps["Tm"] = PdfOp_Tm; | |
| 1636 gPdfOps["T*"] = PdfOp_T_star; | |
| 1637 | |
| 1638 gPdfOps["m"] = PdfOp_m; | |
| 1639 gPdfOps["l"] = PdfOp_l; | |
| 1640 gPdfOps["c"] = PdfOp_c; | |
| 1641 gPdfOps["v"] = PdfOp_v; | |
| 1642 gPdfOps["y"] = PdfOp_y; | |
| 1643 gPdfOps["h"] = PdfOp_h; | |
| 1644 gPdfOps["re"] = PdfOp_re; | |
| 1645 | |
| 1646 gPdfOps["S"] = PdfOp_S; | |
| 1647 gPdfOps["s"] = PdfOp_s; | |
| 1648 gPdfOps["f"] = PdfOp_f; | |
| 1649 gPdfOps["F"] = PdfOp_F; | |
| 1650 gPdfOps["f*"] = PdfOp_f_star; | |
| 1651 gPdfOps["B"] = PdfOp_B; | |
| 1652 gPdfOps["B*"] = PdfOp_B_star; | |
| 1653 gPdfOps["b"] = PdfOp_b; | |
| 1654 gPdfOps["b*"] = PdfOp_b_star; | |
| 1655 gPdfOps["n"] = PdfOp_n; | |
| 1656 | |
| 1657 gPdfOps["BT"] = PdfOp_BT; | |
| 1658 gPdfOps["ET"] = PdfOp_ET; | |
| 1659 | |
| 1660 gPdfOps["Tj"] = PdfOp_Tj; | |
| 1661 gPdfOps["'"] = PdfOp_quote; | |
| 1662 gPdfOps["\""] = PdfOp_doublequote; | |
| 1663 gPdfOps["TJ"] = PdfOp_TJ; | |
| 1664 | |
| 1665 gPdfOps["CS"] = PdfOp_CS; | |
| 1666 gPdfOps["cs"] = PdfOp_cs; | |
| 1667 gPdfOps["SC"] = PdfOp_SC; | |
| 1668 gPdfOps["SCN"] = PdfOp_SCN; | |
| 1669 gPdfOps["sc"] = PdfOp_sc; | |
| 1670 gPdfOps["scn"] = PdfOp_scn; | |
| 1671 gPdfOps["G"] = PdfOp_G; | |
| 1672 gPdfOps["g"] = PdfOp_g; | |
| 1673 gPdfOps["RG"] = PdfOp_RG; | |
| 1674 gPdfOps["rg"] = PdfOp_rg; | |
| 1675 gPdfOps["K"] = PdfOp_K; | |
| 1676 gPdfOps["k"] = PdfOp_k; | |
| 1677 | |
| 1678 gPdfOps["W"] = PdfOp_W; | |
| 1679 gPdfOps["W*"] = PdfOp_W_star; | |
| 1680 | |
| 1681 gPdfOps["BX"] = PdfOp_BX; | |
| 1682 gPdfOps["EX"] = PdfOp_EX; | |
| 1683 | |
| 1684 gPdfOps["BI"] = PdfOp_BI; | |
| 1685 gPdfOps["ID"] = PdfOp_ID; | |
| 1686 gPdfOps["EI"] = PdfOp_EI; | |
| 1687 | |
| 1688 gPdfOps["w"] = PdfOp_w; | |
| 1689 gPdfOps["J"] = PdfOp_J; | |
| 1690 gPdfOps["j"] = PdfOp_j; | |
| 1691 gPdfOps["M"] = PdfOp_M; | |
| 1692 gPdfOps["d"] = PdfOp_d; | |
| 1693 gPdfOps["ri"] = PdfOp_ri; | |
| 1694 gPdfOps["i"] = PdfOp_i; | |
| 1695 gPdfOps["gs"] = PdfOp_gs; | |
| 1696 | |
| 1697 gPdfOps["Tc"] = PdfOp_Tc; | |
| 1698 gPdfOps["Tw"] = PdfOp_Tw; | |
| 1699 gPdfOps["Tz"] = PdfOp_Tz; | |
| 1700 gPdfOps["TL"] = PdfOp_TL; | |
| 1701 gPdfOps["Tf"] = PdfOp_Tf; | |
| 1702 gPdfOps["Tr"] = PdfOp_Tr; | |
| 1703 gPdfOps["Ts"] = PdfOp_Ts; | |
| 1704 | |
| 1705 gPdfOps["d0"] = PdfOp_d0; | |
| 1706 gPdfOps["d1"] = PdfOp_d1; | |
| 1707 | |
| 1708 gPdfOps["sh"] = PdfOp_sh; | |
| 1709 | |
| 1710 gPdfOps["Do"] = PdfOp_Do; | |
| 1711 | |
| 1712 gPdfOps["MP"] = PdfOp_MP; | |
| 1713 gPdfOps["DP"] = PdfOp_DP; | |
| 1714 gPdfOps["BMC"] = PdfOp_BMC; | |
| 1715 gPdfOps["BDC"] = PdfOp_BDC; | |
| 1716 gPdfOps["EMC"] = PdfOp_EMC; | |
| 1717 | |
| 1718 gInitialized = true; | |
| 1719 } | |
| 1720 | |
| 1721 class InitPdfOps { | |
| 1722 public: | |
| 1723 InitPdfOps() { | |
| 1724 initPdfOperatorRenderes(); | |
| 1725 } | |
| 1726 }; | |
| 1727 | |
| 1728 InitPdfOps gInitPdfOps; | |
| 1729 | |
| 1730 void reportPdfRenderStats() { | |
| 1731 std::map<std::string, int>::iterator iter; | |
| 1732 | |
| 1733 for (int i = 0 ; i < kCount_PdfResult; i++) { | |
| 1734 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++it
er) { | |
| 1735 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_s
tr(), iter->second); | |
| 1736 } | |
| 1737 } | |
| 1738 } | |
| 1739 | |
| 1740 PdfResult PdfMainLooper::consumeToken(PdfToken& token) { | |
| 1741 char keyword[256]; | |
| 1742 | |
| 1743 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256) | |
| 1744 { | |
| 1745 strncpy(keyword, token.fKeyword, token.fKeywordLength); | |
| 1746 keyword[token.fKeywordLength] = '\0'; | |
| 1747 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...) | |
| 1748 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[keyword]; | |
| 1749 if (pdfOperatorRenderer) { | |
| 1750 // caller, main work is done by pdfOperatorRenderer(...) | |
| 1751 PdfTokenLooper* childLooper = NULL; | |
| 1752 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)
][keyword]++; | |
| 1753 | |
| 1754 if (childLooper) { | |
| 1755 childLooper->setUp(this); | |
| 1756 childLooper->loop(); | |
| 1757 delete childLooper; | |
| 1758 } | |
| 1759 } else { | |
| 1760 gRenderStats[kUnsupported_PdfResult][keyword]++; | |
| 1761 } | |
| 1762 } | |
| 1763 else if (token.fType == kObject_TokenType) | |
| 1764 { | |
| 1765 fPdfContext->fObjectStack.push( token.fObject ); | |
| 1766 } | |
| 1767 else { | |
| 1768 // TODO(edisonn): deine or use assert not reached | |
| 1769 return kIgnoreError_PdfResult; | |
| 1770 } | |
| 1771 return kOK_PdfResult; | |
| 1772 } | |
| 1773 | |
| 1774 void PdfMainLooper::loop() { | |
| 1775 PdfToken token; | |
| 1776 while (readToken(fTokenizer, &token)) { | |
| 1777 consumeToken(token); | |
| 1778 } | |
| 1779 } | |
| 1780 | |
| 1781 PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) { | |
| 1782 //pdfContext.fInlineImage.fKeyValuePairs[key] = value; | |
| 1783 return kNYI_PdfResult; | |
| 1784 } | |
| 1785 | |
| 1786 void PdfInlineImageLooper::loop() { | |
| 1787 PdfToken token; | |
| 1788 while (readToken(fTokenizer, &token)) { | |
| 1789 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") ==
0) { | |
| 1790 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper(); | |
| 1791 looper->setUp(this); | |
| 1792 looper->loop(); | |
| 1793 } else { | |
| 1794 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI"
) == 0) { | |
| 1795 done(); | |
| 1796 return; | |
| 1797 } | |
| 1798 | |
| 1799 consumeToken(token); | |
| 1800 } | |
| 1801 } | |
| 1802 // TODO(edisonn): report error/warning, EOF without EI. | |
| 1803 } | |
| 1804 | |
| 1805 PdfResult PdfInlineImageLooper::done() { | |
| 1806 | |
| 1807 // TODO(edisonn): long to short names | |
| 1808 // TODO(edisonn): set properties in a map | |
| 1809 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilitie
s to uncompress | |
| 1810 // the stream. | |
| 1811 | |
| 1812 SkBitmap bitmap; | |
| 1813 setup_bitmap(&bitmap, 50, 50, SK_ColorRED); | |
| 1814 | |
| 1815 // TODO(edisonn): matrix use. | |
| 1816 // Draw dummy red square, to show the prezence of the inline image. | |
| 1817 fCanvas->drawBitmap(bitmap, | |
| 1818 SkDoubleToScalar(0), | |
| 1819 SkDoubleToScalar(0), | |
| 1820 NULL); | |
| 1821 return kNYI_PdfResult; | |
| 1822 } | |
| 1823 | |
| 1824 PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) { | |
| 1825 return fParent->consumeToken(token); | |
| 1826 } | |
| 1827 | |
| 1828 void PdfCompatibilitySectionLooper::loop() { | |
| 1829 // TODO(edisonn): save stacks position, or create a new stack? | |
| 1830 // TODO(edisonn): what happens if we pop out more variables then when we sta
rted? | |
| 1831 // restore them? fail? We could create a new operands stack for every new BX
/EX section, | |
| 1832 // pop-ing too much will not affect outside the section. | |
| 1833 PdfToken token; | |
| 1834 while (readToken(fTokenizer, &token)) { | |
| 1835 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") ==
0) { | |
| 1836 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper(); | |
| 1837 looper->setUp(this); | |
| 1838 looper->loop(); | |
| 1839 delete looper; | |
| 1840 } else { | |
| 1841 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX"
) == 0) break; | |
| 1842 fParent->consumeToken(token); | |
| 1843 } | |
| 1844 } | |
| 1845 // TODO(edisonn): restore stack. | |
| 1846 } | |
| 1847 | |
| 1848 // TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf | |
| 1849 // TODO(edisonn): Add API for Forms viewing and editing | |
| 1850 // e.g. SkBitmap getPage(int page); | |
| 1851 // int formsCount(); | |
| 1852 // SkForm getForm(int formID); // SkForm(SkRect, .. other data) | |
| 1853 // TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsin
g all content, ... | |
| 1854 // if we load the first page, and we zoom to fit to screen horizontally, then lo
ad only those | |
| 1855 // resources needed, so the preview is fast. | |
| 1856 // TODO (edisonn): hide parser/tokenizer behind and interface and a query langua
ge, and resolve | |
| 1857 // references automatically. | |
| 1858 | |
| 1859 bool SkPdfViewer::load(const SkString inputFileName, SkPicture* out) { | |
| 1860 std::cout << "PDF Loaded: " << inputFileName.c_str() << std::endl; | |
| 1861 | |
| 1862 SkNativeParsedPDF* doc = new SkNativeParsedPDF(inputFileName.c_str()); | |
| 1863 if (!doc->pages()) | |
| 1864 { | |
| 1865 std::cout << "ERROR: Empty PDF Document" << inputFileName.c_str() << std
::endl; | |
| 1866 return false; | |
| 1867 } else { | |
| 1868 | |
| 1869 for (int pn = 0; pn < doc->pages(); ++pn) { | |
| 1870 // TODO(edisonn): implement inheritance properties as per PDF spec | |
| 1871 //SkRect rect = page->MediaBox(); | |
| 1872 SkRect rect = doc->MediaBox(pn); | |
| 1873 | |
| 1874 #ifdef PDF_TRACE | |
| 1875 printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rect.wi
dth()), SkScalarToDouble(rect.height())); | |
| 1876 #endif | |
| 1877 | |
| 1878 // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to
use? | |
| 1879 | |
| 1880 SkBitmap bitmap; | |
| 1881 #ifdef PDF_DEBUG_3X | |
| 1882 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (
int)SkScalarToDouble(rect.height())); | |
| 1883 #else | |
| 1884 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkSc
alarToDouble(rect.height())); | |
| 1885 #endif | |
| 1886 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap))); | |
| 1887 SkCanvas canvas(device); | |
| 1888 | |
| 1889 gDumpBitmap = &bitmap; | |
| 1890 | |
| 1891 gDumpCanvas = &canvas; | |
| 1892 doc->drawPage(pn, &canvas); | |
| 1893 | |
| 1894 SkString out; | |
| 1895 if (doc->pages() > 1) { | |
| 1896 out.appendf("%s-%i.png", inputFileName.c_str(), pn); | |
| 1897 } else { | |
| 1898 out = inputFileName; | |
| 1899 // .pdf -> .png | |
| 1900 out[out.size() - 2] = 'n'; | |
| 1901 out[out.size() - 1] = 'g'; | |
| 1902 } | |
| 1903 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG
_Type, 100); | |
| 1904 } | |
| 1905 return true; | |
| 1906 } | |
| 1907 | |
| 1908 return true; | |
| 1909 } | |
| OLD | NEW |