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