OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2013 Google Inc. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license that can be | |
5 * found in the LICENSE file. | |
6 */ | |
7 | |
8 #include "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 } | |
OLD | NEW |