2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkPdfRenderer.h"
10 #include "SkBitmapDevice.h"
12 #include "SkColorPriv.h"
14 #include "SkForceLinking.h"
15 #include "SkGraphics.h"
16 #include "SkImageDecoder.h"
17 #include "SkImageEncoder.h"
19 #include "SkPicture.h"
20 #include "SkPdfFont.h"
21 #include "SkPdfGraphicsState.h"
22 #include "SkPdfHeaders_autogen.h"
23 #include "SkPdfMapper_autogen.h"
24 #include "SkPdfNativeTokenizer.h"
25 #include "SkPdfRenderer.h"
26 #include "SkPdfReporter.h"
27 #include "SkPdfTokenLooper.h"
28 #include "SkPdfUtils.h"
30 #include "SkTypeface.h"
34 // TODO(edisonn): #ifdef these ones, as they are used only for debugging.
35 extern "C" SkPdfContext* gPdfContext;
37 __SK_FORCE_IMAGE_DECODER_LINKING;
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
44 // Helper macros to load variables from stack, and automatically check their type.
45 #define EXPECT_OPERANDS(name,pdfContext,n) \
46 bool __failed = pdfContext->fObjectStack.count() < n; \
47 SkPdfREPORTCODE(const char* __operator_name = name); \
48 SkPdfREPORTCODE((void)__operator_name); \
49 SkPdfReportIf(pdfContext->fObjectStack.count() < n, \
50 kIgnoreError_SkPdfIssueSeverity, \
51 kStackOverflow_SkPdfIssue, \
52 "Not enought parameters.", NULL, pdfContext); \
53 SkDEBUGCODE(int __cnt = n);
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(); \
65 // TODO(edisonn): make all pop function to use name##_obj
66 #define POP_NUMBER(pdfContext,name) \
67 SkDEBUGCODE(__cnt--); \
68 SkASSERT(__cnt >= 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, \
79 SkPdfNativeObject::_kNumber_PdfObjectType, \
83 name = name##_obj->numberValue(); \
87 #define POP_INTEGER(pdfContext,name) \
88 SkDEBUGCODE(__cnt--); \
89 SkASSERT(__cnt >= 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, \
100 SkPdfNativeObject::kInteger_PdfObjectType, \
104 name = name##_obj->intValue(); \
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, \
119 SkPdfNativeObject::kInteger_PdfObjectType | \
120 SkPdfNativeObject::kReal_PdfObjectType, \
124 var = tmp->numberValue(); \
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, \
141 SkPdfNativeObject::kName_PdfObjectType, \
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, \
161 SkPdfNativeObject::kString_PdfObjectType | \
162 SkPdfNativeObject::kHexString_PdfObjectType, \
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, \
182 SkPdfNativeObject::kArray_PdfObjectType, \
186 name = (SkPdfArray*)tmp; \
190 #define CHECK_PARAMETERS() \
191 SkASSERT(__cnt == 0); \
192 if (__failed) return kIgnoreError_SkPdfResult;
195 NotOwnedString strings_DeviceRGB;
196 NotOwnedString strings_DeviceCMYK;
201 NotOwnedString::init(&strings_DeviceRGB, "DeviceRGB");
202 NotOwnedString::init(&strings_DeviceCMYK, "DeviceCMYK");
206 // TODO(edisonn): this will not work in chrome! Find another solution!
207 StringsInit gStringsInit;
209 // TODO(edisonn): Document SkPdfTokenLooper and subclasses.
210 class PdfInlineImageLooper : public SkPdfTokenLooper {
212 explicit PdfInlineImageLooper(SkPdfTokenLooper* parent)
213 : INHERITED(parent) {}
215 SkPdfResult consumeToken(PdfToken& token) override;
216 void loop() override;
219 typedef SkPdfTokenLooper INHERITED;
222 class PdfCompatibilitySectionLooper : public SkPdfTokenLooper {
224 explicit PdfCompatibilitySectionLooper(SkPdfTokenLooper* parent)
225 : INHERITED (parent) {}
227 SkPdfResult consumeToken(PdfToken& token) override;
228 void loop() override;
231 typedef SkPdfTokenLooper INHERITED;
235 static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
236 bitmap->allocN32Pixels(width, height);
237 bitmap->eraseColor(color);
240 // TODO(edisonn): synonyms? /DeviceRGB and /RGB mean the same thing. Context dependent.
241 static int GetColorSpaceComponents(NotOwnedString& colorSpace) {
242 if (colorSpace.equals("DeviceCMYK")) {
244 } else if (colorSpace.equals("DeviceGray") ||
245 colorSpace.equals("CalGray") ||
246 colorSpace.equals("Indexed")) {
248 } else if (colorSpace.equals("DeviceRGB") ||
249 colorSpace.equals("CalRGB") ||
250 colorSpace.equals("Lab")) {
257 SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
259 matrix.setAll(SkDoubleToScalar(array[0]),
260 SkDoubleToScalar(array[2]),
261 SkDoubleToScalar(array[4]),
262 SkDoubleToScalar(array[1]),
263 SkDoubleToScalar(array[3]),
264 SkDoubleToScalar(array[5]),
267 SkDoubleToScalar(1));
272 SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
275 // TODO(edisonn): security issue, ret if size() != 6
276 if (pdfArray == NULL) {
277 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue,
278 "null array passed to build matrix", NULL, NULL);
279 return SkMatrix::I();
282 if (pdfArray->size() != 6) {
283 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnexpectedArraySize_SkPdfIssue,
284 "null array passed to build matrix", pdfArray, NULL);
285 return SkMatrix::I();
288 for (int i = 0; i < 6; i++) {
289 const SkPdfNativeObject* elem = pdfArray->operator [](i);
290 if (elem == NULL || !elem->isNumber()) {
291 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, elem,
292 SkPdfNativeObject::_kNumber_PdfObjectType, NULL);
293 return SkMatrix::I();
295 array[i] = elem->numberValue();
298 return SkMatrixFromPdfMatrix(array);
301 // TODO(edisonn): debug code, used to analyze rendering when we find bugs.
302 extern "C" SkPdfNativeDoc* gDoc;
304 static SkPdfResult DrawText(SkPdfContext* pdfContext,
305 const SkPdfNativeObject* _str,
308 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
309 if (skfont == NULL) {
310 skfont = SkPdfFont::Default();
313 if (_str == NULL || !_str->isAnyString()) {
314 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
317 SkPdfNativeObject::_kAnyString_PdfObjectType,
319 return kIgnoreError_SkPdfResult;
321 const SkPdfString* str = (const SkPdfString*)_str;
323 SkUnencodedText binary(str);
325 SkDecodedText decoded;
327 if (skfont->encoding() == NULL) {
328 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingEncoding_SkPdfIssue,
329 "draw text", _str, pdfContext);
330 return kNYI_SkPdfResult;
333 skfont->encoding()->decodeText(binary, &decoded);
336 // TODO(edisonn): does size 0 mean anything special?
337 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
338 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
341 // TODO(edisonn): implement font scaler
342 // if (fCurFont && fCurFont->GetFontScale() != 0) {
343 // paint.setTextScaleX(fCurFont->GetFontScale() / 100.0);
346 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
348 skfont->drawText(decoded, &paint, pdfContext, canvas);
350 return kOK_SkPdfResult;
353 // TODO(edisonn): create header files with declarations!
354 SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper* parentLooper);
355 SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper* parentLooper);
356 SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper* parentLooper);
357 SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper* parentLooper);
359 // TODO(edisonn): perf!!!
360 static SkColorTable* getGrayColortable() {
361 static SkColorTable* grayColortable = NULL;
362 if (grayColortable == NULL) {
363 SkPMColor* colors = new SkPMColor[256];
364 for (int i = 0 ; i < 256; i++) {
365 colors[i] = SkPreMultiplyARGB(255, i, i, i);
367 grayColortable = new SkColorTable(colors, 256);
369 return grayColortable;
372 static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream,
373 size_t uncompressedStreamLength,
374 int width, int height, int bytesPerLine,
375 int bpc, const SkString& colorSpace,
376 bool transparencyMask) {
377 SkBitmap* bitmap = new SkBitmap();
379 //int components = GetColorSpaceComponents(colorSpace);
380 //#define MAX_COMPONENTS 10
382 // TODO(edisonn): assume start of lines are aligned at 32 bits?
383 // Is there a faster way to load the uncompressed stream into a bitmap?
385 // minimal support for now
386 if ((colorSpace.equals("DeviceRGB") || colorSpace.equals("RGB")) && bpc == 8) {
387 uint32_t* uncompressedStreamArgb = (SkColor*)malloc(width * height * sizeof(uint32_t));
389 for (int h = 0 ; h < height; h++) {
390 long i = width * (h);
391 for (int w = 0 ; w < width; w++) {
392 uncompressedStreamArgb[i] = SkPackARGB32(0xFF,
393 uncompressedStream[3 * w],
394 uncompressedStream[3 * w + 1],
395 uncompressedStream[3 * w + 2]);
398 uncompressedStream += bytesPerLine;
401 const SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
402 bitmap->installPixels(info, uncompressedStreamArgb, info.minRowBytes());
404 else if ((colorSpace.equals("DeviceGray") || colorSpace.equals("Gray")) && bpc == 8) {
405 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
407 for (int h = 0 ; h < height; h++) {
408 long i = width * (h);
409 for (int w = 0 ; w < width; w++) {
410 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedStream[w] :
411 uncompressedStream[w];
414 uncompressedStream += bytesPerLine;
417 const SkColorType ct = transparencyMask ? kAlpha_8_SkColorType : kIndex_8_SkColorType;
418 const SkImageInfo info = SkImageInfo::Make(width, height, ct, kPremul_SkAlphaType);
419 bitmap->installPixels(info, uncompressedStreamA8, info.minRowBytes(),
420 transparencyMask ? NULL : getGrayColortable(), NULL, NULL);
423 // TODO(edisonn): pass color space and context here?
424 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI", NULL, NULL);
427 // TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
430 // This functions returns the image, it does not look at the smask.
431 static SkBitmap* getImageFromObjectCore(SkPdfContext* pdfContext,
432 SkPdfImageDictionary* image, bool transparencyMask) {
433 if (image == NULL || !image->hasStream()) {
434 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", image,
435 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
439 int bpc = (int)image->BitsPerComponent(pdfContext->fPdfDoc);
440 int width = (int)image->Width(pdfContext->fPdfDoc);
441 int height = (int)image->Height(pdfContext->fPdfDoc);
442 SkString colorSpace("DeviceRGB");
444 bool indexed = false;
445 SkPMColor colors[256];
448 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
449 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
450 } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) {
451 SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc);
452 if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") &&
453 (array->objAtAIndex(1)->isName("DeviceRGB") ||
454 array->objAtAIndex(1)->isName("RGB")) &&
455 array->objAtAIndex(2)->isInteger() &&
456 array->objAtAIndex(3)->isHexString()
458 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI",
461 cnt = (int)array->objAtAIndex(2)->intValue() + 1;
463 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
464 "Color space feature NYI, cnt > 256", image, pdfContext);
467 NotOwnedString data = array->objAtAIndex(3)->strRef();
468 if (data.fBytes != (unsigned int)cnt * 3) {
469 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
470 "Image color table mismatch color space specs", array, pdfContext);
473 for (int i = 0 ; i < cnt; i++) {
474 colors[i] = SkPreMultiplyARGB(0xff,
476 data.fBuffer[3 * i + 1],
477 data.fBuffer[3 * i + 2]);
482 // TODO(edisonn): implement image masks.
483 /* bool imageMask = image->imageMask();
485 if (bpc != 0 && bpc != 1) {
486 // TODO(edisonn): report warning to be used in testing.
493 const unsigned char* uncompressedStream = NULL;
494 size_t uncompressedStreamLength = 0;
496 SkPdfStream* stream = (SkPdfStream*)image;
498 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
499 uncompressedStream == NULL || uncompressedStreamLength == 0) {
500 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", stream,
501 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
505 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
507 if (streamDict->has_Filter() &&
508 ((streamDict->isFilterAName(NULL) &&
509 streamDict->getFilterAsName(NULL).equals("DCTDecode")) ||
510 (streamDict->isFilterAArray(NULL) &&
511 streamDict->getFilterAsArray(NULL)->size() > 0 &&
512 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
513 streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2()
514 .equals("DCTDecode")))) {
515 SkBitmap* bitmap = new SkBitmap();
516 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap);
520 // TODO(edisonn): assumes RGB for now, since it is the only one implemented
522 SkBitmap* bitmap = new SkBitmap();
523 const SkImageInfo info = SkImageInfo::Make(width, height, kIndex_8_SkColorType,
524 kPremul_SkAlphaType);
525 SkAutoTUnref<SkColorTable> colorTable(new SkColorTable(colors, cnt));
526 bitmap->installPixels(info, (void*)uncompressedStream, info.minRowBytes(), colorTable,
531 int bytesPerLine = (int)(uncompressedStreamLength / height);
533 if (uncompressedStreamLength % height != 0) {
534 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
538 SkBitmap* bitmap = transferImageStreamToBitmap(
539 (unsigned char*)uncompressedStream, uncompressedStreamLength,
540 (int)width, (int)height, bytesPerLine,
541 (int)bpc, colorSpace,
547 static SkBitmap* getImageFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* image,
548 bool transparencyMask) {
549 if (!transparencyMask) {
550 if (!image->hasData(SkPdfNativeObject::kBitmap_Data)) {
551 SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask);
552 image->setData(bitmap, SkPdfNativeObject::kBitmap_Data);
554 return (SkBitmap*) image->data(SkPdfNativeObject::kBitmap_Data);
556 return getImageFromObjectCore(pdfContext, image, transparencyMask);
560 static SkBitmap* getSmaskFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* obj) {
561 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
564 return getImageFromObject(pdfContext, sMask, true);
567 // TODO(edisonn): implement GS SMask. Default to empty right now.
568 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
569 "implement GS SMask. Default to empty right now.", obj, pdfContext);
571 return pdfContext->fGraphicsState.fSMask;
574 static SkPdfResult doXObject_Image(SkPdfContext* pdfContext, SkCanvas* canvas,
575 SkPdfImageDictionary* skpdfimage) {
576 if (skpdfimage == NULL) {
577 return kIgnoreError_SkPdfResult;
580 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
581 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
584 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
586 SkScalar z = SkIntToScalar(0);
587 SkScalar one = SkIntToScalar(1);
589 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z),
590 SkPoint::Make(one, one), SkPoint::Make(z, one)};
591 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one),
592 SkPoint::Make(one, z), SkPoint::Make(z, z)};
594 SkAssertResult(flip.setPolyToPoly(from, to, 4));
595 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
596 solveImageFlip.preConcat(flip);
597 canvas->setMatrix(solveImageFlip);
600 SkPoint final[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z),
601 SkPoint::Make(one, one), SkPoint::Make(z, one)};
602 solveImageFlip.mapPoints(final, 4);
603 printf("IMAGE rect = ");
604 for (int i = 0; i < 4; i++) {
605 printf("(%f %f) ", SkScalarToDouble(final[i].x()), SkScalarToDouble(final[i].y()));
610 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0),
611 SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
613 // TODO(edisonn): soft mask type? alpha/luminosity.
614 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
615 "implement soft mask type", skpdfimage, pdfContext);
618 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
620 if (!sMask || sMask->empty()) {
621 canvas->drawBitmapRect(*image, dst, &paint);
623 canvas->saveLayer(&dst, &paint);
624 canvas->drawBitmapRect(*image, dst, NULL);
626 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode);
627 canvas->drawBitmapRect(*sMask, dst, &xfer);
633 return kPartial_SkPdfResult;
636 //TODO(edisonn): options for implementing isolation and knockout
637 // 1) emulate them (current solution)
639 // CON: will need to use readPixels, which means serious perf issues
640 // 2) Compile a plan for an array of matrixes, compose the result at the end
641 // PRO: might be faster then 1, no need to readPixels
642 // CON: multiple drawings (but on smaller areas), pay a price at loading pdf to
643 // compute a pdf draw plan
644 // on average, a load with empty draw is 100ms on all the skps we have, for complete sites
645 // 3) support them natively in SkCanvas
647 // CON: we would still need to use a form of readPixels anyway, so perf might be the same as 1)
648 // 4) compile a plan using pathops, and render once without any fancy rules with backdrop
650 // CON: pathops must be bug free first + time to compute new paths
651 // pay a price at loading pdf to compute a pdf draw plan
652 // on average, a load with empty draw is 100ms on all the skps we have, for complete sites
653 // 5) for knockout, render the objects in reverse order, and add every object to the clip, and any
654 // new draw will be cliped
656 static void doGroup_before(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox,
657 SkPdfTransparencyGroupDictionary* tgroup, bool page) {
658 SkRect bboxOrig = bbox;
660 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
661 // bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
663 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
664 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
667 // TODO(edisonn): non isolation should probably be implemented in skia
668 //static void doGroup_after(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox,
669 // SkPdfTransparencyGroupDictionary* tgroup) {
671 // canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
674 static SkPdfResult doXObject_Form(SkPdfContext* pdfContext, SkCanvas* canvas,
675 SkPdfType1FormDictionary* skobj) {
676 if (!skobj || !skobj->hasStream()) {
677 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj,
678 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
679 return kIgnoreError_SkPdfResult;
682 if (!skobj->has_BBox()) {
683 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox",
685 return kIgnoreError_SkPdfResult;
688 PdfOp_q(pdfContext, canvas, NULL);
691 if (skobj->Resources(pdfContext->fPdfDoc)) {
692 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
695 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
697 if (skobj->has_Matrix()) {
698 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
699 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
700 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
701 pdfContext->fGraphicsState.fMatrixTm = matrix;
702 pdfContext->fGraphicsState.fMatrixTlm = matrix;
703 // TODO(edisonn): text matrixes mosltly NYI
706 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
707 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM;
709 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
711 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
712 // TODO(edisonn): constants (AA) from settings.
713 canvas->clipRect(bbox, SkRegion::kIntersect_Op, false);
716 if (skobj->has_Group()) {
717 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
718 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
721 SkPdfStream* stream = (SkPdfStream*)skobj;
723 pdfContext->parseStream(stream, canvas);
725 if (skobj->has_Group()) {
729 PdfOp_Q(pdfContext, canvas, NULL);
730 return kPartial_SkPdfResult;
733 static SkPdfResult doXObject_Pattern(SkPdfContext* pdfContext, SkCanvas* canvas,
734 SkPdfType1PatternDictionary* skobj) {
735 if (!skobj || !skobj->hasStream()) {
736 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream",
737 skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
738 return kIgnoreError_SkPdfResult;
741 if (!skobj->has_BBox()) {
742 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox",
744 return kIgnoreError_SkPdfResult;
747 PdfOp_q(pdfContext, canvas, NULL);
750 if (skobj->Resources(pdfContext->fPdfDoc)) {
751 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
754 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix");
756 if (skobj->has_Matrix()) {
757 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat(
758 skobj->Matrix(pdfContext->fPdfDoc));
761 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix");
763 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix);
764 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix;
766 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
767 // TODO(edisonn): constants (AA) from settings.
768 canvas->clipRect(bbox, SkRegion::kIntersect_Op, false);
770 SkPdfStream* stream = (SkPdfStream*)skobj;
772 pdfContext->parseStream(stream, canvas);
774 PdfOp_Q(pdfContext, canvas, NULL);
775 return kPartial_SkPdfResult;
778 // TODO(edisonn): PS NYI
779 //static SkPdfResult doXObject_PS(SkPdfContext* pdfContext, SkCanvas* canvas,
780 // const SkPdfNativeObject* obj) {
781 // return kNYI_SkPdfResult;
784 SkPdfResult doType3Char(SkPdfContext* pdfContext, SkCanvas* canvas, const SkPdfNativeObject* skobj,
785 SkRect bBox, SkMatrix matrix, double textSize) {
786 if (!skobj || !skobj->hasStream()) {
787 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj,
788 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
789 return kIgnoreError_SkPdfResult;
792 PdfOp_q(pdfContext, canvas, NULL);
794 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
795 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize),
796 SkDoubleToScalar(textSize));
797 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrixTm;
799 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
800 pdfContext->fGraphicsState.fCTM.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
802 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
804 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
807 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
809 SkTraceRect(rm, "bbox mapped");
811 // TODO(edisonn): constants (AA) from settings.
812 canvas->clipRect(bBox, SkRegion::kIntersect_Op, false);
814 SkPdfStream* stream = (SkPdfStream*)skobj;
816 pdfContext->parseStream(stream, canvas);
818 PdfOp_Q(pdfContext, canvas, NULL);
820 return kPartial_SkPdfResult;
823 // The PDF could be corrupted so a dict refers recursively to the same dict, if this happens
824 // we end up with a stack overflow and crash.
825 class CheckRecursiveRendering {
826 SkPdfNativeObject* fObj;
828 CheckRecursiveRendering(SkPdfNativeObject* obj) : fObj(obj) {
829 SkASSERT(!obj->inRendering());
830 obj->startRendering();
833 ~CheckRecursiveRendering() {
834 SkASSERT(fObj->inRendering());
835 fObj->doneRendering();
838 static bool IsInRendering(const SkPdfNativeObject* obj) {
839 return obj->inRendering();
843 static SkPdfResult doXObject(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfNativeObject* obj) {
844 if (CheckRecursiveRendering::IsInRendering(obj)) {
845 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kRecursiveReferencing_SkPdfIssue,
846 "Recursive reverencing is invalid in draw objects", obj, pdfContext);
847 return kIgnoreError_SkPdfResult;
850 CheckRecursiveRendering checkRecursion(obj);
852 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
854 case kImageDictionary_SkPdfNativeObjectType:
855 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)obj);
856 case kType1FormDictionary_SkPdfNativeObjectType:
857 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj);
858 //case kObjectDictionaryXObjectPS_SkPdfNativeObjectType:
859 //return doXObject_PS(skxobj.asPS());
861 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) !=
862 kNone_SkPdfNativeObjectType) {
863 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
864 return doXObject_Pattern(pdfContext, canvas, pattern);
866 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "doXObject",
870 return kIgnoreError_SkPdfResult;
873 static SkPdfResult doPage(SkPdfContext* pdfContext, SkCanvas* canvas,
874 SkPdfPageObjectDictionary* skobj) {
875 if (!skobj || !skobj->isContentsAStream(pdfContext->fPdfDoc)) {
876 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", skobj,
877 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
878 return kNYI_SkPdfResult;
881 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
884 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream",
885 skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
886 return kIgnoreError_SkPdfResult;
889 // FIXME (scroggo): renderPage also sets fResources. Are these redundant?
890 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
892 if (!pdfContext->fGraphicsState.fResources) {
893 // It might be null because we have not implemented yet inheritance.
894 return kIgnoreError_SkPdfResult;
897 if (CheckRecursiveRendering::IsInRendering(skobj)) {
898 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kRecursiveReferencing_SkPdfIssue,
899 "Recursive reverencing is invalid in draw objects", skobj, pdfContext);
900 return kIgnoreError_SkPdfResult;
902 CheckRecursiveRendering checkRecursion(skobj);
905 // FIXME (scroggo): Is this save necessary? May be needed for rendering a nested PDF.
906 PdfOp_q(pdfContext, canvas, NULL);
908 // TODO(edisonn): MediaBox can be inherited!!!!
909 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MediaBox inheritance NYI",
911 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc);
912 if (skobj->has_Group()) {
913 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
914 doGroup_before(pdfContext, canvas, bbox, tgroup, true);
919 pdfContext->parseStream(stream, canvas);
922 PdfOp_Q(pdfContext, canvas, NULL);
923 return kPartial_SkPdfResult;
926 SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
927 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
929 pdfContext->fObjectStack.nest();
930 return kOK_SkPdfResult;
933 SkPdfResult PdfOp_Q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
934 if (pdfContext->fStateStack.count() > 0) {
935 pdfContext->fGraphicsState = pdfContext->fStateStack.top();
936 pdfContext->fStateStack.pop();
939 if (pdfContext->fObjectStack.nestingLevel() == 0) {
940 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackNestingOverflow_SkPdfIssue,
941 "stack nesting overflow (q/Q)", NULL, pdfContext);
942 return kIgnoreError_SkPdfResult;
944 pdfContext->fObjectStack.unnest();
947 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackOverflow_SkPdfIssue,
948 "stack overflow (q/Q)", NULL, pdfContext);
949 return kIgnoreError_SkPdfResult;
952 return kOK_SkPdfResult;
955 static SkPdfResult PdfOp_cm(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
956 EXPECT_OPERANDS("cm", pdfContext, 6);
957 POP_NUMBER(pdfContext, f);
958 POP_NUMBER(pdfContext, e);
959 POP_NUMBER(pdfContext, d);
960 POP_NUMBER(pdfContext, c);
961 POP_NUMBER(pdfContext, b);
962 POP_NUMBER(pdfContext, a);
964 double array[6] = {a, b, c, d, e, f};
977 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
979 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
983 for (int i = 0 ; i < 6 ; i++) {
984 printf("%f ", array[i]);
987 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
990 return kOK_SkPdfResult;
993 //leading TL Set the text leading, Tl
994 //, to leading, which is a number expressed in unscaled text
995 //space units. Text leading is used only by the T*, ', and " operators. Initial value: 0.
996 static SkPdfResult PdfOp_TL(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
997 EXPECT_OPERANDS("TL", pdfContext, 1);
998 POP_NUMBER(pdfContext, ty);
1001 pdfContext->fGraphicsState.fTextLeading = ty;
1003 return kOK_SkPdfResult;
1006 static SkPdfResult PdfOp_Td(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1007 EXPECT_OPERANDS("Td", pdfContext, 2);
1008 POP_NUMBER(pdfContext, ty);
1009 POP_NUMBER(pdfContext, tx);
1012 double array[6] = {1, 0, 0, 1, tx, -ty};
1013 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1015 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1016 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1018 return kPartial_SkPdfResult;
1021 static SkPdfResult PdfOp_TD(SkPdfContext* pdfContext, SkCanvas* canvas,
1022 SkPdfTokenLooper* parentLooper) {
1023 EXPECT_OPERANDS("TD", pdfContext, 2)
1024 POP_NUMBER(pdfContext, ty);
1025 POP_NUMBER(pdfContext, tx);
1028 // TODO(edisonn): Create factory methods or constructors so native is hidden
1029 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
1030 pdfContext->fObjectStack.push(_ty);
1032 PdfOp_TL(pdfContext, canvas, parentLooper);
1034 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
1035 pdfContext->fObjectStack.push(vtx);
1037 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
1038 pdfContext->fObjectStack.push(vty);
1040 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, parentLooper);
1045 static SkPdfResult PdfOp_Tm(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1046 EXPECT_OPERANDS("Tm", pdfContext, 6);
1047 POP_NUMBER(pdfContext, f);
1048 POP_NUMBER(pdfContext, e);
1049 POP_NUMBER(pdfContext, d);
1050 POP_NUMBER(pdfContext, c);
1051 POP_NUMBER(pdfContext, b);
1052 POP_NUMBER(pdfContext, a);
1063 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1064 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
1065 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
1067 // TODO(edisonn): NYI - Text positioning.
1068 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
1069 "Text positioning not implemented for 2+ chars", NULL, pdfContext);
1071 pdfContext->fGraphicsState.fMatrixTm = matrix;
1072 pdfContext->fGraphicsState.fMatrixTlm = matrix;;
1074 return kPartial_SkPdfResult;
1077 //— T* Move to the start of the next line. This operator has the same effect as the code
1079 //where Tl is the current leading parameter in the text state
1080 static SkPdfResult PdfOp_T_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1081 SkPdfTokenLooper* parentLooper) {
1082 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0);
1083 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.fTextLeading);
1085 pdfContext->fObjectStack.push(zero);
1086 pdfContext->fObjectStack.push(tl);
1088 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, parentLooper);
1093 static SkPdfResult PdfOp_m(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1094 if (pdfContext->fGraphicsState.fPathClosed) {
1095 pdfContext->fGraphicsState.fPath.reset();
1096 pdfContext->fGraphicsState.fPathClosed = false;
1099 EXPECT_OPERANDS("m", pdfContext, 2);
1100 POP_NUMBER(pdfContext, y);
1101 POP_NUMBER(pdfContext, x);
1104 pdfContext->fGraphicsState.fCurPosY = y;
1105 pdfContext->fGraphicsState.fCurPosX = x;
1107 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1108 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1110 return kOK_SkPdfResult;
1113 static SkPdfResult PdfOp_l(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1114 if (pdfContext->fGraphicsState.fPathClosed) {
1115 pdfContext->fGraphicsState.fPath.reset();
1116 pdfContext->fGraphicsState.fPathClosed = false;
1119 EXPECT_OPERANDS("l", pdfContext, 2);
1120 POP_NUMBER(pdfContext, y);
1121 POP_NUMBER(pdfContext, x);
1124 pdfContext->fGraphicsState.fCurPosY = y;
1125 pdfContext->fGraphicsState.fCurPosX = x;
1127 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1128 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1130 return kOK_SkPdfResult;
1133 static SkPdfResult PdfOp_c(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1134 if (pdfContext->fGraphicsState.fPathClosed) {
1135 pdfContext->fGraphicsState.fPath.reset();
1136 pdfContext->fGraphicsState.fPathClosed = false;
1139 EXPECT_OPERANDS("c", pdfContext, 6);
1140 POP_NUMBER(pdfContext, y3);
1141 POP_NUMBER(pdfContext, x3);
1142 POP_NUMBER(pdfContext, y2);
1143 POP_NUMBER(pdfContext, x2);
1144 POP_NUMBER(pdfContext, y1);
1145 POP_NUMBER(pdfContext, x1);
1148 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1149 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1150 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1152 pdfContext->fGraphicsState.fCurPosX = x3;
1153 pdfContext->fGraphicsState.fCurPosY = y3;
1155 return kOK_SkPdfResult;
1158 static SkPdfResult PdfOp_v(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1159 if (pdfContext->fGraphicsState.fPathClosed) {
1160 pdfContext->fGraphicsState.fPath.reset();
1161 pdfContext->fGraphicsState.fPathClosed = false;
1164 EXPECT_OPERANDS("v", pdfContext, 4);
1165 POP_NUMBER(pdfContext, y3);
1166 POP_NUMBER(pdfContext, x3);
1167 POP_NUMBER(pdfContext, y2);
1168 POP_NUMBER(pdfContext, x2);
1171 double y1 = pdfContext->fGraphicsState.fCurPosY;
1172 double x1 = pdfContext->fGraphicsState.fCurPosX;
1174 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1175 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1176 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1178 pdfContext->fGraphicsState.fCurPosX = x3;
1179 pdfContext->fGraphicsState.fCurPosY = y3;
1181 return kOK_SkPdfResult;
1184 static SkPdfResult PdfOp_y(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1185 if (pdfContext->fGraphicsState.fPathClosed) {
1186 pdfContext->fGraphicsState.fPath.reset();
1187 pdfContext->fGraphicsState.fPathClosed = false;
1190 EXPECT_OPERANDS("y", pdfContext, 4);
1191 POP_NUMBER(pdfContext, y3);
1192 POP_NUMBER(pdfContext, x3);
1193 POP_NUMBER(pdfContext, y1);
1194 POP_NUMBER(pdfContext, x1);
1197 double y2 = pdfContext->fGraphicsState.fCurPosY;
1198 double x2 = pdfContext->fGraphicsState.fCurPosX;
1200 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1201 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1202 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1204 pdfContext->fGraphicsState.fCurPosX = x3;
1205 pdfContext->fGraphicsState.fCurPosY = y3;
1207 return kOK_SkPdfResult;
1210 static SkPdfResult PdfOp_re(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1211 if (pdfContext->fGraphicsState.fPathClosed) {
1212 pdfContext->fGraphicsState.fPath.reset();
1213 pdfContext->fGraphicsState.fPathClosed = false;
1216 EXPECT_OPERANDS("re", pdfContext, 4);
1217 POP_NUMBER(pdfContext, height);
1218 POP_NUMBER(pdfContext, width);
1219 POP_NUMBER(pdfContext, y);
1220 POP_NUMBER(pdfContext, x);
1223 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x),
1224 SkDoubleToScalar(y),
1225 SkDoubleToScalar(x + width),
1226 SkDoubleToScalar(y + height));
1228 pdfContext->fGraphicsState.fCurPosX = x;
1229 pdfContext->fGraphicsState.fCurPosY = y + height;
1231 return kOK_SkPdfResult;
1234 static SkPdfResult PdfOp_h(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1235 pdfContext->fGraphicsState.fPath.close();
1236 return kOK_SkPdfResult;
1239 static SkPdfResult PdfOp_fillAndStroke(SkPdfContext* pdfContext, SkCanvas* canvas,
1240 bool fill, bool stroke, bool close, bool evenOdd) {
1241 SkPath path = pdfContext->fGraphicsState.fPath;
1247 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
1252 if (fill && !stroke && path.isLine(line)) {
1253 paint.setStyle(SkPaint::kStroke_Style);
1255 // TODO(edisonn): implement this with patterns
1256 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1257 paint.setStrokeWidth(SkDoubleToScalar(0));
1259 canvas->drawPath(path, paint);
1262 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer,
1263 "Pattern", strlen("Pattern")) == 0 &&
1264 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1266 // TODO(edisonn): we can use a shader here, like imageshader to draw fast.
1268 PdfOp_q(pdfContext, canvas, NULL);
1271 path.setFillType(SkPath::kEvenOdd_FillType);
1273 canvas->clipPath(path);
1275 if (pdfContext->fPdfDoc
1277 ->mapType1PatternDictionary(pdfContext->fGraphicsState
1280 != kNone_SkPdfNativeObjectType) {
1281 SkPdfType1PatternDictionary* pattern
1282 = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState
1286 // TODO(edisonn): make PaintType constants
1287 if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
1288 // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
1289 // it will change the result iterating in reverse
1290 // remove then the following bounds.sort();
1291 int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
1292 int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
1294 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
1295 "paterns x/y step is forced to positive number",
1296 pattern, pdfContext);
1298 SkRect bounds = path.getBounds();
1307 while (y < bounds.bottom()) {
1311 while (x < bounds.right()) {
1312 doXObject(pdfContext, canvas, pattern);
1314 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1315 SkIntToScalar(xStep), SkIntToScalar(0));
1317 x += SkIntToScalar(xStep);
1319 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1320 SkIntToScalar(-totalx), SkIntToScalar(0));
1322 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1323 SkIntToScalar(0), SkIntToScalar(-yStep));
1325 y += SkIntToScalar(yStep);
1327 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1328 SkIntToScalar(0), SkIntToScalar(totaly));
1332 PdfOp_Q(pdfContext, canvas, NULL);
1334 paint.setStyle(SkPaint::kFill_Style);
1336 path.setFillType(SkPath::kEvenOdd_FillType);
1339 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1341 canvas->drawPath(path, paint);
1346 if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer,
1347 "Pattern", strlen("Pattern")) == 0) {
1348 // TODO(edisonn): implement Pattern for strokes
1349 paint.setStyle(SkPaint::kStroke_Style);
1351 paint.setColor(SK_ColorGREEN);
1353 // reset it, just in case it messes up the stroke
1354 path.setFillType(SkPath::kWinding_FillType);
1355 canvas->drawPath(path, paint);
1357 paint.setStyle(SkPaint::kStroke_Style);
1359 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1361 // reset it, just in case it messes up the stroke
1362 path.setFillType(SkPath::kWinding_FillType);
1363 canvas->drawPath(path, paint);
1368 pdfContext->fGraphicsState.fPath.reset();
1369 // TODO(edisonn): implement scale/zoom
1371 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1372 #ifndef PDF_DEBUG_NO_CLIPING
1373 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1377 //pdfContext->fGraphicsState.fClipPath.reset();
1378 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1380 return kOK_SkPdfResult;
1384 static SkPdfResult PdfOp_S(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1385 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1388 static SkPdfResult PdfOp_s(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1389 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1392 static SkPdfResult PdfOp_F(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1393 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1396 static SkPdfResult PdfOp_f(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1397 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1400 static SkPdfResult PdfOp_f_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1401 SkPdfTokenLooper*) {
1402 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1405 static SkPdfResult PdfOp_B(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1406 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1409 static SkPdfResult PdfOp_B_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1410 SkPdfTokenLooper*) {
1411 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1414 static SkPdfResult PdfOp_b(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1415 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1418 static SkPdfResult PdfOp_b_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1419 SkPdfTokenLooper*) {
1420 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1423 static SkPdfResult PdfOp_n(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1424 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
1425 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1426 #ifndef PDF_DEBUG_NO_CLIPING
1427 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1431 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1433 pdfContext->fGraphicsState.fPathClosed = true;
1435 return kOK_SkPdfResult;
1438 static SkPdfResult PdfOp_BT(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1439 pdfContext->fGraphicsState.fTextBlock = true;
1440 SkMatrix matrix = pdfContext->fGraphicsState.fCTM;
1441 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
1442 pdfContext->fGraphicsState.fMatrixTm = matrix;
1443 pdfContext->fGraphicsState.fMatrixTlm = matrix;
1445 return kPartial_SkPdfResult;
1448 static SkPdfResult PdfOp_ET(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1449 if (!pdfContext->fGraphicsState.fTextBlock) {
1450 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "ET without BT", NULL,
1453 return kIgnoreError_SkPdfResult;
1456 pdfContext->fGraphicsState.fTextBlock = false;
1458 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1459 return kOK_SkPdfResult;
1462 static SkPdfResult skpdfGraphicsStateApplyFontCore(SkPdfContext* pdfContext,
1463 const SkPdfNativeObject* fontName, double fontSize) {
1465 printf("font name: %s\n", fontName->nameValue2().c_str());
1468 if (!pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) {
1469 // TODO(edisonn): try to recover and draw it any way?
1470 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingFont_SkPdfIssue,
1471 "No font", fontName, pdfContext);
1472 return kIgnoreError_SkPdfResult;
1475 SkPdfNativeObject* objFont
1476 = pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)->get(fontName);
1477 objFont = pdfContext->fPdfDoc->resolveReference(objFont);
1478 if (kNone_SkPdfNativeObjectType == pdfContext->fPdfDoc->mapper()->mapFontDictionary(objFont)) {
1479 // TODO(edisonn): try to recover and draw it any way?
1480 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kInvalidFont_SkPdfIssue,
1481 "Invalid font", objFont, pdfContext);
1482 return kIgnoreError_SkPdfResult;
1485 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1487 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1490 pdfContext->fGraphicsState.fSkFont = skfont;
1492 pdfContext->fGraphicsState.fCurFontSize = fontSize;
1493 return kOK_SkPdfResult;
1496 //font size Tf Set the text font, Tf
1497 //, to font and the text font size, Tfs, to size. font is the name of a
1498 //font resource in the Fontsubdictionary of the current resource dictionary; size is
1499 //a number representing a scale factor. There is no initial value for either font or
1500 //size; they must be specified explicitly using Tf before any text is shown.
1501 static SkPdfResult PdfOp_Tf(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1502 EXPECT_OPERANDS("Tf", pdfContext, 2);
1503 POP_NUMBER(pdfContext, fontSize);
1504 POP_NAME(pdfContext, fontName);
1507 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1510 static SkPdfResult PdfOp_Tj(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1511 EXPECT_OPERANDS("Tj", pdfContext, 1);
1512 POP_STRING(pdfContext, str);
1515 if (!pdfContext->fGraphicsState.fTextBlock) {
1516 // TODO(edisonn): try to recover and draw it any way?
1517 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "Tj without BT", NULL,
1519 return kIgnoreError_SkPdfResult;
1522 SkPdfResult ret = DrawText(pdfContext, str, canvas);
1527 static SkPdfResult PdfOp_quote(SkPdfContext* pdfContext, SkCanvas* canvas,
1528 SkPdfTokenLooper* parentLooper) {
1529 if (!pdfContext->fGraphicsState.fTextBlock) {
1530 // TODO(edisonn): try to recover and draw it any way?
1531 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue,
1532 "' without BT", NULL, pdfContext);
1533 return kIgnoreError_SkPdfResult;
1536 PdfOp_T_star(pdfContext, canvas, parentLooper);
1537 // Do not pop, and push, just transfer the param to Tj
1538 return PdfOp_Tj(pdfContext, canvas, parentLooper);
1541 static SkPdfResult PdfOp_doublequote(SkPdfContext* pdfContext, SkCanvas* canvas,
1542 SkPdfTokenLooper* parentLooper) {
1543 if (!pdfContext->fGraphicsState.fTextBlock) {
1544 // TODO(edisonn): try to recover and draw it any way?
1545 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue,
1546 "\" without BT", NULL, pdfContext);
1547 return kIgnoreError_SkPdfResult;
1550 EXPECT_OPERANDS("\"", pdfContext, 3);
1551 POP_OBJ(pdfContext, str);
1552 POP_OBJ(pdfContext, ac);
1553 POP_OBJ(pdfContext, aw);
1556 pdfContext->fObjectStack.push(aw);
1557 PdfOp_Tw(pdfContext, canvas, parentLooper);
1559 pdfContext->fObjectStack.push(ac);
1560 PdfOp_Tc(pdfContext, canvas, parentLooper);
1562 pdfContext->fObjectStack.push(str);
1563 PdfOp_quote(pdfContext, canvas, parentLooper);
1565 return kPartial_SkPdfResult;
1568 static SkPdfResult PdfOp_TJ(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1569 EXPECT_OPERANDS("Tf", pdfContext, 1);
1570 POP_ARRAY(pdfContext, array);
1573 if (!pdfContext->fGraphicsState.fTextBlock) {
1574 // TODO(edisonn): try to recover and draw it any way?
1575 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingBT_SkPdfIssue, "TJ without BT", NULL,
1577 return kIgnoreError_SkPdfResult;
1580 if (!array->isArray()) {
1581 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, array,
1582 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
1583 return kIgnoreError_SkPdfResult;
1586 for( int i=0; i<static_cast<int>(array->size()); i++ )
1589 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
1590 "element [i] is null, no element should be null",
1592 SkPdfNativeObject::_kAnyString_PdfObjectType |
1593 SkPdfNativeObject::_kNumber_PdfObjectType,
1595 } else if( (*array)[i]->isAnyString()) {
1596 SkPdfNativeObject* obj = (*array)[i];
1597 DrawText(pdfContext, obj, canvas);
1598 } else if ((*array)[i]->isNumber()) {
1599 double dx = (*array)[i]->numberValue();
1601 matrix.setAll(SkDoubleToScalar(1),
1602 SkDoubleToScalar(0),
1603 // TODO(edisonn): use writing mode, vertical/horizontal.
1604 SkDoubleToScalar(-dx), // amount is substracted!!!
1605 SkDoubleToScalar(0),
1606 SkDoubleToScalar(1),
1607 SkDoubleToScalar(0),
1608 SkDoubleToScalar(0),
1609 SkDoubleToScalar(0),
1610 SkDoubleToScalar(1));
1612 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1614 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", (*array)[i],
1615 SkPdfNativeObject::kArray_PdfObjectType |
1616 SkPdfNativeObject::_kNumber_PdfObjectType,
1620 return kPartial_SkPdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1623 static SkPdfResult PdfOp_CS_cs(SkPdfContext* pdfContext, SkCanvas* canvas,
1624 SkPdfColorOperator* colorOperator) {
1625 EXPECT_OPERANDS("CS/cs", pdfContext, 1);
1626 POP_NAME(pdfContext, name);
1629 //Next, get the ColorSpace Dictionary from the Resource Dictionary:
1630 SkPdfDictionary* colorSpaceResource
1631 = pdfContext->fGraphicsState.fResources->ColorSpace(pdfContext->fPdfDoc);
1633 SkPdfNativeObject* colorSpace
1634 = colorSpaceResource ? pdfContext->fPdfDoc
1635 ->resolveReference(colorSpaceResource->get(name)) :
1638 if (colorSpace == NULL) {
1639 colorOperator->fColorSpace = name->strRef();
1642 printf("CS = %s\n", colorSpace->toString(0, 0).c_str());
1644 if (colorSpace->isName()) {
1645 colorOperator->fColorSpace = colorSpace->strRef();
1646 } else if (colorSpace->isArray()) {
1647 size_t cnt = colorSpace->size();
1649 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
1650 "color space has length 0", colorSpace, pdfContext);
1651 return kIgnoreError_SkPdfResult;
1653 SkPdfNativeObject* type = colorSpace->objAtAIndex(0);
1654 type = pdfContext->fPdfDoc->resolveReference(type);
1656 if (type->isName("ICCBased")) {
1658 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
1659 "ICCBased color space must have an array with 2 elements",
1660 colorSpace, pdfContext);
1661 return kIgnoreError_SkPdfResult;
1663 SkPdfNativeObject* prop = colorSpace->objAtAIndex(1);
1664 prop = pdfContext->fPdfDoc->resolveReference(prop);
1666 printf("ICCBased prop = %s\n", prop->toString(0, 0).c_str());
1668 // TODO(edisonn): hack
1669 if (prop && prop->isDictionary() && prop->get("N") &&
1670 prop->get("N")->isInteger() && prop->get("N")->intValue() == 3) {
1671 colorOperator->setColorSpace(&strings_DeviceRGB);
1672 return kPartial_SkPdfResult;
1674 return kNYI_SkPdfResult;
1679 return kPartial_SkPdfResult;
1682 static SkPdfResult PdfOp_CS(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1683 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1686 static SkPdfResult PdfOp_cs(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1687 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1690 static SkPdfResult PdfOp_SC_sc(SkPdfContext* pdfContext, SkCanvas* canvas,
1691 SkPdfColorOperator* colorOperator) {
1695 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1697 bool doubles = true;
1698 if (colorOperator->fColorSpace.equals("Indexed")) {
1703 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
1706 EXPECT_OPERANDS("SC/sc", pdfContext, n);
1708 for (int i = n - 1; i >= 0 ; i--) {
1710 POP_NUMBER_INTO(pdfContext, c[i]);
1712 // v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
1717 // TODO(edisonn): Now, set that color. Only DeviceRGB supported.
1718 // TODO(edisonn): do possible field values to enum at parsing time!
1719 // TODO(edisonn): support also abbreviations /DeviceRGB == /RGB
1720 if (colorOperator->fColorSpace.equals("DeviceRGB") ||
1721 colorOperator->fColorSpace.equals("RGB")) {
1722 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*c[0]),
1724 (U8CPU)(255*c[2])));
1726 return kPartial_SkPdfResult;
1729 static SkPdfResult PdfOp_SC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1730 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1733 static SkPdfResult PdfOp_sc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1734 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1737 static SkPdfResult PdfOp_SCN_scn(SkPdfContext* pdfContext, SkCanvas* canvas,
1738 SkPdfColorOperator* colorOperator) {
1739 if (pdfContext->fObjectStack.count() > 0 && pdfContext->fObjectStack.top()->isName()) {
1740 SkPdfNativeObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop();
1742 SkPdfDictionary* patternResources
1743 = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
1745 if (patternResources == NULL) {
1747 printf("ExtGState is NULL!\n");
1749 return kIgnoreError_SkPdfResult;
1752 colorOperator->setPatternColorSpace(
1753 pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
1756 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1757 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1759 return kPartial_SkPdfResult;
1762 static SkPdfResult PdfOp_SCN(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1763 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1766 static SkPdfResult PdfOp_scn(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1767 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1770 static SkPdfResult PdfOp_G_g(SkPdfContext* pdfContext, SkCanvas* canvas,
1771 SkPdfColorOperator* colorOperator) {
1772 EXPECT_OPERANDS("G/g", pdfContext, 1);
1773 POP_NUMBER(pdfContext, gray);
1776 // TODO(edisonn): limit gray in [0, 1]
1778 // TODO(edisonn): HACK - it should be device gray, but not suported right now
1779 colorOperator->fColorSpace = strings_DeviceRGB;
1780 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255 * gray),
1781 (U8CPU)(255 * gray),
1782 (U8CPU)(255 * gray)));
1784 return kPartial_SkPdfResult;
1787 static SkPdfResult PdfOp_G(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1788 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1791 static SkPdfResult PdfOp_g(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1792 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1795 static SkPdfResult PdfOp_RG_rg(SkPdfContext* pdfContext, SkCanvas* canvas,
1796 SkPdfColorOperator* colorOperator) {
1797 EXPECT_OPERANDS("RG/rg", pdfContext, 3);
1798 POP_NUMBER(pdfContext, b);
1799 POP_NUMBER(pdfContext, g);
1800 POP_NUMBER(pdfContext, r);
1803 colorOperator->fColorSpace = strings_DeviceRGB;
1804 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
1805 return kOK_SkPdfResult;
1808 static SkPdfResult PdfOp_RG(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1809 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1812 static SkPdfResult PdfOp_rg(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1813 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1816 static SkPdfResult PdfOp_K_k(SkPdfContext* pdfContext, SkCanvas* canvas,
1817 SkPdfColorOperator* colorOperator) {
1818 // TODO(edisonn): spec has some rules about overprint, implement them.
1819 EXPECT_OPERANDS("K/k", pdfContext, 4);
1820 POP_NUMBER(pdfContext, k);
1821 POP_NUMBER(pdfContext, y);
1822 POP_NUMBER(pdfContext, m);
1823 POP_NUMBER(pdfContext, c);
1826 // TODO(edisonn): really silly quick way to remove compiler warning
1827 if (k + y + m + c == 0) {
1828 return kNYI_SkPdfResult;
1831 //colorOperator->fColorSpace = strings_DeviceCMYK;
1832 // TODO(edisonn): Set color.
1833 return kNYI_SkPdfResult;
1836 static SkPdfResult PdfOp_K(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1837 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1840 static SkPdfResult PdfOp_k(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1841 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1844 static SkPdfResult PdfOp_W(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1845 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1846 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1848 return kOK_SkPdfResult;
1851 static SkPdfResult PdfOp_W_star(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1852 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1854 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1855 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1857 return kOK_SkPdfResult;
1860 static SkPdfResult PdfOp_BX(SkPdfContext* pdfContext, SkCanvas* canvas,
1861 SkPdfTokenLooper* parentLooper) {
1862 PdfCompatibilitySectionLooper looper(parentLooper);
1864 return kOK_SkPdfResult;
1867 static SkPdfResult PdfOp_EX(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1868 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue,
1869 "EX operator should not be called, it is handled in a looper, "
1870 "unless the file is corrupted, we should assert",
1873 return kIgnoreError_SkPdfResult;
1876 static SkPdfResult PdfOp_BI(SkPdfContext* pdfContext, SkCanvas* canvas,
1877 SkPdfTokenLooper* parentLooper) {
1878 PdfInlineImageLooper looper(parentLooper);
1880 return kOK_SkPdfResult;
1883 static SkPdfResult PdfOp_ID(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1884 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue,
1885 "ID operator should not be called, it is habdled in a looper, "
1886 "unless the file is corrupted, we should assert",
1888 return kIgnoreError_SkPdfResult;
1891 static SkPdfResult PdfOp_EI(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1892 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNullObject_SkPdfIssue,
1893 "EI operator should not be called, it is habdled in a looper, "
1894 "unless the file is corrupted, we should assert",
1896 return kIgnoreError_SkPdfResult;
1899 static SkPdfResult skpdfGraphicsStateApply_ca(SkPdfContext* pdfContext, double ca) {
1900 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
1901 return kOK_SkPdfResult;
1904 static SkPdfResult skpdfGraphicsStateApply_CA(SkPdfContext* pdfContext, double CA) {
1905 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
1906 return kOK_SkPdfResult;
1909 static SkPdfResult skpdfGraphicsStateApplyLW(SkPdfContext* pdfContext, double lineWidth) {
1910 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1911 return kOK_SkPdfResult;
1914 static SkPdfResult skpdfGraphicsStateApplyLC(SkPdfContext* pdfContext, int64_t lineCap) {
1915 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
1916 return kOK_SkPdfResult;
1919 static SkPdfResult skpdfGraphicsStateApplyLJ(SkPdfContext* pdfContext, int64_t lineJoin) {
1920 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
1921 return kOK_SkPdfResult;
1924 static SkPdfResult skpdfGraphicsStateApplyML(SkPdfContext* pdfContext, double miterLimit) {
1925 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
1926 return kOK_SkPdfResult;
1929 // TODO(edisonn): test all dashing rules, not sure if they work as in skia.
1931 1) [ ] 0 No dash; solid, unbroken lines
1932 2) [3] 0 3 units on, 3 units off, …
1933 3) [2] 1 1 on, 2 off, 2 on, 2 off, …
1934 4) [2 1] 0 2 on, 1 off, 2 on, 1 off, …
1935 5) [3 5] 6 2 off, 3 on, 5 off, 3 on, 5 off, …
1936 6) [2 3] 11 1 on, 3 off, 2 on, 3 off, 2 on, …
1939 static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* intervals,
1940 SkPdfNativeObject* phase) {
1941 if (intervals == NULL) {
1942 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, intervals,
1943 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
1944 return kIgnoreError_SkPdfResult;
1947 if (phase == NULL) {
1948 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, phase,
1949 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
1950 return kIgnoreError_SkPdfResult;
1953 int cnt = (int) intervals->size();
1955 // TODO(edisonn): alloc memory
1956 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
1957 "dash array size unssuported, cnt > 256", intervals, pdfContext);
1958 return kIgnoreError_SkPdfResult;
1960 for (int i = 0; i < cnt; i++) {
1961 if (!intervals->objAtAIndex(i) || !intervals->objAtAIndex(i)->isNumber()) {
1962 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
1963 intervals->objAtAIndex(i),
1964 SkPdfNativeObject::_kNumber_PdfObjectType, NULL);
1965 return kIgnoreError_SkPdfResult;
1970 for (int i = 0 ; i < cnt; i++) {
1971 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
1972 total += pdfContext->fGraphicsState.fDashArray[i];
1976 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
1979 // TODO(edisonn): report error/warning
1980 return kNYI_SkPdfResult;
1983 pdfContext->fGraphicsState.fDashArrayLength = cnt;
1984 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue();
1985 if (pdfContext->fGraphicsState.fDashPhase == 0) {
1986 // other rules, changes?
1987 pdfContext->fGraphicsState.fDashPhase = SkDoubleToScalar(total);
1990 return kOK_SkPdfResult;
1993 static SkPdfResult skpdfGraphicsStateApplyD(SkPdfContext* pdfContext, SkPdfArray* dash) {
1994 if (!dash || dash->isArray()) {
1995 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash,
1996 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
1997 return kIgnoreError_SkPdfResult;
2000 if (dash->size() != 2) {
2001 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
2002 "hash array must have 2 elements", dash, pdfContext);
2003 return kIgnoreError_SkPdfResult;
2006 if (!dash->objAtAIndex(0) || !dash->objAtAIndex(0)->isArray()) {
2007 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash->objAtAIndex(0),
2008 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
2009 return kIgnoreError_SkPdfResult;
2012 if (!dash->objAtAIndex(1) || !dash->objAtAIndex(1)->isNumber()) {
2013 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, dash->objAtAIndex(1),
2014 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
2015 return kIgnoreError_SkPdfResult;
2018 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0),
2019 dash->objAtAIndex(1));
2022 static void skpdfGraphicsStateApplyFont(SkPdfContext* pdfContext, SkPdfArray* fontAndSize) {
2023 if (!fontAndSize || !fontAndSize->isArray()) {
2024 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, fontAndSize,
2025 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
2029 if (fontAndSize->size() != 2) {
2030 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
2031 "font array must have 2 elements", fontAndSize, pdfContext);
2035 if (!fontAndSize->objAtAIndex(0) || !fontAndSize->objAtAIndex(0)->isName()) {
2036 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2037 fontAndSize->objAtAIndex(0),
2038 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
2043 if (!fontAndSize->objAtAIndex(1) || !fontAndSize->objAtAIndex(1)->isNumber()) {
2044 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2045 fontAndSize->objAtAIndex(0),
2046 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
2050 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0),
2051 fontAndSize->objAtAIndex(1)->numberValue());
2055 //lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
2056 static SkPdfResult PdfOp_w(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2057 EXPECT_OPERANDS("w", pdfContext, 1);
2058 POP_NUMBER(pdfContext, lw);
2061 return skpdfGraphicsStateApplyLW(pdfContext, lw);
2064 //lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
2065 static SkPdfResult PdfOp_J(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2066 // TODO(edisonn): round/ceil to int?
2067 EXPECT_OPERANDS("J", pdfContext, 1);
2068 POP_NUMBER(pdfContext, lc);
2071 return skpdfGraphicsStateApplyLC(pdfContext, (int)lc);
2074 //lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
2075 static SkPdfResult PdfOp_j(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2076 // TODO(edisonn): round/ceil to int?
2077 EXPECT_OPERANDS("j", pdfContext, 1);
2078 POP_NUMBER(pdfContext, lj);
2081 return skpdfGraphicsStateApplyLJ(pdfContext, (int)lj);
2084 //miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
2085 static SkPdfResult PdfOp_M(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2086 EXPECT_OPERANDS("M", pdfContext, 1);
2087 POP_NUMBER(pdfContext, ml);
2089 return skpdfGraphicsStateApplyML(pdfContext, ml);
2092 //dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
2094 static SkPdfResult PdfOp_d(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2095 EXPECT_OPERANDS("d", pdfContext, 2);
2096 POP_OBJ(pdfContext, phase);
2097 POP_ARRAY(pdfContext, array);
2100 return skpdfGraphicsStateApplyD(pdfContext, array, phase);
2103 //intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents”
2105 static SkPdfResult PdfOp_ri(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2106 pdfContext->fObjectStack.pop();
2108 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "render intent NYI", NULL,
2111 return kNYI_SkPdfResult;
2114 //flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1, “Flatness
2115 //Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci-
2116 //fies the output device’s default flatness tolerance.
2117 static SkPdfResult PdfOp_i(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2118 EXPECT_OPERANDS("i", pdfContext, 1);
2119 POP_NUMBER(pdfContext, flatness);
2122 if (flatness < 0 || flatness > 100) {
2123 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2124 "flatness must be a real in [0, 100] range", flatness_obj, pdfContext);
2125 return kIgnoreError_SkPdfResult;
2128 return kNYI_SkPdfResult;
2131 SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
2133 class InitBlendModes {
2136 // TODO(edisonn): use the python code generator?
2137 // TABLE 7.2 Standard separable blend modes
2138 gPdfBlendModes.set("Normal", SkXfermode::kSrc_Mode);
2139 gPdfBlendModes.set("Multiply", SkXfermode::kMultiply_Mode);
2140 gPdfBlendModes.set("Screen", SkXfermode::kScreen_Mode);
2141 gPdfBlendModes.set("Overlay", SkXfermode::kOverlay_Mode);
2142 gPdfBlendModes.set("Darken", SkXfermode::kDarken_Mode);
2143 gPdfBlendModes.set("Lighten", SkXfermode::kLighten_Mode);
2144 gPdfBlendModes.set("ColorDodge", SkXfermode::kColorDodge_Mode);
2145 gPdfBlendModes.set("ColorBurn", SkXfermode::kColorBurn_Mode);
2146 gPdfBlendModes.set("HardLight", SkXfermode::kHardLight_Mode);
2147 gPdfBlendModes.set("SoftLight", SkXfermode::kSoftLight_Mode);
2148 gPdfBlendModes.set("Difference", SkXfermode::kDifference_Mode);
2149 gPdfBlendModes.set("Exclusion", SkXfermode::kExclusion_Mode);
2151 // TABLE 7.3 Standard nonseparable blend modes
2152 gPdfBlendModes.set("Hue", SkXfermode::kHue_Mode);
2153 gPdfBlendModes.set("Saturation", SkXfermode::kSaturation_Mode);
2154 gPdfBlendModes.set("Color", SkXfermode::kColor_Mode);
2155 gPdfBlendModes.set("Luminosity", SkXfermode::kLuminosity_Mode);
2159 InitBlendModes _gDummyInniter;
2161 static SkXfermode::Mode xferModeFromBlendMode(const char* blendMode, size_t len) {
2162 SkXfermode::Mode mode = (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2163 if (gPdfBlendModes.find(blendMode, len, &mode)) {
2167 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2170 static void skpdfGraphicsStateApplyBM_name(SkPdfContext* pdfContext, const SkString& blendMode) {
2171 SkXfermode::Mode mode = xferModeFromBlendMode(blendMode.c_str(), blendMode.size());
2172 if (mode <= SkXfermode::kLastMode) {
2173 pdfContext->fGraphicsState.fBlendModesLength = 1;
2174 pdfContext->fGraphicsState.fBlendModes[0] = mode;
2176 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue,
2177 blendMode.c_str(), NULL, pdfContext);
2181 static void skpdfGraphicsStateApplyBM_array(SkPdfContext* pdfContext, SkPdfArray* blendModes) {
2182 if (!blendModes || !blendModes->isArray()) {
2183 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, blendModes,
2184 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
2188 if (blendModes->size() == 0 || blendModes->size() > 256) {
2189 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
2190 "length of blendmodes, 0, is an erro, 256+, is NYI", blendModes, pdfContext);
2194 SkXfermode::Mode modes[256];
2195 int cnt = (int) blendModes->size();
2196 for (int i = 0; i < cnt; i++) {
2197 SkPdfNativeObject* name = blendModes->objAtAIndex(i);
2198 if (!name || !name->isName()) {
2199 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name,
2200 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
2203 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
2204 if (mode > SkXfermode::kLastMode) {
2205 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue, NULL, name,
2211 pdfContext->fGraphicsState.fBlendModesLength = cnt;
2212 for (int i = 0; i < cnt; i++) {
2213 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
2217 static void skpdfGraphicsStateApplySMask_dict(SkPdfContext* pdfContext, SkPdfDictionary* sMask) {
2218 if (!sMask || !sMask->isName()) {
2219 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, sMask,
2220 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
2224 if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
2225 pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
2226 } else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
2227 SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
2228 pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
2230 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
2231 "Dictionary must be SoftMask, or SoftMaskImage",
2232 sMask, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
2236 static void skpdfGraphicsStateApplySMask_name(SkPdfContext* pdfContext, const SkString& sMask) {
2237 if (sMask.equals("None")) {
2238 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
2239 pdfContext->fGraphicsState.fSMask = NULL;
2243 SkPdfDictionary* extGStateDictionary
2244 = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2246 if (extGStateDictionary == NULL) {
2247 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL,
2248 pdfContext->fGraphicsState.fResources, pdfContext);
2252 SkPdfNativeObject* obj
2253 = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(sMask.c_str()));
2254 if (!obj || !obj->isDictionary()) {
2255 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, obj,
2256 SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
2260 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
2261 pdfContext->fGraphicsState.fSMask = NULL;
2263 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
2266 static void skpdfGraphicsStateApplyAIS(SkPdfContext* pdfContext, bool alphaSource) {
2267 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2271 //dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
2272 //the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current
2273 //resource dictionary (see the next section).
2274 static SkPdfResult PdfOp_gs(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2275 EXPECT_OPERANDS("gs", pdfContext, 1);
2276 POP_NAME(pdfContext, name);
2279 SkPdfDictionary* extGStateDictionary
2280 = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2282 if (extGStateDictionary == NULL) {
2283 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL,
2284 pdfContext->fGraphicsState.fResources, pdfContext);
2285 return kIgnoreError_SkPdfResult;
2288 SkPdfNativeObject* value
2289 = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
2291 if (kNone_SkPdfNativeObjectType ==
2292 pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2293 return kIgnoreError_SkPdfResult;
2295 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
2298 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2299 gs, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
2300 return kIgnoreError_SkPdfResult;
2304 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2308 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2312 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2316 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2320 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2323 if (gs->has_Font()) {
2324 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2328 if (gs->isBMAName(pdfContext->fPdfDoc)) {
2329 skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
2330 } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
2331 skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
2333 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", gs->get("BM"),
2334 SkPdfNativeObject::kArray_PdfObjectType |
2335 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
2339 if (gs->has_SMask()) {
2340 if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
2341 skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
2342 } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
2343 skpdfGraphicsStateApplySMask_dict(pdfContext,
2344 gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
2346 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
2349 SkPdfNativeObject::kDictionary_PdfObjectType |
2350 SkPdfNativeObject::kName_PdfObjectType,
2356 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
2360 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2363 if (gs->has_AIS()) {
2364 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
2367 // TODO(edisonn): make sure we loaded all those properties in graphic state.
2369 return kOK_SkPdfResult;
2372 //charSpace Tc Set the character spacing, Tc
2373 //, to charSpace, which is a number expressed in unscaled text space units.
2374 // Character spacing is used by the Tj, TJ, and ' operators.
2376 SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2377 EXPECT_OPERANDS("Tc", pdfContext, 1);
2378 POP_NUMBER(pdfContext, charSpace);
2381 pdfContext->fGraphicsState.fCharSpace = charSpace;
2383 return kOK_SkPdfResult;
2386 //wordSpace Tw Set the word spacing, T
2388 //, to wordSpace, which is a number expressed in unscaled
2389 //text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial
2391 SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2392 EXPECT_OPERANDS("Tw", pdfContext, 1);
2393 POP_NUMBER(pdfContext, wordSpace);
2396 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2398 return kOK_SkPdfResult;
2401 //scale Tz Set the horizontal scaling, Th
2402 //, to (scale ˜ 100). scale is a number specifying the
2403 //percentage of the normal width. Initial value: 100 (normal width).
2404 static SkPdfResult PdfOp_Tz(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2405 EXPECT_OPERANDS("Tz", pdfContext, 1);
2406 POP_NUMBER(pdfContext, scale);
2410 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2411 "scale must a positive real number", scale_obj, pdfContext);
2412 return kError_SkPdfResult;
2415 return kNYI_SkPdfResult;
2418 //render Tr Set the text rendering mode, T
2419 //mode, to render, which is an integer. Initial value: 0.
2420 static SkPdfResult PdfOp_Tr(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2421 EXPECT_OPERANDS("Tr", pdfContext, 1);
2422 POP_INTEGER(pdfContext, mode);
2425 if (mode < 0) { // TODO(edisonn): function/enums with supported modes
2426 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2427 "mode must a positive integer or 0", mode_obj, pdfContext);
2428 return kError_SkPdfResult;
2431 return kNYI_SkPdfResult;
2433 //rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2434 //units. Initial value: 0.
2435 static SkPdfResult PdfOp_Ts(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2436 EXPECT_OPERANDS("Ts", pdfContext, 1);
2437 POP_NUMBER(pdfContext, rise);
2441 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2442 "rise must a positive real number", rise_obj, pdfContext);
2443 return kNYI_SkPdfResult;
2446 return kNYI_SkPdfResult;
2450 static SkPdfResult PdfOp_d0(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2451 EXPECT_OPERANDS("d0", pdfContext, 2);
2452 POP_NUMBER(pdfContext, wy);
2453 POP_NUMBER(pdfContext, wx);
2457 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2458 "wx must a positive real number", wx_obj, pdfContext);
2459 return kError_SkPdfResult;
2463 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2464 "wy must a positive real number", wy_obj, pdfContext);
2465 return kError_SkPdfResult;
2468 return kNYI_SkPdfResult;
2471 //wx wy llx lly urx ury d1
2472 static SkPdfResult PdfOp_d1(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2473 EXPECT_OPERANDS("d1", pdfContext, 6);
2474 POP_NUMBER(pdfContext, ury);
2475 POP_NUMBER(pdfContext, urx);
2476 POP_NUMBER(pdfContext, lly);
2477 POP_NUMBER(pdfContext, llx);
2478 POP_NUMBER(pdfContext, wy);
2479 POP_NUMBER(pdfContext, wx);
2482 // TODO(edisonn): really silly quick way to remove warning
2483 if (wx + wy + llx + lly + urx + ury) {
2484 return kNYI_SkPdfResult;
2487 return kNYI_SkPdfResult;
2491 static SkPdfResult PdfOp_sh(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2492 EXPECT_OPERANDS("sh", pdfContext, 1);
2493 POP_NAME(pdfContext, name);
2497 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name,
2498 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
2499 return kError_SkPdfResult;
2502 return kNYI_SkPdfResult;
2506 static SkPdfResult PdfOp_Do(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2507 EXPECT_OPERANDS("Do", pdfContext, 1);
2508 POP_NAME(pdfContext, name);
2511 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
2513 if (xObject == NULL) {
2514 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingXObject_SkPdfIssue, NULL,
2515 pdfContext->fGraphicsState.fResources, pdfContext);
2516 return kIgnoreError_SkPdfResult;
2519 SkPdfNativeObject* value = xObject->get(name);
2520 value = pdfContext->fPdfDoc->resolveReference(value);
2522 return doXObject(pdfContext, canvas, value);
2525 //tag MP Designate a marked-content point. tag is a name object indicating the role or
2526 //significance of the point.
2527 static SkPdfResult PdfOp_MP(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2528 EXPECT_OPERANDS("MP", pdfContext, 1);
2529 POP_OBJ(pdfContext, tag);
2533 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2534 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2535 return kNYI_SkPdfResult;
2538 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MP NYI", NULL, NULL);
2539 return kNYI_SkPdfResult;
2542 //tag properties DP Designate a marked-content point with an associated property list. tag is a
2543 //name object indicating the role or significance of the point; properties is
2544 //either an inline dictionary containing the property list or a name object
2545 //associated with it in the Properties subdictionary of the current resource
2546 //dictionary (see Section 9.5.1, “Property Lists”).
2547 static SkPdfResult PdfOp_DP(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2548 EXPECT_OPERANDS("DP", pdfContext, 2);
2549 POP_OBJ(pdfContext, properties);
2550 POP_OBJ(pdfContext, tag);
2554 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2555 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2556 return kNYI_SkPdfResult;
2559 if (properties == NULL) {
2560 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties,
2561 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2562 return kNYI_SkPdfResult;
2565 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "DP NYI", NULL, NULL);
2566 return kNYI_SkPdfResult;
2569 //tag BMC Begin a marked-content sequence terminated by a balancing EMC operator.
2570 //tag is a name object indicating the role or significance of the sequence.
2571 static SkPdfResult PdfOp_BMC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2572 EXPECT_OPERANDS("BMC", pdfContext, 1);
2573 POP_OBJ(pdfContext, tag);
2577 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2578 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2579 return kNYI_SkPdfResult;
2582 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BMC NYI", NULL, NULL);
2583 return kNYI_SkPdfResult;
2586 //tag properties BDC Begin a marked-content sequence with an associated property list, terminated
2587 //by a balancing EMCoperator. tag is a name object indicating the role or significance of the
2588 // sequence; propertiesis either an inline dictionary containing the
2589 //property list or a name object associated with it in the Properties subdictionary of the current
2590 //resource dictionary (see Section 9.5.1, “Property Lists”).
2591 static SkPdfResult PdfOp_BDC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2592 EXPECT_OPERANDS("BDC", pdfContext, 2);
2593 POP_OBJ(pdfContext, properties);
2594 POP_OBJ(pdfContext, tag);
2598 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2599 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2600 return kNYI_SkPdfResult;
2603 if (properties == NULL) {
2604 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties,
2605 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2606 return kNYI_SkPdfResult;
2609 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BDC NYI", NULL, NULL);
2610 return kNYI_SkPdfResult;
2613 //— EMC End a marked-content sequence begun by a BMC or BDC operator.
2614 static SkPdfResult PdfOp_EMC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2615 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "EMC NYI", NULL, NULL);
2616 return kNYI_SkPdfResult;
2619 #include "SkPdfOps.h"
2621 SkTDict<PdfOperatorRenderer> gPdfOps(100);
2623 static void initPdfOperatorRenderes() {
2624 static bool gInitialized = false;
2629 gPdfOps.set("q", PdfOp_q);
2630 gPdfOps.set("Q", PdfOp_Q);
2631 gPdfOps.set("cm", PdfOp_cm);
2633 gPdfOps.set("TD", PdfOp_TD);
2634 gPdfOps.set("Td", PdfOp_Td);
2635 gPdfOps.set("Tm", PdfOp_Tm);
2636 gPdfOps.set("T*", PdfOp_T_star);
2638 gPdfOps.set("m", PdfOp_m);
2639 gPdfOps.set("l", PdfOp_l);
2640 gPdfOps.set("c", PdfOp_c);
2641 gPdfOps.set("v", PdfOp_v);
2642 gPdfOps.set("y", PdfOp_y);
2643 gPdfOps.set("h", PdfOp_h);
2644 gPdfOps.set("re", PdfOp_re);
2646 gPdfOps.set("S", PdfOp_S);
2647 gPdfOps.set("s", PdfOp_s);
2648 gPdfOps.set("f", PdfOp_f);
2649 gPdfOps.set("F", PdfOp_F);
2650 gPdfOps.set("f*", PdfOp_f_star);
2651 gPdfOps.set("B", PdfOp_B);
2652 gPdfOps.set("B*", PdfOp_B_star);
2653 gPdfOps.set("b", PdfOp_b);
2654 gPdfOps.set("b*", PdfOp_b_star);
2655 gPdfOps.set("n", PdfOp_n);
2657 gPdfOps.set("BT", PdfOp_BT);
2658 gPdfOps.set("ET", PdfOp_ET);
2660 gPdfOps.set("Tj", PdfOp_Tj);
2661 gPdfOps.set("'", PdfOp_quote);
2662 gPdfOps.set("\"", PdfOp_doublequote);
2663 gPdfOps.set("TJ", PdfOp_TJ);
2665 gPdfOps.set("CS", PdfOp_CS);
2666 gPdfOps.set("cs", PdfOp_cs);
2667 gPdfOps.set("SC", PdfOp_SC);
2668 gPdfOps.set("SCN", PdfOp_SCN);
2669 gPdfOps.set("sc", PdfOp_sc);
2670 gPdfOps.set("scn", PdfOp_scn);
2671 gPdfOps.set("G", PdfOp_G);
2672 gPdfOps.set("g", PdfOp_g);
2673 gPdfOps.set("RG", PdfOp_RG);
2674 gPdfOps.set("rg", PdfOp_rg);
2675 gPdfOps.set("K", PdfOp_K);
2676 gPdfOps.set("k", PdfOp_k);
2678 gPdfOps.set("W", PdfOp_W);
2679 gPdfOps.set("W*", PdfOp_W_star);
2681 gPdfOps.set("BX", PdfOp_BX);
2682 gPdfOps.set("EX", PdfOp_EX);
2684 gPdfOps.set("BI", PdfOp_BI);
2685 gPdfOps.set("ID", PdfOp_ID);
2686 gPdfOps.set("EI", PdfOp_EI);
2688 gPdfOps.set("w", PdfOp_w);
2689 gPdfOps.set("J", PdfOp_J);
2690 gPdfOps.set("j", PdfOp_j);
2691 gPdfOps.set("M", PdfOp_M);
2692 gPdfOps.set("d", PdfOp_d);
2693 gPdfOps.set("ri", PdfOp_ri);
2694 gPdfOps.set("i", PdfOp_i);
2695 gPdfOps.set("gs", PdfOp_gs);
2697 gPdfOps.set("Tc", PdfOp_Tc);
2698 gPdfOps.set("Tw", PdfOp_Tw);
2699 gPdfOps.set("Tz", PdfOp_Tz);
2700 gPdfOps.set("TL", PdfOp_TL);
2701 gPdfOps.set("Tf", PdfOp_Tf);
2702 gPdfOps.set("Tr", PdfOp_Tr);
2703 gPdfOps.set("Ts", PdfOp_Ts);
2705 gPdfOps.set("d0", PdfOp_d0);
2706 gPdfOps.set("d1", PdfOp_d1);
2708 gPdfOps.set("sh", PdfOp_sh);
2710 gPdfOps.set("Do", PdfOp_Do);
2712 gPdfOps.set("MP", PdfOp_MP);
2713 gPdfOps.set("DP", PdfOp_DP);
2714 gPdfOps.set("BMC", PdfOp_BMC);
2715 gPdfOps.set("BDC", PdfOp_BDC);
2716 gPdfOps.set("EMC", PdfOp_EMC);
2718 gInitialized = true;
2724 initPdfOperatorRenderes();
2728 InitPdfOps gInitPdfOps;
2730 SkPdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
2732 return kIgnoreError_SkPdfResult;
2735 void PdfInlineImageLooper::loop() {
2736 // FIXME (scroggo): Does this need to be looper? It does not consumeTokens,
2737 // nor does it loop. The one thing it does is provide access to the
2738 // protected members of SkPdfTokenLooper.
2739 doXObject_Image(fPdfContext, fCanvas, fTokenizer->readInlineImage());
2742 SkPdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2743 return fParent->consumeToken(token);
2746 void PdfCompatibilitySectionLooper::loop() {
2747 PdfOp_q(fPdfContext, fCanvas, NULL);
2750 while (fTokenizer->readToken(&token)) {
2751 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2752 PdfCompatibilitySectionLooper looper(this);
2755 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) {
2758 fParent->consumeToken(token);
2762 PdfOp_Q(fPdfContext, fCanvas, NULL);
2765 // TODO(edisonn): for debugging - remove or put it in a #ifdef
2766 SkPdfContext* gPdfContext = NULL;
2768 bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
2773 if (page < 0 || page >= pages()) {
2777 SkPdfContext pdfContext(fPdfDoc);
2779 // FIXME (scroggo): Is this matrix needed?
2780 pdfContext.fOriginalMatrix = SkMatrix::I();
2781 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2783 gPdfContext = &pdfContext;
2785 SkScalar z = SkIntToScalar(0);
2786 SkScalar w = dst.width();
2787 SkScalar h = dst.height();
2789 if (SkScalarTruncToInt(w) <= 0 || SkScalarTruncToInt(h) <= 0) {
2793 // FIXME (scroggo): The media box may not be anchored at 0,0. Is this okay?
2794 SkScalar wp = fPdfDoc->MediaBox(page).width();
2795 SkScalar hp = fPdfDoc->MediaBox(page).height();
2797 SkPoint pdfSpace[4] = {SkPoint::Make(z, z),
2798 SkPoint::Make(wp, z),
2799 SkPoint::Make(wp, hp),
2800 SkPoint::Make(z, hp)};
2803 // Use larger image to make sure we do not draw anything outside of page
2804 // could be used in tests.
2805 SkPoint skiaSpace[4] = {SkPoint::Make(w+z, h+h),
2806 SkPoint::Make(w+w, h+h),
2807 SkPoint::Make(w+w, h+z),
2808 SkPoint::Make(w+z, h+z)};
2810 SkPoint skiaSpace[4] = {SkPoint::Make(z, h),
2811 SkPoint::Make(w, h),
2812 SkPoint::Make(w, z),
2813 SkPoint::Make(z, z)};
2816 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2817 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2819 // FIXME (scroggo): Do we need to translate to account for the fact that
2820 // the media box (or the destination rect) may not be anchored at 0,0?
2821 pdfContext.fOriginalMatrix.postConcat(canvas->getTotalMatrix());
2823 pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
2824 pdfContext.fGraphicsState.fContentStreamMatrix = pdfContext.fOriginalMatrix;
2825 pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
2826 pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
2828 #ifndef PDF_DEBUG_NO_PAGE_CLIPING
2829 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
2832 // FIXME (scroggo): This concat may not be necessary, since we generally
2833 // call SkCanvas::setMatrix() before using the canvas.
2834 canvas->concat(pdfContext.fOriginalMatrix);
2836 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2838 // TODO(edisonn:) erase with white before draw? Right now the caller is responsible.
2840 // paint.setColor(SK_ColorWHITE);
2841 // canvas->drawRect(rect, paint);
2848 SkPdfRenderer* SkPdfRenderer::CreateFromFile(const char* inputFileName) {
2849 // FIXME: SkPdfNativeDoc should have a similar Create function.
2850 SkPdfNativeDoc* pdfDoc = SkNEW_ARGS(SkPdfNativeDoc, (inputFileName));
2851 if (pdfDoc->pages() == 0) {
2856 return SkNEW_ARGS(SkPdfRenderer, (pdfDoc));
2859 SkPdfRenderer* SkPdfRenderer::CreateFromStream(SkStream* stream) {
2860 // TODO(edisonn): create static function that could return NULL if there are errors
2861 SkPdfNativeDoc* pdfDoc = SkNEW_ARGS(SkPdfNativeDoc, (stream));
2862 if (pdfDoc->pages() == 0) {
2867 return SkNEW_ARGS(SkPdfRenderer, (pdfDoc));
2870 SkPdfRenderer::SkPdfRenderer(SkPdfNativeDoc* doc)
2874 SkPdfRenderer::~SkPdfRenderer() {
2878 int SkPdfRenderer::pages() const {
2879 SkASSERT(fPdfDoc != NULL);
2880 return fPdfDoc->pages();
2883 SkRect SkPdfRenderer::MediaBox(int page) const {
2884 SkASSERT(fPdfDoc != NULL);
2885 return fPdfDoc->MediaBox(page);
2888 size_t SkPdfRenderer::bytesUsed() const {
2889 SkASSERT(fPdfDoc != NULL);
2890 return fPdfDoc->bytesUsed();
2893 bool SkPDFNativeRenderToBitmap(SkStream* stream,
2896 SkPdfContent unused,
2898 SkASSERT(page >= 0);
2899 SkPdfRenderer* renderer = SkPdfRenderer::CreateFromStream(stream);
2900 if (NULL == renderer) {
2904 SkRect rect = renderer->MediaBox(page < 0 ? 0 :page);
2906 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(dpi / 72.0));
2907 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(dpi / 72.0));
2909 rect = SkRect::MakeWH(width, height);
2911 setup_bitmap(output, SkScalarCeilToInt(width), SkScalarCeilToInt(height));
2913 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (*output)));
2914 SkCanvas canvas(device);
2916 return renderer->renderPage(page, &canvas, rect);