1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkPdfRenderer.h"
9
10 #include "SkBitmapDevice.h"
11 #include "SkCanvas.h"
12 #include "SkColorPriv.h"
13 #include "SkDevice.h"
14 #include "SkForceLinking.h"
15 #include "SkGraphics.h"
16 #include "SkImageDecoder.h"
17 #include "SkImageEncoder.h"
18 #include "SkOSFile.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"
29 #include "SkStream.h"
30 #include "SkTypeface.h"
31 #include "SkTArray.h"
32 #include "SkTDict.h"
33
34 // TODO(edisonn): #ifdef these ones, as they are used only for debugging.
35 extern "C" SkPdfContext* gPdfContext;
36
37 __SK_FORCE_IMAGE_DECODER_LINKING;
38
39 // TODO(edisonn): tool, show what objects were read during rendering - will help to identify
40 // features with incomplete implementation
41 // TODO(edisonn): security - validate all the user input, all pdf!
42 // TODO(edisonn): testability -add option to render without text, or only render text
43
44 // Helper macros to load variables from stack, and automatically check their 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);
54
55 #define POP_OBJ(pdfContext,name) \
56 SkDEBUGCODE(__cnt--); \
57 SkASSERT(__cnt >= 0); \
58 SkPdfNativeObject* name = NULL; \
59 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
60 if (pdfContext->fObjectStack.count() > 0) { \
61 name = pdfContext->fObjectStack.top(); \
62 pdfContext->fObjectStack.pop(); \
63 }
64
65 // TODO(edisonn): make all pop function to use name##_obj
66 #define POP_NUMBER(pdfContext,name) \
67 SkDEBUGCODE(__cnt--); \
68 SkASSERT(__cnt >= 0); \
69 double name = 0; \
70 SkPdfNativeObject* name##_obj = NULL; \
71 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
72 if (pdfContext->fObjectStack.count() > 0) { \
73 name##_obj = pdfContext->fObjectStack.top(); \
74 pdfContext->fObjectStack.pop(); \
75 if (!name##_obj || !name##_obj->isNumber()) { \
76 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
77 __operator_name, \
78 name##_obj, \
79 SkPdfNativeObject::_kNumber_PdfObjectType, \
80 NULL);\
81 __failed = true;\
82 } else { \
83 name = name##_obj->numberValue(); \
84 } \
85 }
86
87 #define POP_INTEGER(pdfContext,name) \
88 SkDEBUGCODE(__cnt--); \
89 SkASSERT(__cnt >= 0); \
90 int64_t name = 0; \
91 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
92 SkPdfNativeObject* name##_obj = NULL; \
93 if (pdfContext->fObjectStack.count() > 0) { \
94 name##_obj = pdfContext->fObjectStack.top(); \
95 pdfContext->fObjectStack.pop(); \
96 if (!name##_obj || !name##_obj->isInteger()) { \
97 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
98 __operator_name, \
99 name##_obj, \
100 SkPdfNativeObject::kInteger_PdfObjectType, \
101 NULL);\
102 __failed = true;\
103 } else { \
104 name = name##_obj->intValue(); \
105 } \
106 }
107
108 #define POP_NUMBER_INTO(pdfContext,var) \
109 SkDEBUGCODE(__cnt--); \
110 SkASSERT(__cnt >= 0); \
111 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
112 if (pdfContext->fObjectStack.count() > 0) { \
113 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
114 pdfContext->fObjectStack.pop(); \
115 if (!tmp || !tmp->isNumber()) { \
116 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
117 __operator_name, \
118 tmp, \
119 SkPdfNativeObject::kInteger_PdfObjectType | \
120 SkPdfNativeObject::kReal_PdfObjectType, \
121 NULL);\
122 __failed = true;\
123 } else { \
124 var = tmp->numberValue(); \
125 } \
126 }
127
128
129 #define POP_NAME(pdfContext,name) \
130 SkDEBUGCODE(__cnt--); \
131 SkASSERT(__cnt >= 0); \
132 SkPdfNativeObject* name = NULL; \
133 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
134 if (pdfContext->fObjectStack.count() > 0) { \
135 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
136 pdfContext->fObjectStack.pop(); \
137 if (!tmp || !tmp->isName()) { \
138 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
139 __operator_name, \
140 tmp, \
141 SkPdfNativeObject::kName_PdfObjectType, \
142 NULL);\
143 __failed = true;\
144 } else { \
145 name = tmp; \
146 } \
147 }
148
149 #define POP_STRING(pdfContext,name) \
150 SkDEBUGCODE(__cnt--); \
151 SkASSERT(__cnt >= 0); \
152 SkPdfNativeObject* name = NULL; \
153 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
154 if (pdfContext->fObjectStack.count() > 0) { \
155 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
156 pdfContext->fObjectStack.pop(); \
157 if (!tmp || !tmp->isAnyString()) { \
158 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
159 __operator_name, \
160 tmp, \
161 SkPdfNativeObject::kString_PdfObjectType | \
162 SkPdfNativeObject::kHexString_PdfObjectType, \
163 NULL);\
164 __failed = true;\
165 } else { \
166 name = tmp; \
167 } \
168 }
169
170 #define POP_ARRAY(pdfContext,name) \
171 SkDEBUGCODE(__cnt--); \
172 SkASSERT(__cnt >= 0); \
173 SkPdfArray* name = NULL; \
174 __failed = __failed || pdfContext->fObjectStack.count() == 0; \
175 if (pdfContext->fObjectStack.count() > 0) { \
176 SkPdfNativeObject* tmp = pdfContext->fObjectStack.top(); \
177 pdfContext->fObjectStack.pop(); \
178 if (!tmp || !tmp->isArray()) { \
179 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, \
180 __operator_name, \
181 tmp, \
182 SkPdfNativeObject::kArray_PdfObjectType, \
183 NULL);\
184 __failed = true;\
185 } else { \
186 name = (SkPdfArray*)tmp; \
187 } \
188 }
189
190 #define CHECK_PARAMETERS() \
191 SkASSERT(__cnt == 0); \
192 if (__failed) return kIgnoreError_SkPdfResult;
193
194
195 NotOwnedString strings_DeviceRGB;
196 NotOwnedString strings_DeviceCMYK;
197
198 class StringsInit {
199 public:
StringsInit()200 StringsInit() {
201 NotOwnedString::init(&strings_DeviceRGB, "DeviceRGB");
202 NotOwnedString::init(&strings_DeviceCMYK, "DeviceCMYK");
203 }
204 };
205
206 // TODO(edisonn): this will not work in chrome! Find another solution!
207 StringsInit gStringsInit;
208
209 // TODO(edisonn): Document SkPdfTokenLooper and subclasses.
210 class PdfInlineImageLooper : public SkPdfTokenLooper {
211 public:
PdfInlineImageLooper(SkPdfTokenLooper * parent)212 explicit PdfInlineImageLooper(SkPdfTokenLooper* parent)
213 : INHERITED(parent) {}
214
215 SkPdfResult consumeToken(PdfToken& token) override;
216 void loop() override;
217
218 private:
219 typedef SkPdfTokenLooper INHERITED;
220 };
221
222 class PdfCompatibilitySectionLooper : public SkPdfTokenLooper {
223 public:
PdfCompatibilitySectionLooper(SkPdfTokenLooper * parent)224 explicit PdfCompatibilitySectionLooper(SkPdfTokenLooper* parent)
225 : INHERITED (parent) {}
226
227 SkPdfResult consumeToken(PdfToken& token) override;
228 void loop() override;
229
230 private:
231 typedef SkPdfTokenLooper INHERITED;
232 };
233
234 // Utilities
setup_bitmap(SkBitmap * bitmap,int width,int height,SkColor color=SK_ColorWHITE)235 static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
236 bitmap->allocN32Pixels(width, height);
237 bitmap->eraseColor(color);
238 }
239
240 // TODO(edisonn): synonyms? /DeviceRGB and /RGB mean the same thing. Context dependent.
GetColorSpaceComponents(NotOwnedString & colorSpace)241 static int GetColorSpaceComponents(NotOwnedString& colorSpace) {
242 if (colorSpace.equals("DeviceCMYK")) {
243 return 4;
244 } else if (colorSpace.equals("DeviceGray") ||
245 colorSpace.equals("CalGray") ||
246 colorSpace.equals("Indexed")) {
247 return 1;
248 } else if (colorSpace.equals("DeviceRGB") ||
249 colorSpace.equals("CalRGB") ||
250 colorSpace.equals("Lab")) {
251 return 3;
252 } else {
253 return 0;
254 }
255 }
256
SkMatrixFromPdfMatrix(double array[6])257 SkMatrix SkMatrixFromPdfMatrix(double array[6]) {
258 SkMatrix matrix;
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]),
265 SkDoubleToScalar(0),
266 SkDoubleToScalar(0),
267 SkDoubleToScalar(1));
268
269 return matrix;
270 }
271
SkMatrixFromPdfArray(SkPdfArray * pdfArray)272 SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) {
273 double array[6];
274
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();
280 }
281
282 if (pdfArray->size() != 6) {
283 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnexpectedArraySize_SkPdfIssue,
284 "null array passed to build matrix", pdfArray, NULL);
285 return SkMatrix::I();
286 }
287
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();
294 }
295 array[i] = elem->numberValue();
296 }
297
298 return SkMatrixFromPdfMatrix(array);
299 }
300
301 // TODO(edisonn): debug code, used to analyze rendering when we find bugs.
302 extern "C" SkPdfNativeDoc* gDoc;
303
DrawText(SkPdfContext * pdfContext,const SkPdfNativeObject * _str,SkCanvas * canvas)304 static SkPdfResult DrawText(SkPdfContext* pdfContext,
305 const SkPdfNativeObject* _str,
306 SkCanvas* canvas)
307 {
308 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont;
309 if (skfont == NULL) {
310 skfont = SkPdfFont::Default();
311 }
312
313 if (_str == NULL || !_str->isAnyString()) {
314 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
315 "DrawText",
316 _str,
317 SkPdfNativeObject::_kAnyString_PdfObjectType,
318 pdfContext);
319 return kIgnoreError_SkPdfResult;
320 }
321 const SkPdfString* str = (const SkPdfString*)_str;
322
323 SkUnencodedText binary(str);
324
325 SkDecodedText decoded;
326
327 if (skfont->encoding() == NULL) {
328 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingEncoding_SkPdfIssue,
329 "draw text", _str, pdfContext);
330 return kNYI_SkPdfResult;
331 }
332
333 skfont->encoding()->decodeText(binary, &decoded);
334
335 SkPaint paint;
336 // TODO(edisonn): does size 0 mean anything special?
337 if (pdfContext->fGraphicsState.fCurFontSize != 0) {
338 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSize));
339 }
340
341 // TODO(edisonn): implement font scaler
342 // if (fCurFont && fCurFont->GetFontScale() != 0) {
343 // paint.setTextScaleX(fCurFont->GetFontScale() / 100.0);
344 // }
345
346 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
347
348 skfont->drawText(decoded, &paint, pdfContext, canvas);
349
350 return kOK_SkPdfResult;
351 }
352
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);
358
359 // TODO(edisonn): perf!!!
getGrayColortable()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);
366 }
367 grayColortable = new SkColorTable(colors, 256);
368 }
369 return grayColortable;
370 }
371
transferImageStreamToBitmap(const unsigned char * uncompressedStream,size_t uncompressedStreamLength,int width,int height,int bytesPerLine,int bpc,const SkString & colorSpace,bool transparencyMask)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();
378
379 //int components = GetColorSpaceComponents(colorSpace);
380 //#define MAX_COMPONENTS 10
381
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?
384
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));
388
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]);
396 i++;
397 }
398 uncompressedStream += bytesPerLine;
399 }
400
401 const SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
402 bitmap->installPixels(info, uncompressedStreamArgb, info.minRowBytes());
403 }
404 else if ((colorSpace.equals("DeviceGray") || colorSpace.equals("Gray")) && bpc == 8) {
405 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
406
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];
412 i++;
413 }
414 uncompressedStream += bytesPerLine;
415 }
416
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);
421 }
422
423 // TODO(edisonn): pass color space and context here?
424 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI", NULL, NULL);
425 return bitmap;
426 }
427 // TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 222, 444 to closest
428 // skia format.
429
430 // This functions returns the image, it does not look at the smask.
getImageFromObjectCore(SkPdfContext * pdfContext,SkPdfImageDictionary * image,bool transparencyMask)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);
436 return NULL;
437 }
438
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");
443
444 bool indexed = false;
445 SkPMColor colors[256];
446 int cnt = 0;
447
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()
457 ) {
458 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "Color space NYI",
459 image, pdfContext);
460 indexed = true;
461 cnt = (int)array->objAtAIndex(2)->intValue() + 1;
462 if (cnt > 256) {
463 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
464 "Color space feature NYI, cnt > 256", image, pdfContext);
465 return NULL;
466 }
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);
471 return NULL;
472 }
473 for (int i = 0 ; i < cnt; i++) {
474 colors[i] = SkPreMultiplyARGB(0xff,
475 data.fBuffer[3 * i],
476 data.fBuffer[3 * i + 1],
477 data.fBuffer[3 * i + 2]);
478 }
479 }
480 }
481
482 // TODO(edisonn): implement image masks.
483 /* bool imageMask = image->imageMask();
484 if (imageMask) {
485 if (bpc != 0 && bpc != 1) {
486 // TODO(edisonn): report warning to be used in testing.
487 return SkBitmap();
488 }
489 bpc = 1;
490 }
491 */
492
493 const unsigned char* uncompressedStream = NULL;
494 size_t uncompressedStreamLength = 0;
495
496 SkPdfStream* stream = (SkPdfStream*)image;
497
498 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
499 uncompressedStream == NULL || uncompressedStreamLength == 0) {
500 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream", stream,
501 SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
502 return NULL;
503 }
504
505 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
506
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);
517 return bitmap;
518 }
519
520 // TODO(edisonn): assumes RGB for now, since it is the only one implemented
521 if (indexed) {
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,
527 NULL, NULL);
528 return bitmap;
529 }
530
531 int bytesPerLine = (int)(uncompressedStreamLength / height);
532 #ifdef PDF_TRACE
533 if (uncompressedStreamLength % height != 0) {
534 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n");
535 }
536 #endif
537
538 SkBitmap* bitmap = transferImageStreamToBitmap(
539 (unsigned char*)uncompressedStream, uncompressedStreamLength,
540 (int)width, (int)height, bytesPerLine,
541 (int)bpc, colorSpace,
542 transparencyMask);
543
544 return bitmap;
545 }
546
getImageFromObject(SkPdfContext * pdfContext,SkPdfImageDictionary * image,bool transparencyMask)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);
553 }
554 return (SkBitmap*) image->data(SkPdfNativeObject::kBitmap_Data);
555 } else {
556 return getImageFromObjectCore(pdfContext, image, transparencyMask);
557 }
558 }
559
getSmaskFromObject(SkPdfContext * pdfContext,SkPdfImageDictionary * obj)560 static SkBitmap* getSmaskFromObject(SkPdfContext* pdfContext, SkPdfImageDictionary* obj) {
561 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
562
563 if (sMask) {
564 return getImageFromObject(pdfContext, sMask, true);
565 }
566
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);
570
571 return pdfContext->fGraphicsState.fSMask;
572 }
573
doXObject_Image(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfImageDictionary * skpdfimage)574 static SkPdfResult doXObject_Image(SkPdfContext* pdfContext, SkCanvas* canvas,
575 SkPdfImageDictionary* skpdfimage) {
576 if (skpdfimage == NULL) {
577 return kIgnoreError_SkPdfResult;
578 }
579
580 SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
581 SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
582
583 canvas->save();
584 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
585
586 SkScalar z = SkIntToScalar(0);
587 SkScalar one = SkIntToScalar(1);
588
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)};
593 SkMatrix flip;
594 SkAssertResult(flip.setPolyToPoly(from, to, 4));
595 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
596 solveImageFlip.preConcat(flip);
597 canvas->setMatrix(solveImageFlip);
598
599 #ifdef PDF_TRACE
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()));
606 }
607 printf("\n");
608 #endif // PDF_TRACE
609
610 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0),
611 SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
612
613 // TODO(edisonn): soft mask type? alpha/luminosity.
614 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
615 "implement soft mask type", skpdfimage, pdfContext);
616
617 SkPaint paint;
618 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
619
620 if (!sMask || sMask->empty()) {
621 canvas->drawBitmapRect(*image, dst, &paint);
622 } else {
623 canvas->saveLayer(&dst, &paint);
624 canvas->drawBitmapRect(*image, dst, NULL);
625 SkPaint xfer;
626 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode);
627 canvas->drawBitmapRect(*sMask, dst, &xfer);
628 canvas->restore();
629 }
630
631 canvas->restore();
632
633 return kPartial_SkPdfResult;
634 }
635
636 //TODO(edisonn): options for implementing isolation and knockout
637 // 1) emulate them (current solution)
638 // PRO: simple
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
646 // PRO: simple
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
649 // PRO: simple, fast
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
655
doGroup_before(SkPdfContext * pdfContext,SkCanvas * canvas,SkRect bbox,SkPdfTransparencyGroupDictionary * tgroup,bool page)656 static void doGroup_before(SkPdfContext* pdfContext, SkCanvas* canvas, SkRect bbox,
657 SkPdfTransparencyGroupDictionary* tgroup, bool page) {
658 SkRect bboxOrig = bbox;
659 SkBitmap backdrop;
660 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc);
661 // bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc);
662 SkPaint paint;
663 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
664 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL);
665 }
666
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) {
670 // if not isolated
671 // canvas->drawBitmapRect(backdrop, bboxOrig, NULL);
672 //}
673
doXObject_Form(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfType1FormDictionary * skobj)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;
680 }
681
682 if (!skobj->has_BBox()) {
683 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox",
684 skobj, pdfContext);
685 return kIgnoreError_SkPdfResult;
686 }
687
688 PdfOp_q(pdfContext, canvas, NULL);
689
690
691 if (skobj->Resources(pdfContext->fPdfDoc)) {
692 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
693 }
694
695 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
696
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
704 }
705
706 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
707 pdfContext->fGraphicsState.fContentStreamMatrix = pdfContext->fGraphicsState.fCTM;
708
709 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
710
711 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
712 // TODO(edisonn): constants (AA) from settings.
713 canvas->clipRect(bbox, SkRegion::kIntersect_Op, false);
714
715 // This is a group?
716 if (skobj->has_Group()) {
717 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdfDoc);
718 doGroup_before(pdfContext, canvas, bbox, tgroup, false);
719 }
720
721 SkPdfStream* stream = (SkPdfStream*)skobj;
722
723 pdfContext->parseStream(stream, canvas);
724
725 if (skobj->has_Group()) {
726 canvas->restore();
727 }
728
729 PdfOp_Q(pdfContext, canvas, NULL);
730 return kPartial_SkPdfResult;
731 }
732
doXObject_Pattern(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfType1PatternDictionary * skobj)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;
739 }
740
741 if (!skobj->has_BBox()) {
742 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingRequiredKey_SkPdfIssue, "BBox",
743 skobj, pdfContext);
744 return kIgnoreError_SkPdfResult;
745 }
746
747 PdfOp_q(pdfContext, canvas, NULL);
748
749
750 if (skobj->Resources(pdfContext->fPdfDoc)) {
751 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
752 }
753
754 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Current Content stream matrix");
755
756 if (skobj->has_Matrix()) {
757 pdfContext->fGraphicsState.fContentStreamMatrix.preConcat(
758 skobj->Matrix(pdfContext->fPdfDoc));
759 }
760
761 SkTraceMatrix(pdfContext->fGraphicsState.fContentStreamMatrix, "Total Content stream matrix");
762
763 canvas->setMatrix(pdfContext->fGraphicsState.fContentStreamMatrix);
764 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fContentStreamMatrix;
765
766 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc);
767 // TODO(edisonn): constants (AA) from settings.
768 canvas->clipRect(bbox, SkRegion::kIntersect_Op, false);
769
770 SkPdfStream* stream = (SkPdfStream*)skobj;
771
772 pdfContext->parseStream(stream, canvas);
773
774 PdfOp_Q(pdfContext, canvas, NULL);
775 return kPartial_SkPdfResult;
776 }
777
778 // TODO(edisonn): PS NYI
779 //static SkPdfResult doXObject_PS(SkPdfContext* pdfContext, SkCanvas* canvas,
780 // const SkPdfNativeObject* obj) {
781 // return kNYI_SkPdfResult;
782 //}
783
doType3Char(SkPdfContext * pdfContext,SkCanvas * canvas,const SkPdfNativeObject * skobj,SkRect bBox,SkMatrix matrix,double textSize)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;
790 }
791
792 PdfOp_q(pdfContext, canvas, NULL);
793
794 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
795 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize),
796 SkDoubleToScalar(textSize));
797 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrixTm;
798
799 pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
800 pdfContext->fGraphicsState.fCTM.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
801
802 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
803
804 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
805
806 SkRect rm = bBox;
807 pdfContext->fGraphicsState.fCTM.mapRect(&rm);
808
809 SkTraceRect(rm, "bbox mapped");
810
811 // TODO(edisonn): constants (AA) from settings.
812 canvas->clipRect(bBox, SkRegion::kIntersect_Op, false);
813
814 SkPdfStream* stream = (SkPdfStream*)skobj;
815
816 pdfContext->parseStream(stream, canvas);
817
818 PdfOp_Q(pdfContext, canvas, NULL);
819
820 return kPartial_SkPdfResult;
821 }
822
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;
827 public:
CheckRecursiveRendering(SkPdfNativeObject * obj)828 CheckRecursiveRendering(SkPdfNativeObject* obj) : fObj(obj) {
829 SkASSERT(!obj->inRendering());
830 obj->startRendering();
831 }
832
~CheckRecursiveRendering()833 ~CheckRecursiveRendering() {
834 SkASSERT(fObj->inRendering());
835 fObj->doneRendering();
836 }
837
IsInRendering(const SkPdfNativeObject * obj)838 static bool IsInRendering(const SkPdfNativeObject* obj) {
839 return obj->inRendering();
840 }
841 };
842
doXObject(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfNativeObject * obj)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;
848 }
849
850 CheckRecursiveRendering checkRecursion(obj);
851
852 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj))
853 {
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());
860 default: {
861 if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) !=
862 kNone_SkPdfNativeObjectType) {
863 SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj;
864 return doXObject_Pattern(pdfContext, canvas, pattern);
865 }
866 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "doXObject",
867 obj, pdfContext);
868 }
869 }
870 return kIgnoreError_SkPdfResult;
871 }
872
doPage(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfPageObjectDictionary * skobj)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;
879 }
880
881 SkPdfStream* stream = skobj->getContentsAsStream(pdfContext->fPdfDoc);
882
883 if (!stream) {
884 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "Missing stream",
885 skobj, SkPdfNativeObject::_kStream_PdfObjectType, pdfContext);
886 return kIgnoreError_SkPdfResult;
887 }
888
889 // FIXME (scroggo): renderPage also sets fResources. Are these redundant?
890 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
891
892 if (!pdfContext->fGraphicsState.fResources) {
893 // It might be null because we have not implemented yet inheritance.
894 return kIgnoreError_SkPdfResult;
895 }
896
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;
901 }
902 CheckRecursiveRendering checkRecursion(skobj);
903
904
905 // FIXME (scroggo): Is this save necessary? May be needed for rendering a nested PDF.
906 PdfOp_q(pdfContext, canvas, NULL);
907
908 // TODO(edisonn): MediaBox can be inherited!!!!
909 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MediaBox inheritance NYI",
910 NULL, pdfContext);
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);
915 } else {
916 canvas->save();
917 }
918
919 pdfContext->parseStream(stream, canvas);
920
921 canvas->restore();
922 PdfOp_Q(pdfContext, canvas, NULL);
923 return kPartial_SkPdfResult;
924 }
925
PdfOp_q(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)926 SkPdfResult PdfOp_q(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
927 pdfContext->fStateStack.push(pdfContext->fGraphicsState);
928 canvas->save();
929 pdfContext->fObjectStack.nest();
930 return kOK_SkPdfResult;
931 }
932
PdfOp_Q(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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();
937 canvas->restore();
938
939 if (pdfContext->fObjectStack.nestingLevel() == 0) {
940 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackNestingOverflow_SkPdfIssue,
941 "stack nesting overflow (q/Q)", NULL, pdfContext);
942 return kIgnoreError_SkPdfResult;
943 } else {
944 pdfContext->fObjectStack.unnest();
945 }
946 } else {
947 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kStackOverflow_SkPdfIssue,
948 "stack overflow (q/Q)", NULL, pdfContext);
949 return kIgnoreError_SkPdfResult;
950 }
951
952 return kOK_SkPdfResult;
953 }
954
PdfOp_cm(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
963 CHECK_PARAMETERS();
964 double array[6] = {a, b, c, d, e, f};
965
966 // a b
967 // c d
968 // e f
969
970 // 0 1
971 // 2 3
972 // 4 5
973
974 // sx ky
975 // kx sy
976 // tx ty
977 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
978
979 pdfContext->fGraphicsState.fCTM.preConcat(matrix);
980
981 #ifdef PDF_TRACE
982 printf("cm ");
983 for (int i = 0 ; i < 6 ; i++) {
984 printf("%f ", array[i]);
985 }
986 printf("\n");
987 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
988 #endif
989
990 return kOK_SkPdfResult;
991 }
992
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.
PdfOp_TL(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)996 static SkPdfResult PdfOp_TL(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
997 EXPECT_OPERANDS("TL", pdfContext, 1);
998 POP_NUMBER(pdfContext, ty);
999 CHECK_PARAMETERS();
1000
1001 pdfContext->fGraphicsState.fTextLeading = ty;
1002
1003 return kOK_SkPdfResult;
1004 }
1005
PdfOp_Td(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
1010 CHECK_PARAMETERS();
1011
1012 double array[6] = {1, 0, 0, 1, tx, -ty};
1013 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1014
1015 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1016 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix);
1017
1018 return kPartial_SkPdfResult;
1019 }
1020
PdfOp_TD(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper * parentLooper)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);
1026 CHECK_PARAMETERS();
1027
1028 // TODO(edisonn): Create factory methods or constructors so native is hidden
1029 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty);
1030 pdfContext->fObjectStack.push(_ty);
1031
1032 PdfOp_TL(pdfContext, canvas, parentLooper);
1033
1034 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx);
1035 pdfContext->fObjectStack.push(vtx);
1036
1037 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty);
1038 pdfContext->fObjectStack.push(vty);
1039
1040 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, parentLooper);
1041
1042 return ret;
1043 }
1044
PdfOp_Tm(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
1053 CHECK_PARAMETERS();
1054
1055 double array[6];
1056 array[0] = a;
1057 array[1] = b;
1058 array[2] = c;
1059 array[3] = d;
1060 array[4] = e;
1061 array[5] = f;
1062
1063 SkMatrix matrix = SkMatrixFromPdfMatrix(array);
1064 matrix.postConcat(pdfContext->fGraphicsState.fCTM);
1065 matrix.preScale(SkDoubleToScalar(1), SkDoubleToScalar(-1));
1066
1067 // TODO(edisonn): NYI - Text positioning.
1068 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
1069 "Text positioning not implemented for 2+ chars", NULL, pdfContext);
1070
1071 pdfContext->fGraphicsState.fMatrixTm = matrix;
1072 pdfContext->fGraphicsState.fMatrixTlm = matrix;
1073
1074 return kPartial_SkPdfResult;
1075 }
1076
1077 //— T* Move to the start of the next line. This operator has the same effect as the code
1078 //0 Tl Td
1079 //where Tl is the current leading parameter in the text state
PdfOp_T_star(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper * parentLooper)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);
1084
1085 pdfContext->fObjectStack.push(zero);
1086 pdfContext->fObjectStack.push(tl);
1087
1088 SkPdfResult ret = PdfOp_Td(pdfContext, canvas, parentLooper);
1089
1090 return ret;
1091 }
1092
PdfOp_m(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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;
1097 }
1098
1099 EXPECT_OPERANDS("m", pdfContext, 2);
1100 POP_NUMBER(pdfContext, y);
1101 POP_NUMBER(pdfContext, x);
1102 CHECK_PARAMETERS();
1103
1104 pdfContext->fGraphicsState.fCurPosY = y;
1105 pdfContext->fGraphicsState.fCurPosX = x;
1106
1107 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1108 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1109
1110 return kOK_SkPdfResult;
1111 }
1112
PdfOp_l(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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;
1117 }
1118
1119 EXPECT_OPERANDS("l", pdfContext, 2);
1120 POP_NUMBER(pdfContext, y);
1121 POP_NUMBER(pdfContext, x);
1122 CHECK_PARAMETERS();
1123
1124 pdfContext->fGraphicsState.fCurPosY = y;
1125 pdfContext->fGraphicsState.fCurPosX = x;
1126
1127 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosX),
1128 SkDoubleToScalar(pdfContext->fGraphicsState.fCurPosY));
1129
1130 return kOK_SkPdfResult;
1131 }
1132
PdfOp_c(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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;
1137 }
1138
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);
1146 CHECK_PARAMETERS();
1147
1148 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1149 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1150 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1151
1152 pdfContext->fGraphicsState.fCurPosX = x3;
1153 pdfContext->fGraphicsState.fCurPosY = y3;
1154
1155 return kOK_SkPdfResult;
1156 }
1157
PdfOp_v(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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;
1162 }
1163
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);
1169 CHECK_PARAMETERS();
1170
1171 double y1 = pdfContext->fGraphicsState.fCurPosY;
1172 double x1 = pdfContext->fGraphicsState.fCurPosX;
1173
1174 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1175 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1176 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1177
1178 pdfContext->fGraphicsState.fCurPosX = x3;
1179 pdfContext->fGraphicsState.fCurPosY = y3;
1180
1181 return kOK_SkPdfResult;
1182 }
1183
PdfOp_y(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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;
1188 }
1189
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);
1195 CHECK_PARAMETERS();
1196
1197 double y2 = pdfContext->fGraphicsState.fCurPosY;
1198 double x2 = pdfContext->fGraphicsState.fCurPosX;
1199
1200 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
1201 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1202 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
1203
1204 pdfContext->fGraphicsState.fCurPosX = x3;
1205 pdfContext->fGraphicsState.fCurPosY = y3;
1206
1207 return kOK_SkPdfResult;
1208 }
1209
PdfOp_re(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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;
1214 }
1215
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);
1221 CHECK_PARAMETERS();
1222
1223 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x),
1224 SkDoubleToScalar(y),
1225 SkDoubleToScalar(x + width),
1226 SkDoubleToScalar(y + height));
1227
1228 pdfContext->fGraphicsState.fCurPosX = x;
1229 pdfContext->fGraphicsState.fCurPosY = y + height;
1230
1231 return kOK_SkPdfResult;
1232 }
1233
PdfOp_h(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1234 static SkPdfResult PdfOp_h(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1235 pdfContext->fGraphicsState.fPath.close();
1236 return kOK_SkPdfResult;
1237 }
1238
PdfOp_fillAndStroke(SkPdfContext * pdfContext,SkCanvas * canvas,bool fill,bool stroke,bool close,bool evenOdd)1239 static SkPdfResult PdfOp_fillAndStroke(SkPdfContext* pdfContext, SkCanvas* canvas,
1240 bool fill, bool stroke, bool close, bool evenOdd) {
1241 SkPath path = pdfContext->fGraphicsState.fPath;
1242
1243 if (close) {
1244 path.close();
1245 }
1246
1247 canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
1248
1249 SkPaint paint;
1250
1251 SkPoint line[2];
1252 if (fill && !stroke && path.isLine(line)) {
1253 paint.setStyle(SkPaint::kStroke_Style);
1254
1255 // TODO(edisonn): implement this with patterns
1256 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1257 paint.setStrokeWidth(SkDoubleToScalar(0));
1258
1259 canvas->drawPath(path, paint);
1260 } else {
1261 if (fill) {
1262 if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer,
1263 "Pattern", strlen("Pattern")) == 0 &&
1264 pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) {
1265
1266 // TODO(edisonn): we can use a shader here, like imageshader to draw fast.
1267
1268 PdfOp_q(pdfContext, canvas, NULL);
1269
1270 if (evenOdd) {
1271 path.setFillType(SkPath::kEvenOdd_FillType);
1272 }
1273 canvas->clipPath(path);
1274
1275 if (pdfContext->fPdfDoc
1276 ->mapper()
1277 ->mapType1PatternDictionary(pdfContext->fGraphicsState
1278 .fNonStroking
1279 .fPattern)
1280 != kNone_SkPdfNativeObjectType) {
1281 SkPdfType1PatternDictionary* pattern
1282 = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState
1283 .fNonStroking
1284 .fPattern;
1285
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));
1293
1294 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
1295 "paterns x/y step is forced to positive number",
1296 pattern, pdfContext);
1297
1298 SkRect bounds = path.getBounds();
1299 bounds.sort();
1300
1301 SkScalar x;
1302 SkScalar y;
1303
1304 y = bounds.top();
1305 int totalx = 0;
1306 int totaly = 0;
1307 while (y < bounds.bottom()) {
1308 x = bounds.left();
1309 totalx = 0;
1310
1311 while (x < bounds.right()) {
1312 doXObject(pdfContext, canvas, pattern);
1313
1314 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1315 SkIntToScalar(xStep), SkIntToScalar(0));
1316 totalx += xStep;
1317 x += SkIntToScalar(xStep);
1318 }
1319 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1320 SkIntToScalar(-totalx), SkIntToScalar(0));
1321
1322 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1323 SkIntToScalar(0), SkIntToScalar(-yStep));
1324 totaly += yStep;
1325 y += SkIntToScalar(yStep);
1326 }
1327 pdfContext->fGraphicsState.fContentStreamMatrix.preTranslate(
1328 SkIntToScalar(0), SkIntToScalar(totaly));
1329 }
1330 }
1331
1332 PdfOp_Q(pdfContext, canvas, NULL);
1333 } else {
1334 paint.setStyle(SkPaint::kFill_Style);
1335 if (evenOdd) {
1336 path.setFillType(SkPath::kEvenOdd_FillType);
1337 }
1338
1339 pdfContext->fGraphicsState.applyGraphicsState(&paint, false);
1340
1341 canvas->drawPath(path, paint);
1342 }
1343 }
1344
1345 if (stroke) {
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);
1350
1351 paint.setColor(SK_ColorGREEN);
1352
1353 // reset it, just in case it messes up the stroke
1354 path.setFillType(SkPath::kWinding_FillType);
1355 canvas->drawPath(path, paint);
1356 } else {
1357 paint.setStyle(SkPaint::kStroke_Style);
1358
1359 pdfContext->fGraphicsState.applyGraphicsState(&paint, true);
1360
1361 // reset it, just in case it messes up the stroke
1362 path.setFillType(SkPath::kWinding_FillType);
1363 canvas->drawPath(path, paint);
1364 }
1365 }
1366 }
1367
1368 pdfContext->fGraphicsState.fPath.reset();
1369 // TODO(edisonn): implement scale/zoom
1370
1371 if (pdfContext->fGraphicsState.fHasClipPathToApply) {
1372 #ifndef PDF_DEBUG_NO_CLIPING
1373 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
1374 #endif
1375 }
1376
1377 //pdfContext->fGraphicsState.fClipPath.reset();
1378 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1379
1380 return kOK_SkPdfResult;
1381
1382 }
1383
PdfOp_S(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1384 static SkPdfResult PdfOp_S(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1385 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false);
1386 }
1387
PdfOp_s(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1388 static SkPdfResult PdfOp_s(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1389 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false);
1390 }
1391
PdfOp_F(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1392 static SkPdfResult PdfOp_F(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1393 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1394 }
1395
PdfOp_f(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1396 static SkPdfResult PdfOp_f(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1397 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false);
1398 }
1399
PdfOp_f_star(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1400 static SkPdfResult PdfOp_f_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1401 SkPdfTokenLooper*) {
1402 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true);
1403 }
1404
PdfOp_B(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1405 static SkPdfResult PdfOp_B(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1406 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false);
1407 }
1408
PdfOp_B_star(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1409 static SkPdfResult PdfOp_B_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1410 SkPdfTokenLooper*) {
1411 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true);
1412 }
1413
PdfOp_b(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1414 static SkPdfResult PdfOp_b(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1415 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false);
1416 }
1417
PdfOp_b_star(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1418 static SkPdfResult PdfOp_b_star(SkPdfContext* pdfContext, SkCanvas* canvas,
1419 SkPdfTokenLooper*) {
1420 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true);
1421 }
1422
PdfOp_n(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
1428 #endif
1429 }
1430
1431 pdfContext->fGraphicsState.fHasClipPathToApply = false;
1432
1433 pdfContext->fGraphicsState.fPathClosed = true;
1434
1435 return kOK_SkPdfResult;
1436 }
1437
PdfOp_BT(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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;
1444
1445 return kPartial_SkPdfResult;
1446 }
1447
PdfOp_ET(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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,
1451 pdfContext);
1452
1453 return kIgnoreError_SkPdfResult;
1454 }
1455
1456 pdfContext->fGraphicsState.fTextBlock = false;
1457
1458 // TODO(edisonn): anything else to be done once we are done with draw text? Like restore stack?
1459 return kOK_SkPdfResult;
1460 }
1461
skpdfGraphicsStateApplyFontCore(SkPdfContext * pdfContext,const SkPdfNativeObject * fontName,double fontSize)1462 static SkPdfResult skpdfGraphicsStateApplyFontCore(SkPdfContext* pdfContext,
1463 const SkPdfNativeObject* fontName, double fontSize) {
1464 #ifdef PDF_TRACE
1465 printf("font name: %s\n", fontName->nameValue2().c_str());
1466 #endif
1467
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;
1473 }
1474
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;
1483 }
1484
1485 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont;
1486
1487 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc, fd);
1488
1489 if (skfont) {
1490 pdfContext->fGraphicsState.fSkFont = skfont;
1491 }
1492 pdfContext->fGraphicsState.fCurFontSize = fontSize;
1493 return kOK_SkPdfResult;
1494 }
1495
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.
PdfOp_Tf(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
1505 CHECK_PARAMETERS();
1506
1507 return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
1508 }
1509
PdfOp_Tj(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1510 static SkPdfResult PdfOp_Tj(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1511 EXPECT_OPERANDS("Tj", pdfContext, 1);
1512 POP_STRING(pdfContext, str);
1513 CHECK_PARAMETERS();
1514
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,
1518 pdfContext);
1519 return kIgnoreError_SkPdfResult;
1520 }
1521
1522 SkPdfResult ret = DrawText(pdfContext, str, canvas);
1523
1524 return ret;
1525 }
1526
PdfOp_quote(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper * parentLooper)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;
1534 }
1535
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);
1539 }
1540
PdfOp_doublequote(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper * 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;
1548 }
1549
1550 EXPECT_OPERANDS("\"", pdfContext, 3);
1551 POP_OBJ(pdfContext, str);
1552 POP_OBJ(pdfContext, ac);
1553 POP_OBJ(pdfContext, aw);
1554 CHECK_PARAMETERS();
1555
1556 pdfContext->fObjectStack.push(aw);
1557 PdfOp_Tw(pdfContext, canvas, parentLooper);
1558
1559 pdfContext->fObjectStack.push(ac);
1560 PdfOp_Tc(pdfContext, canvas, parentLooper);
1561
1562 pdfContext->fObjectStack.push(str);
1563 PdfOp_quote(pdfContext, canvas, parentLooper);
1564
1565 return kPartial_SkPdfResult;
1566 }
1567
PdfOp_TJ(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1568 static SkPdfResult PdfOp_TJ(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1569 EXPECT_OPERANDS("Tf", pdfContext, 1);
1570 POP_ARRAY(pdfContext, array);
1571 CHECK_PARAMETERS();
1572
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,
1576 pdfContext);
1577 return kIgnoreError_SkPdfResult;
1578 }
1579
1580 if (!array->isArray()) {
1581 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, array,
1582 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
1583 return kIgnoreError_SkPdfResult;
1584 }
1585
1586 for( int i=0; i<static_cast<int>(array->size()); i++ )
1587 {
1588 if (!(*array)[i]) {
1589 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
1590 "element [i] is null, no element should be null",
1591 array,
1592 SkPdfNativeObject::_kAnyString_PdfObjectType |
1593 SkPdfNativeObject::_kNumber_PdfObjectType,
1594 pdfContext);
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();
1600 SkMatrix matrix;
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));
1611
1612 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
1613 } else {
1614 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", (*array)[i],
1615 SkPdfNativeObject::kArray_PdfObjectType |
1616 SkPdfNativeObject::_kNumber_PdfObjectType,
1617 pdfContext);
1618 }
1619 }
1620 return kPartial_SkPdfResult; // TODO(edisonn): Implement fully DrawText before returing OK.
1621 }
1622
PdfOp_CS_cs(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfColorOperator * colorOperator)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);
1627 CHECK_PARAMETERS();
1628
1629 //Next, get the ColorSpace Dictionary from the Resource Dictionary:
1630 SkPdfDictionary* colorSpaceResource
1631 = pdfContext->fGraphicsState.fResources->ColorSpace(pdfContext->fPdfDoc);
1632
1633 SkPdfNativeObject* colorSpace
1634 = colorSpaceResource ? pdfContext->fPdfDoc
1635 ->resolveReference(colorSpaceResource->get(name)) :
1636 name;
1637
1638 if (colorSpace == NULL) {
1639 colorOperator->fColorSpace = name->strRef();
1640 } else {
1641 #ifdef PDF_TRACE
1642 printf("CS = %s\n", colorSpace->toString(0, 0).c_str());
1643 #endif // PDF_TRACE
1644 if (colorSpace->isName()) {
1645 colorOperator->fColorSpace = colorSpace->strRef();
1646 } else if (colorSpace->isArray()) {
1647 size_t cnt = colorSpace->size();
1648 if (cnt == 0) {
1649 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
1650 "color space has length 0", colorSpace, pdfContext);
1651 return kIgnoreError_SkPdfResult;
1652 }
1653 SkPdfNativeObject* type = colorSpace->objAtAIndex(0);
1654 type = pdfContext->fPdfDoc->resolveReference(type);
1655
1656 if (type->isName("ICCBased")) {
1657 if (cnt != 2) {
1658 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
1659 "ICCBased color space must have an array with 2 elements",
1660 colorSpace, pdfContext);
1661 return kIgnoreError_SkPdfResult;
1662 }
1663 SkPdfNativeObject* prop = colorSpace->objAtAIndex(1);
1664 prop = pdfContext->fPdfDoc->resolveReference(prop);
1665 #ifdef PDF_TRACE
1666 printf("ICCBased prop = %s\n", prop->toString(0, 0).c_str());
1667 #endif // PDF_TRACE
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;
1673 }
1674 return kNYI_SkPdfResult;
1675 }
1676 }
1677 }
1678
1679 return kPartial_SkPdfResult;
1680 }
1681
PdfOp_CS(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1682 static SkPdfResult PdfOp_CS(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1683 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1684 }
1685
PdfOp_cs(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1686 static SkPdfResult PdfOp_cs(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1687 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1688 }
1689
PdfOp_SC_sc(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfColorOperator * colorOperator)1690 static SkPdfResult PdfOp_SC_sc(SkPdfContext* pdfContext, SkCanvas* canvas,
1691 SkPdfColorOperator* colorOperator) {
1692 double c[4];
1693 // int64_t v[4];
1694
1695 int n = GetColorSpaceComponents(colorOperator->fColorSpace);
1696
1697 bool doubles = true;
1698 if (colorOperator->fColorSpace.equals("Indexed")) {
1699 doubles = false;
1700 }
1701
1702 #ifdef PDF_TRACE
1703 printf("color space = %s, N = %i\n", colorOperator->fColorSpace.fBuffer, n);
1704 #endif
1705
1706 EXPECT_OPERANDS("SC/sc", pdfContext, n);
1707
1708 for (int i = n - 1; i >= 0 ; i--) {
1709 if (doubles) {
1710 POP_NUMBER_INTO(pdfContext, c[i]);
1711 // } else {
1712 // v[i] = pdfContext->fObjectStack.top()->intValue(); pdfContext->fObjectStack.pop();
1713 }
1714 }
1715 CHECK_PARAMETERS();
1716
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]),
1723 (U8CPU)(255*c[1]),
1724 (U8CPU)(255*c[2])));
1725 }
1726 return kPartial_SkPdfResult;
1727 }
1728
PdfOp_SC(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1729 static SkPdfResult PdfOp_SC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1730 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1731 }
1732
PdfOp_sc(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1733 static SkPdfResult PdfOp_sc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1734 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1735 }
1736
PdfOp_SCN_scn(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfColorOperator * colorOperator)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();
1741
1742 SkPdfDictionary* patternResources
1743 = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc);
1744
1745 if (patternResources == NULL) {
1746 #ifdef PDF_TRACE
1747 printf("ExtGState is NULL!\n");
1748 #endif
1749 return kIgnoreError_SkPdfResult;
1750 }
1751
1752 colorOperator->setPatternColorSpace(
1753 pdfContext->fPdfDoc->resolveReference(patternResources->get(name)));
1754 }
1755
1756 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec.
1757 PdfOp_SC_sc(pdfContext, canvas, colorOperator);
1758
1759 return kPartial_SkPdfResult;
1760 }
1761
PdfOp_SCN(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1762 static SkPdfResult PdfOp_SCN(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1763 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1764 }
1765
PdfOp_scn(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1766 static SkPdfResult PdfOp_scn(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1767 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1768 }
1769
PdfOp_G_g(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfColorOperator * colorOperator)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);
1774 CHECK_PARAMETERS();
1775
1776 // TODO(edisonn): limit gray in [0, 1]
1777
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)));
1783
1784 return kPartial_SkPdfResult;
1785 }
1786
PdfOp_G(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1787 static SkPdfResult PdfOp_G(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1788 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1789 }
1790
PdfOp_g(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1791 static SkPdfResult PdfOp_g(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1792 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1793 }
1794
PdfOp_RG_rg(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfColorOperator * colorOperator)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);
1801 CHECK_PARAMETERS();
1802
1803 colorOperator->fColorSpace = strings_DeviceRGB;
1804 colorOperator->setRGBColor(SkColorSetRGB((U8CPU)(255*r), (U8CPU)(255*g), (U8CPU)(255*b)));
1805 return kOK_SkPdfResult;
1806 }
1807
PdfOp_RG(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1808 static SkPdfResult PdfOp_RG(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1809 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1810 }
1811
PdfOp_rg(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1812 static SkPdfResult PdfOp_rg(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1813 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1814 }
1815
PdfOp_K_k(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfColorOperator * colorOperator)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);
1824 CHECK_PARAMETERS();
1825
1826 // TODO(edisonn): really silly quick way to remove compiler warning
1827 if (k + y + m + c == 0) {
1828 return kNYI_SkPdfResult;
1829 }
1830
1831 //colorOperator->fColorSpace = strings_DeviceCMYK;
1832 // TODO(edisonn): Set color.
1833 return kNYI_SkPdfResult;
1834 }
1835
PdfOp_K(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1836 static SkPdfResult PdfOp_K(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1837 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking);
1838 }
1839
PdfOp_k(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1840 static SkPdfResult PdfOp_k(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1841 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStroking);
1842 }
1843
PdfOp_W(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1844 static SkPdfResult PdfOp_W(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1845 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1846 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1847
1848 return kOK_SkPdfResult;
1849 }
1850
PdfOp_W_star(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)1851 static SkPdfResult PdfOp_W_star(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
1852 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath;
1853
1854 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType);
1855 pdfContext->fGraphicsState.fHasClipPathToApply = true;
1856
1857 return kOK_SkPdfResult;
1858 }
1859
PdfOp_BX(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper * parentLooper)1860 static SkPdfResult PdfOp_BX(SkPdfContext* pdfContext, SkCanvas* canvas,
1861 SkPdfTokenLooper* parentLooper) {
1862 PdfCompatibilitySectionLooper looper(parentLooper);
1863 looper.loop();
1864 return kOK_SkPdfResult;
1865 }
1866
PdfOp_EX(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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",
1871 NULL, pdfContext);
1872
1873 return kIgnoreError_SkPdfResult;
1874 }
1875
PdfOp_BI(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper * parentLooper)1876 static SkPdfResult PdfOp_BI(SkPdfContext* pdfContext, SkCanvas* canvas,
1877 SkPdfTokenLooper* parentLooper) {
1878 PdfInlineImageLooper looper(parentLooper);
1879 looper.loop();
1880 return kOK_SkPdfResult;
1881 }
1882
PdfOp_ID(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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",
1887 NULL, pdfContext);
1888 return kIgnoreError_SkPdfResult;
1889 }
1890
PdfOp_EI(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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",
1895 NULL, pdfContext);
1896 return kIgnoreError_SkPdfResult;
1897 }
1898
skpdfGraphicsStateApply_ca(SkPdfContext * pdfContext,double ca)1899 static SkPdfResult skpdfGraphicsStateApply_ca(SkPdfContext* pdfContext, double ca) {
1900 pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
1901 return kOK_SkPdfResult;
1902 }
1903
skpdfGraphicsStateApply_CA(SkPdfContext * pdfContext,double CA)1904 static SkPdfResult skpdfGraphicsStateApply_CA(SkPdfContext* pdfContext, double CA) {
1905 pdfContext->fGraphicsState.fStroking.fOpacity = CA;
1906 return kOK_SkPdfResult;
1907 }
1908
skpdfGraphicsStateApplyLW(SkPdfContext * pdfContext,double lineWidth)1909 static SkPdfResult skpdfGraphicsStateApplyLW(SkPdfContext* pdfContext, double lineWidth) {
1910 pdfContext->fGraphicsState.fLineWidth = lineWidth;
1911 return kOK_SkPdfResult;
1912 }
1913
skpdfGraphicsStateApplyLC(SkPdfContext * pdfContext,int64_t lineCap)1914 static SkPdfResult skpdfGraphicsStateApplyLC(SkPdfContext* pdfContext, int64_t lineCap) {
1915 pdfContext->fGraphicsState.fLineCap = (int)lineCap;
1916 return kOK_SkPdfResult;
1917 }
1918
skpdfGraphicsStateApplyLJ(SkPdfContext * pdfContext,int64_t lineJoin)1919 static SkPdfResult skpdfGraphicsStateApplyLJ(SkPdfContext* pdfContext, int64_t lineJoin) {
1920 pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
1921 return kOK_SkPdfResult;
1922 }
1923
skpdfGraphicsStateApplyML(SkPdfContext * pdfContext,double miterLimit)1924 static SkPdfResult skpdfGraphicsStateApplyML(SkPdfContext* pdfContext, double miterLimit) {
1925 pdfContext->fGraphicsState.fMiterLimit = miterLimit;
1926 return kOK_SkPdfResult;
1927 }
1928
1929 // TODO(edisonn): test all dashing rules, not sure if they work as in skia.
1930 /*
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, …
1937 */
1938
skpdfGraphicsStateApplyD(SkPdfContext * pdfContext,SkPdfArray * intervals,SkPdfNativeObject * phase)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;
1945 }
1946
1947 if (phase == NULL) {
1948 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, phase,
1949 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
1950 return kIgnoreError_SkPdfResult;
1951 }
1952
1953 int cnt = (int) intervals->size();
1954 if (cnt >= 256) {
1955 // TODO(edisonn): alloc memory
1956 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue,
1957 "dash array size unssuported, cnt > 256", intervals, pdfContext);
1958 return kIgnoreError_SkPdfResult;
1959 }
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;
1966 }
1967 }
1968
1969 double total = 0;
1970 for (int i = 0 ; i < cnt; i++) {
1971 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
1972 total += pdfContext->fGraphicsState.fDashArray[i];
1973 }
1974 if (cnt & 1) {
1975 if (cnt == 1) {
1976 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsState.fDashArray[0];
1977 cnt++;
1978 } else {
1979 // TODO(edisonn): report error/warning
1980 return kNYI_SkPdfResult;
1981 }
1982 }
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);
1988 }
1989
1990 return kOK_SkPdfResult;
1991 }
1992
skpdfGraphicsStateApplyD(SkPdfContext * pdfContext,SkPdfArray * dash)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;
1998 }
1999
2000 if (dash->size() != 2) {
2001 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
2002 "hash array must have 2 elements", dash, pdfContext);
2003 return kIgnoreError_SkPdfResult;
2004 }
2005
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;
2010 }
2011
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;
2016 }
2017
2018 return skpdfGraphicsStateApplyD(pdfContext, (SkPdfArray*)dash->objAtAIndex(0),
2019 dash->objAtAIndex(1));
2020 }
2021
skpdfGraphicsStateApplyFont(SkPdfContext * pdfContext,SkPdfArray * fontAndSize)2022 static void skpdfGraphicsStateApplyFont(SkPdfContext* pdfContext, SkPdfArray* fontAndSize) {
2023 if (!fontAndSize || !fontAndSize->isArray()) {
2024 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, fontAndSize,
2025 SkPdfNativeObject::kArray_PdfObjectType, pdfContext);
2026 return;
2027 }
2028
2029 if (fontAndSize->size() != 2) {
2030 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kIncostistentSizes_SkPdfIssue,
2031 "font array must have 2 elements", fontAndSize, pdfContext);
2032 return;
2033 }
2034
2035 if (!fontAndSize->objAtAIndex(0) || !fontAndSize->objAtAIndex(0)->isName()) {
2036 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2037 fontAndSize->objAtAIndex(0),
2038 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
2039 return;
2040 }
2041
2042
2043 if (!fontAndSize->objAtAIndex(1) || !fontAndSize->objAtAIndex(1)->isNumber()) {
2044 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2045 fontAndSize->objAtAIndex(0),
2046 SkPdfNativeObject::_kNumber_PdfObjectType, pdfContext);
2047 return;
2048 }
2049
2050 skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0),
2051 fontAndSize->objAtAIndex(1)->numberValue());
2052 }
2053
2054
2055 //lineWidth w Set the line width in the graphics state (see “Line Width” on page 152).
PdfOp_w(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2056 static SkPdfResult PdfOp_w(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2057 EXPECT_OPERANDS("w", pdfContext, 1);
2058 POP_NUMBER(pdfContext, lw);
2059 CHECK_PARAMETERS();
2060
2061 return skpdfGraphicsStateApplyLW(pdfContext, lw);
2062 }
2063
2064 //lineCap J Set the line cap style in the graphics state (see “Line Cap Style” on page 153).
PdfOp_J(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
2069 CHECK_PARAMETERS();
2070
2071 return skpdfGraphicsStateApplyLC(pdfContext, (int)lc);
2072 }
2073
2074 //lineJoin j Set the line join style in the graphics state (see “Line Join Style” on page 153).
PdfOp_j(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
2079 CHECK_PARAMETERS();
2080
2081 return skpdfGraphicsStateApplyLJ(pdfContext, (int)lj);
2082 }
2083
2084 //miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on page 153).
PdfOp_M(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2085 static SkPdfResult PdfOp_M(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2086 EXPECT_OPERANDS("M", pdfContext, 1);
2087 POP_NUMBER(pdfContext, ml);
2088 CHECK_PARAMETERS();
2089 return skpdfGraphicsStateApplyML(pdfContext, ml);
2090 }
2091
2092 //dashArray dashPhase d Set the line dash pattern in the graphics state (see “Line Dash Pattern” on
2093 //page 155).
PdfOp_d(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
2098 CHECK_PARAMETERS();
2099
2100 return skpdfGraphicsStateApplyD(pdfContext, array, phase);
2101 }
2102
2103 //intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see “Rendering Intents”
2104 // on page 197).
PdfOp_ri(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2105 static SkPdfResult PdfOp_ri(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2106 pdfContext->fObjectStack.pop();
2107
2108 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "render intent NYI", NULL,
2109 pdfContext);
2110
2111 return kNYI_SkPdfResult;
2112 }
2113
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.
PdfOp_i(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2117 static SkPdfResult PdfOp_i(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2118 EXPECT_OPERANDS("i", pdfContext, 1);
2119 POP_NUMBER(pdfContext, flatness);
2120 CHECK_PARAMETERS();
2121
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;
2126 }
2127
2128 return kNYI_SkPdfResult;
2129 }
2130
2131 SkTDict<SkXfermode::Mode> gPdfBlendModes(20);
2132
2133 class InitBlendModes {
2134 public:
InitBlendModes()2135 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);
2150
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);
2156 }
2157 };
2158
2159 InitBlendModes _gDummyInniter;
2160
xferModeFromBlendMode(const char * blendMode,size_t len)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)) {
2164 return mode;
2165 }
2166
2167 return (SkXfermode::Mode)(SkXfermode::kLastMode + 1);
2168 }
2169
skpdfGraphicsStateApplyBM_name(SkPdfContext * pdfContext,const SkString & blendMode)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;
2175 } else {
2176 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue,
2177 blendMode.c_str(), NULL, pdfContext);
2178 }
2179 }
2180
skpdfGraphicsStateApplyBM_array(SkPdfContext * pdfContext,SkPdfArray * blendModes)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);
2185 return;
2186 }
2187
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);
2191 return;
2192 }
2193
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);
2201 return;
2202 }
2203 SkXfermode::Mode mode = xferModeFromBlendMode(name->c_str(), name->lenstr());
2204 if (mode > SkXfermode::kLastMode) {
2205 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kUnknownBlendMode_SkPdfIssue, NULL, name,
2206 pdfContext);
2207 return;
2208 }
2209 }
2210
2211 pdfContext->fGraphicsState.fBlendModesLength = cnt;
2212 for (int i = 0; i < cnt; i++) {
2213 pdfContext->fGraphicsState.fBlendModes[i] = modes[i];
2214 }
2215 }
2216
skpdfGraphicsStateApplySMask_dict(SkPdfContext * pdfContext,SkPdfDictionary * sMask)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);
2221 return;
2222 }
2223
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);
2229 } else {
2230 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
2231 "Dictionary must be SoftMask, or SoftMaskImage",
2232 sMask, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
2233 }
2234 }
2235
skpdfGraphicsStateApplySMask_name(SkPdfContext * pdfContext,const SkString & sMask)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;
2240 return;
2241 }
2242
2243 SkPdfDictionary* extGStateDictionary
2244 = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2245
2246 if (extGStateDictionary == NULL) {
2247 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL,
2248 pdfContext->fGraphicsState.fResources, pdfContext);
2249 return;
2250 }
2251
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);
2257 return;
2258 }
2259
2260 pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
2261 pdfContext->fGraphicsState.fSMask = NULL;
2262
2263 skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
2264 }
2265
skpdfGraphicsStateApplyAIS(SkPdfContext * pdfContext,bool alphaSource)2266 static void skpdfGraphicsStateApplyAIS(SkPdfContext* pdfContext, bool alphaSource) {
2267 pdfContext->fGraphicsState.fAlphaSource = alphaSource;
2268 }
2269
2270
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).
PdfOp_gs(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2274 static SkPdfResult PdfOp_gs(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2275 EXPECT_OPERANDS("gs", pdfContext, 1);
2276 POP_NAME(pdfContext, name);
2277 CHECK_PARAMETERS();
2278
2279 SkPdfDictionary* extGStateDictionary
2280 = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
2281
2282 if (extGStateDictionary == NULL) {
2283 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingExtGState_SkPdfIssue, NULL,
2284 pdfContext->fGraphicsState.fResources, pdfContext);
2285 return kIgnoreError_SkPdfResult;
2286 }
2287
2288 SkPdfNativeObject* value
2289 = pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name));
2290
2291 if (kNone_SkPdfNativeObjectType ==
2292 pdfContext->fPdfDoc->mapper()->mapGraphicsStateDictionary(value)) {
2293 return kIgnoreError_SkPdfResult;
2294 }
2295 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value;
2296
2297 if (gs == NULL) {
2298 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL,
2299 gs, SkPdfNativeObject::kDictionary_PdfObjectType, pdfContext);
2300 return kIgnoreError_SkPdfResult;
2301 }
2302
2303 if (gs->has_LW()) {
2304 skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
2305 }
2306
2307 if (gs->has_LC()) {
2308 skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
2309 }
2310
2311 if (gs->has_LJ()) {
2312 skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
2313 }
2314
2315 if (gs->has_ML()) {
2316 skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
2317 }
2318
2319 if (gs->has_D()) {
2320 skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
2321 }
2322
2323 if (gs->has_Font()) {
2324 skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
2325 }
2326
2327 if (gs->has_BM()) {
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));
2332 } else {
2333 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "wrong type", gs->get("BM"),
2334 SkPdfNativeObject::kArray_PdfObjectType |
2335 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
2336 }
2337 }
2338
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));
2345 } else {
2346 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
2347 "wrong type",
2348 gs->get("BM"),
2349 SkPdfNativeObject::kDictionary_PdfObjectType |
2350 SkPdfNativeObject::kName_PdfObjectType,
2351 pdfContext);
2352 }
2353 }
2354
2355 if (gs->has_ca()) {
2356 skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
2357 }
2358
2359 if (gs->has_CA()) {
2360 skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
2361 }
2362
2363 if (gs->has_AIS()) {
2364 skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
2365 }
2366
2367 // TODO(edisonn): make sure we loaded all those properties in graphic state.
2368
2369 return kOK_SkPdfResult;
2370 }
2371
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.
2375 //Initial value: 0.
PdfOp_Tc(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2376 SkPdfResult PdfOp_Tc(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2377 EXPECT_OPERANDS("Tc", pdfContext, 1);
2378 POP_NUMBER(pdfContext, charSpace);
2379 CHECK_PARAMETERS();
2380
2381 pdfContext->fGraphicsState.fCharSpace = charSpace;
2382
2383 return kOK_SkPdfResult;
2384 }
2385
2386 //wordSpace Tw Set the word spacing, T
2387 //w
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
2390 //value: 0.
PdfOp_Tw(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2391 SkPdfResult PdfOp_Tw(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2392 EXPECT_OPERANDS("Tw", pdfContext, 1);
2393 POP_NUMBER(pdfContext, wordSpace);
2394 CHECK_PARAMETERS();
2395
2396 pdfContext->fGraphicsState.fWordSpace = wordSpace;
2397
2398 return kOK_SkPdfResult;
2399 }
2400
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).
PdfOp_Tz(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2404 static SkPdfResult PdfOp_Tz(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2405 EXPECT_OPERANDS("Tz", pdfContext, 1);
2406 POP_NUMBER(pdfContext, scale);
2407 CHECK_PARAMETERS();
2408
2409 if (scale < 0) {
2410 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2411 "scale must a positive real number", scale_obj, pdfContext);
2412 return kError_SkPdfResult;
2413 }
2414
2415 return kNYI_SkPdfResult;
2416 }
2417
2418 //render Tr Set the text rendering mode, T
2419 //mode, to render, which is an integer. Initial value: 0.
PdfOp_Tr(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2420 static SkPdfResult PdfOp_Tr(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2421 EXPECT_OPERANDS("Tr", pdfContext, 1);
2422 POP_INTEGER(pdfContext, mode);
2423 CHECK_PARAMETERS();
2424
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;
2429 }
2430
2431 return kNYI_SkPdfResult;
2432 }
2433 //rise Ts Set the text rise, Trise, to rise, which is a number expressed in unscaled text space
2434 //units. Initial value: 0.
PdfOp_Ts(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2435 static SkPdfResult PdfOp_Ts(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2436 EXPECT_OPERANDS("Ts", pdfContext, 1);
2437 POP_NUMBER(pdfContext, rise);
2438 CHECK_PARAMETERS();
2439
2440 if (rise < 0) {
2441 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2442 "rise must a positive real number", rise_obj, pdfContext);
2443 return kNYI_SkPdfResult;
2444 }
2445
2446 return kNYI_SkPdfResult;
2447 }
2448
2449 //wx wy d0
PdfOp_d0(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
2454 CHECK_PARAMETERS();
2455
2456 if (wx < 0) {
2457 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2458 "wx must a positive real number", wx_obj, pdfContext);
2459 return kError_SkPdfResult;
2460 }
2461
2462 if (wy < 0) {
2463 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kOutOfRange_SkPdfIssue,
2464 "wy must a positive real number", wy_obj, pdfContext);
2465 return kError_SkPdfResult;
2466 }
2467
2468 return kNYI_SkPdfResult;
2469 }
2470
2471 //wx wy llx lly urx ury d1
PdfOp_d1(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
2480 CHECK_PARAMETERS();
2481
2482 // TODO(edisonn): really silly quick way to remove warning
2483 if (wx + wy + llx + lly + urx + ury) {
2484 return kNYI_SkPdfResult;
2485 }
2486
2487 return kNYI_SkPdfResult;
2488 }
2489
2490 //name sh
PdfOp_sh(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2491 static SkPdfResult PdfOp_sh(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2492 EXPECT_OPERANDS("sh", pdfContext, 1);
2493 POP_NAME(pdfContext, name);
2494 CHECK_PARAMETERS();
2495
2496 if (name == NULL) {
2497 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, name,
2498 SkPdfNativeObject::kName_PdfObjectType, pdfContext);
2499 return kError_SkPdfResult;
2500 }
2501
2502 return kNYI_SkPdfResult;
2503 }
2504
2505 //name Do
PdfOp_Do(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2506 static SkPdfResult PdfOp_Do(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2507 EXPECT_OPERANDS("Do", pdfContext, 1);
2508 POP_NAME(pdfContext, name);
2509 CHECK_PARAMETERS();
2510
2511 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(pdfContext->fPdfDoc);
2512
2513 if (xObject == NULL) {
2514 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kMissingXObject_SkPdfIssue, NULL,
2515 pdfContext->fGraphicsState.fResources, pdfContext);
2516 return kIgnoreError_SkPdfResult;
2517 }
2518
2519 SkPdfNativeObject* value = xObject->get(name);
2520 value = pdfContext->fPdfDoc->resolveReference(value);
2521
2522 return doXObject(pdfContext, canvas, value);
2523 }
2524
2525 //tag MP Designate a marked-content point. tag is a name object indicating the role or
2526 //significance of the point.
PdfOp_MP(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2527 static SkPdfResult PdfOp_MP(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2528 EXPECT_OPERANDS("MP", pdfContext, 1);
2529 POP_OBJ(pdfContext, tag);
2530 CHECK_PARAMETERS();
2531
2532 if (tag == NULL) {
2533 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2534 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2535 return kNYI_SkPdfResult;
2536 }
2537
2538 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "MP NYI", NULL, NULL);
2539 return kNYI_SkPdfResult;
2540 }
2541
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”).
PdfOp_DP(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
2551 CHECK_PARAMETERS();
2552
2553 if (tag == NULL) {
2554 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2555 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2556 return kNYI_SkPdfResult;
2557 }
2558
2559 if (properties == NULL) {
2560 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties,
2561 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2562 return kNYI_SkPdfResult;
2563 }
2564
2565 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "DP NYI", NULL, NULL);
2566 return kNYI_SkPdfResult;
2567 }
2568
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.
PdfOp_BMC(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2571 static SkPdfResult PdfOp_BMC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2572 EXPECT_OPERANDS("BMC", pdfContext, 1);
2573 POP_OBJ(pdfContext, tag);
2574 CHECK_PARAMETERS();
2575
2576 if (tag == NULL) {
2577 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2578 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2579 return kNYI_SkPdfResult;
2580 }
2581
2582 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BMC NYI", NULL, NULL);
2583 return kNYI_SkPdfResult;
2584 }
2585
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”).
PdfOp_BDC(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)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);
2595 CHECK_PARAMETERS();
2596
2597 if (tag == NULL) {
2598 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, tag,
2599 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2600 return kNYI_SkPdfResult;
2601 }
2602
2603 if (properties == NULL) {
2604 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, NULL, properties,
2605 SkPdfNativeObject::_kObject_PdfObjectType, pdfContext);
2606 return kNYI_SkPdfResult;
2607 }
2608
2609 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "BDC NYI", NULL, NULL);
2610 return kNYI_SkPdfResult;
2611 }
2612
2613 //— EMC End a marked-content sequence begun by a BMC or BDC operator.
PdfOp_EMC(SkPdfContext * pdfContext,SkCanvas * canvas,SkPdfTokenLooper *)2614 static SkPdfResult PdfOp_EMC(SkPdfContext* pdfContext, SkCanvas* canvas, SkPdfTokenLooper*) {
2615 SkPdfReport(kCodeWarning_SkPdfIssueSeverity, kNYI_SkPdfIssue, "EMC NYI", NULL, NULL);
2616 return kNYI_SkPdfResult;
2617 }
2618
2619 #include "SkPdfOps.h"
2620
2621 SkTDict<PdfOperatorRenderer> gPdfOps(100);
2622
initPdfOperatorRenderes()2623 static void initPdfOperatorRenderes() {
2624 static bool gInitialized = false;
2625 if (gInitialized) {
2626 return;
2627 }
2628
2629 gPdfOps.set("q", PdfOp_q);
2630 gPdfOps.set("Q", PdfOp_Q);
2631 gPdfOps.set("cm", PdfOp_cm);
2632
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);
2637
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);
2645
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);
2656
2657 gPdfOps.set("BT", PdfOp_BT);
2658 gPdfOps.set("ET", PdfOp_ET);
2659
2660 gPdfOps.set("Tj", PdfOp_Tj);
2661 gPdfOps.set("'", PdfOp_quote);
2662 gPdfOps.set("\"", PdfOp_doublequote);
2663 gPdfOps.set("TJ", PdfOp_TJ);
2664
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);
2677
2678 gPdfOps.set("W", PdfOp_W);
2679 gPdfOps.set("W*", PdfOp_W_star);
2680
2681 gPdfOps.set("BX", PdfOp_BX);
2682 gPdfOps.set("EX", PdfOp_EX);
2683
2684 gPdfOps.set("BI", PdfOp_BI);
2685 gPdfOps.set("ID", PdfOp_ID);
2686 gPdfOps.set("EI", PdfOp_EI);
2687
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);
2696
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);
2704
2705 gPdfOps.set("d0", PdfOp_d0);
2706 gPdfOps.set("d1", PdfOp_d1);
2707
2708 gPdfOps.set("sh", PdfOp_sh);
2709
2710 gPdfOps.set("Do", PdfOp_Do);
2711
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);
2717
2718 gInitialized = true;
2719 }
2720
2721 class InitPdfOps {
2722 public:
InitPdfOps()2723 InitPdfOps() {
2724 initPdfOperatorRenderes();
2725 }
2726 };
2727
2728 InitPdfOps gInitPdfOps;
2729
consumeToken(PdfToken & token)2730 SkPdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) {
2731 SkASSERT(false);
2732 return kIgnoreError_SkPdfResult;
2733 }
2734
loop()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());
2740 }
2741
consumeToken(PdfToken & token)2742 SkPdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) {
2743 return fParent->consumeToken(token);
2744 }
2745
loop()2746 void PdfCompatibilitySectionLooper::loop() {
2747 PdfOp_q(fPdfContext, fCanvas, NULL);
2748
2749 PdfToken token;
2750 while (fTokenizer->readToken(&token)) {
2751 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
2752 PdfCompatibilitySectionLooper looper(this);
2753 looper.loop();
2754 } else {
2755 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX") == 0) {
2756 break;
2757 }
2758 fParent->consumeToken(token);
2759 }
2760 }
2761
2762 PdfOp_Q(fPdfContext, fCanvas, NULL);
2763 }
2764
2765 // TODO(edisonn): for debugging - remove or put it in a #ifdef
2766 SkPdfContext* gPdfContext = NULL;
2767
renderPage(int page,SkCanvas * canvas,const SkRect & dst) const2768 bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas, const SkRect& dst) const {
2769 if (!fPdfDoc) {
2770 return false;
2771 }
2772
2773 if (page < 0 || page >= pages()) {
2774 return false;
2775 }
2776
2777 SkPdfContext pdfContext(fPdfDoc);
2778
2779 // FIXME (scroggo): Is this matrix needed?
2780 pdfContext.fOriginalMatrix = SkMatrix::I();
2781 pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
2782
2783 gPdfContext = &pdfContext;
2784
2785 SkScalar z = SkIntToScalar(0);
2786 SkScalar w = dst.width();
2787 SkScalar h = dst.height();
2788
2789 if (SkScalarTruncToInt(w) <= 0 || SkScalarTruncToInt(h) <= 0) {
2790 return true;
2791 }
2792
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();
2796
2797 SkPoint pdfSpace[4] = {SkPoint::Make(z, z),
2798 SkPoint::Make(wp, z),
2799 SkPoint::Make(wp, hp),
2800 SkPoint::Make(z, hp)};
2801
2802 #ifdef PDF_DEBUG_3X
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)};
2809 #else
2810 SkPoint skiaSpace[4] = {SkPoint::Make(z, h),
2811 SkPoint::Make(w, h),
2812 SkPoint::Make(w, z),
2813 SkPoint::Make(z, z)};
2814 #endif
2815
2816 SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
2817 SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
2818
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());
2822
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;
2827
2828 #ifndef PDF_DEBUG_NO_PAGE_CLIPING
2829 canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
2830 #endif
2831
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);
2835
2836 doPage(&pdfContext, canvas, fPdfDoc->page(page));
2837
2838 // TODO(edisonn:) erase with white before draw? Right now the caller is responsible.
2839 // SkPaint paint;
2840 // paint.setColor(SK_ColorWHITE);
2841 // canvas->drawRect(rect, paint);
2842
2843
2844 canvas->flush();
2845 return true;
2846 }
2847
CreateFromFile(const char * inputFileName)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) {
2852 SkDELETE(pdfDoc);
2853 return NULL;
2854 }
2855
2856 return SkNEW_ARGS(SkPdfRenderer, (pdfDoc));
2857 }
2858
CreateFromStream(SkStream * stream)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) {
2863 SkDELETE(pdfDoc);
2864 return NULL;
2865 }
2866
2867 return SkNEW_ARGS(SkPdfRenderer, (pdfDoc));
2868 }
2869
SkPdfRenderer(SkPdfNativeDoc * doc)2870 SkPdfRenderer::SkPdfRenderer(SkPdfNativeDoc* doc)
2871 :fPdfDoc(doc) {
2872 }
2873
~SkPdfRenderer()2874 SkPdfRenderer::~SkPdfRenderer() {
2875 SkDELETE(fPdfDoc);
2876 }
2877
pages() const2878 int SkPdfRenderer::pages() const {
2879 SkASSERT(fPdfDoc != NULL);
2880 return fPdfDoc->pages();
2881 }
2882
MediaBox(int page) const2883 SkRect SkPdfRenderer::MediaBox(int page) const {
2884 SkASSERT(fPdfDoc != NULL);
2885 return fPdfDoc->MediaBox(page);
2886 }
2887
bytesUsed() const2888 size_t SkPdfRenderer::bytesUsed() const {
2889 SkASSERT(fPdfDoc != NULL);
2890 return fPdfDoc->bytesUsed();
2891 }
2892
SkPDFNativeRenderToBitmap(SkStream * stream,SkBitmap * output,int page,SkPdfContent unused,double dpi)2893 bool SkPDFNativeRenderToBitmap(SkStream* stream,
2894 SkBitmap* output,
2895 int page,
2896 SkPdfContent unused,
2897 double dpi) {
2898 SkASSERT(page >= 0);
2899 SkPdfRenderer* renderer = SkPdfRenderer::CreateFromStream(stream);
2900 if (NULL == renderer) {
2901 return false;
2902 }
2903
2904 SkRect rect = renderer->MediaBox(page < 0 ? 0 :page);
2905
2906 SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(dpi / 72.0));
2907 SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(dpi / 72.0));
2908
2909 rect = SkRect::MakeWH(width, height);
2910
2911 setup_bitmap(output, SkScalarCeilToInt(width), SkScalarCeilToInt(height));
2912
2913 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (*output)));
2914 SkCanvas canvas(device);
2915
2916 return renderer->renderPage(page, &canvas, rect);
2917 }
2918