Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: experimental/PdfViewer/src/SkPdfRenderer.cpp

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

Powered by Google App Engine
This is Rietveld 408576698