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

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

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

Powered by Google App Engine
This is Rietveld 408576698