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 "SkCanvas.h" | |
9 #include "SkDevice.h" | |
10 #include "SkForceLinking.h" | |
11 #include "SkGraphics.h" | |
12 #include "SkImageDecoder.h" | |
13 #include "SkImageEncoder.h" | |
14 #include "SkOSFile.h" | |
15 #include "SkPicture.h" | |
16 #include "SkStream.h" | |
17 #include "SkTypeface.h" | |
18 #include "SkTArray.h" | |
19 #include "picture_utils.h" | |
20 | |
21 #include <iostream> | |
22 #include <cstdio> | |
23 #include <stack> | |
24 #include <set> | |
25 | |
26 __SK_FORCE_IMAGE_DECODER_LINKING; | |
27 | |
28 // TODO(edisonn): tool, show what objects were read at least, show the ones not
even read | |
29 // keep for each object pos in file | |
30 // plug in for VS? syntax coloring, show selected object ... from the text, or f
rom rendered x,y | |
31 | |
32 // TODO(edisonn): security - validate all the user input, all pdf! | |
33 | |
34 // TODO(edisonn): put drawtext in #ifdefs, so comparations will ignore minor cha
nges in text positioning and font | |
35 // this way, we look more at other features and layout in diffs | |
36 | |
37 // TODO(edisonn): move trace dump in the get functions, and mapper ones too so i
t ghappens automatically | |
38 /* | |
39 #ifdef PDF_TRACE | |
40 std::string str; | |
41 pdfContext->fGraphicsState.fResources->native()->ToString(str); | |
42 printf("Print Tf Resources: %s\n", str.c_str()); | |
43 #endif | |
44 */ | |
45 | |
46 #include "SkPdfHeaders_autogen.h" | |
47 #include "SkPdfMapper_autogen.h" | |
48 #include "SkPdfParser.h" | |
49 | |
50 #include "SkPdfBasics.h" | |
51 #include "SkPdfUtils.h" | |
52 | |
53 #include "SkPdfFont.h" | |
54 | |
55 /* | |
56 * TODO(edisonn): | |
57 * - all font types and all ppdf font features | |
58 * - word spacing | |
59 * - load font for baidu.pdf | |
60 * - load font for youtube.pdf | |
61 * - parser for pdf from the definition already available in pdfspec_autoge
n.py | |
62 * - all docs from ~/work | |
63 * - encapsulate native in the pdf api so the skpdf does not know anything about
native ... in progress | |
64 * - load gs/ especially smask and already known prop (skp) ... in progress | |
65 * - wrapper on classes for customizations? e.g. | |
66 * SkPdfPageObjectVanila - has only the basic loaders/getters | |
67 * SkPdfPageObject : public SkPdfPageObjectVanila, extends, and I can add custom
izations here | |
68 * need to find a nice object model for all this with constructors and factories | |
69 * - deal with inheritable automatically ? | |
70 * - deal with specific type in spec directly, add all dictionary types to known
types | |
71 */ | |
72 | |
73 using namespace std; | |
74 | |
75 // Utilities | |
76 static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color
= SK_ColorWHITE) { | |
77 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
78 | |
79 bitmap->allocPixels(); | |
80 bitmap->eraseColor(color); | |
81 } | |
82 | |
83 // TODO(edisonn): synonyms? DeviceRGB and RGB ... | |
84 static int GetColorSpaceComponents(const std::string& colorSpace) { | |
85 if (colorSpace == "DeviceCMYK") { | |
86 return 4; | |
87 } else if (colorSpace == "DeviceGray" || | |
88 colorSpace == "CalGray" || | |
89 colorSpace == "Indexed") { | |
90 return 1; | |
91 } else if (colorSpace == "DeviceRGB" || | |
92 colorSpace == "CalRGB" || | |
93 colorSpace == "Lab") { | |
94 return 3; | |
95 } else { | |
96 return 0; | |
97 } | |
98 } | |
99 | |
100 SkMatrix SkMatrixFromPdfMatrix(double array[6]) { | |
101 SkMatrix matrix; | |
102 matrix.setAll(SkDoubleToScalar(array[0]), | |
103 SkDoubleToScalar(array[2]), | |
104 SkDoubleToScalar(array[4]), | |
105 SkDoubleToScalar(array[1]), | |
106 SkDoubleToScalar(array[3]), | |
107 SkDoubleToScalar(array[5]), | |
108 SkDoubleToScalar(0), | |
109 SkDoubleToScalar(0), | |
110 SkDoubleToScalar(1)); | |
111 | |
112 return matrix; | |
113 } | |
114 | |
115 SkMatrix SkMatrixFromPdfArray(SkPdfArray* pdfArray) { | |
116 double array[6]; | |
117 | |
118 // TODO(edisonn): security issue, ret if size() != 6 | |
119 for (int i = 0; i < 6; i++) { | |
120 const SkPdfObject* elem = pdfArray->operator [](i); | |
121 if (elem == NULL || !elem->isNumber()) { | |
122 return SkMatrix::I(); // TODO(edisonn): report issue | |
123 } | |
124 array[i] = elem->numberValue(); | |
125 } | |
126 | |
127 return SkMatrixFromPdfMatrix(array); | |
128 } | |
129 | |
130 SkBitmap* gDumpBitmap = NULL; | |
131 SkCanvas* gDumpCanvas = NULL; | |
132 char gLastKeyword[100] = ""; | |
133 int gLastOpKeyword = -1; | |
134 char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh
,EI,Do,EX,"; | |
135 int gReadOp = 0; | |
136 | |
137 | |
138 #ifdef PDF_TRACE_DIFF_IN_PNG | |
139 static bool hasVisualEffect(const char* pdfOp) { | |
140 return true; | |
141 if (*pdfOp == '\0') return false; | |
142 | |
143 char markedPdfOp[100] = ","; | |
144 strcat(markedPdfOp, pdfOp); | |
145 strcat(markedPdfOp, ","); | |
146 | |
147 return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL); | |
148 } | |
149 #endif // PDF_TRACE_DIFF_IN_PNG | |
150 | |
151 // TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrum
entation. | |
152 static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) { | |
153 bool ret = fTokenizer->readToken(token); | |
154 | |
155 gReadOp++; | |
156 | |
157 #ifdef PDF_TRACE_DIFF_IN_PNG | |
158 // TODO(edisonn): compare with old bitmap, and save only new bits are availa
ble, and save | |
159 // the numbar and name of last operation, so the file name will reflect op t
hat changed. | |
160 if (hasVisualEffect(gLastKeyword)) { // TODO(edisonn): and has dirty bits. | |
161 gDumpCanvas->flush(); | |
162 | |
163 SkBitmap bitmap; | |
164 setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height()); | |
165 | |
166 memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSiz
e()); | |
167 | |
168 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap))); | |
169 SkCanvas canvas(device); | |
170 | |
171 // draw context stuff here | |
172 SkPaint blueBorder; | |
173 blueBorder.setColor(SK_ColorBLUE); | |
174 blueBorder.setStyle(SkPaint::kStroke_Style); | |
175 blueBorder.setTextSize(SkDoubleToScalar(20)); | |
176 | |
177 SkString str; | |
178 | |
179 const SkClipStack* clipStack = gDumpCanvas->getClipStack(); | |
180 if (clipStack) { | |
181 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterSt
art); | |
182 const SkClipStack::Element* elem; | |
183 double y = 0; | |
184 int total = 0; | |
185 while (elem = iter.next()) { | |
186 total++; | |
187 y += 30; | |
188 | |
189 switch (elem->getType()) { | |
190 case SkClipStack::Element::kRect_Type: | |
191 canvas.drawRect(elem->getRect(), blueBorder); | |
192 canvas.drawText("Rect Clip", strlen("Rect Clip"), SkDoub
leToScalar(10), SkDoubleToScalar(y), blueBorder); | |
193 break; | |
194 case SkClipStack::Element::kPath_Type: | |
195 canvas.drawPath(elem->getPath(), blueBorder); | |
196 canvas.drawText("Path Clip", strlen("Path Clip"), SkDoub
leToScalar(10), SkDoubleToScalar(y), blueBorder); | |
197 break; | |
198 case SkClipStack::Element::kEmpty_Type: | |
199 canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!")
, SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder); | |
200 break; | |
201 default: | |
202 canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!
"), SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder); | |
203 break; | |
204 } | |
205 } | |
206 | |
207 y += 30; | |
208 str.printf("Number of clips in stack: %i", total); | |
209 canvas.drawText(str.c_str(), str.size(), SkDoubleToScalar(10), SkDou
bleToScalar(y), blueBorder); | |
210 } | |
211 | |
212 const SkRegion& clipRegion = gDumpCanvas->getTotalClip(); | |
213 SkPath clipPath; | |
214 if (clipRegion.getBoundaryPath(&clipPath)) { | |
215 SkPaint redBorder; | |
216 redBorder.setColor(SK_ColorRED); | |
217 redBorder.setStyle(SkPaint::kStroke_Style); | |
218 canvas.drawPath(clipPath, redBorder); | |
219 } | |
220 | |
221 canvas.flush(); | |
222 | |
223 SkString out; | |
224 | |
225 // TODO(edisonn): get the image, and overlay on top of it, the clip , gr
afic state, teh stack, | |
226 // ... and other properties, to be able to debug th code easily | |
227 | |
228 out.appendf("/usr/local/google/home/edisonn/log_view2/step-%i-%s.png", g
LastOpKeyword, gLastKeyword); | |
229 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Typ
e, 100); | |
230 } | |
231 | |
232 if (token->fType == kKeyword_TokenType) { | |
233 strcpy(gLastKeyword, token->fKeyword); | |
234 gLastOpKeyword = gReadOp; | |
235 } else { | |
236 strcpy(gLastKeyword, ""); | |
237 } | |
238 #endif | |
239 | |
240 return ret; | |
241 } | |
242 | |
243 | |
244 | |
245 typedef PdfResult (*PdfOperatorRenderer)(PdfContext*, SkCanvas*, PdfTokenLooper*
*); | |
246 | |
247 map<std::string, PdfOperatorRenderer> gPdfOps; | |
248 | |
249 map<std::string, int> gRenderStats[kCount_PdfResult]; | |
250 | |
251 const char* gRenderStatsNames[kCount_PdfResult] = { | |
252 "Success", | |
253 "Partially implemented", | |
254 "Not yet implemented", | |
255 "Ignore Error", | |
256 "Error", | |
257 "Unsupported/Unknown" | |
258 }; | |
259 | |
260 static PdfResult DrawText(PdfContext* pdfContext, | |
261 const SkPdfObject* _str, | |
262 SkCanvas* canvas) | |
263 { | |
264 | |
265 SkPdfFont* skfont = pdfContext->fGraphicsState.fSkFont; | |
266 if (skfont == NULL) { | |
267 skfont = SkPdfFont::Default(); | |
268 } | |
269 | |
270 | |
271 if (_str == NULL || !_str->isAnyString()) { | |
272 // TODO(edisonn): report warning | |
273 return kIgnoreError_PdfResult; | |
274 } | |
275 const SkPdfString* str = (const SkPdfString*)_str; | |
276 | |
277 SkUnencodedText binary(str); | |
278 | |
279 SkDecodedText decoded; | |
280 | |
281 if (skfont->encoding() == NULL) { | |
282 // TODO(edisonn): report warning | |
283 return kNYI_PdfResult; | |
284 } | |
285 | |
286 skfont->encoding()->decodeText(binary, &decoded); | |
287 | |
288 SkPaint paint; | |
289 // TODO(edisonn): when should fCurFont->GetFontSize() used? When cur is fCur
FontSize == 0? | |
290 // Or maybe just not call setTextSize at all? | |
291 if (pdfContext->fGraphicsState.fCurFontSize != 0) { | |
292 paint.setTextSize(SkDoubleToScalar(pdfContext->fGraphicsState.fCurFontSi
ze)); | |
293 } | |
294 | |
295 // if (fCurFont && fCurFont->GetFontScale() != 0) { | |
296 // paint.setTextScaleX(SkFloatToScalar(fCurFont->GetFontScale() / 100.0))
; | |
297 // } | |
298 | |
299 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); | |
300 | |
301 canvas->save(); | |
302 | |
303 #if 1 | |
304 SkMatrix matrix = pdfContext->fGraphicsState.fMatrixTm; | |
305 | |
306 SkPoint point1; | |
307 pdfContext->fGraphicsState.fMatrixTm.mapXY(SkIntToScalar(0), SkIntToScalar(0
), &point1); | |
308 | |
309 SkMatrix mirror; | |
310 mirror.setTranslate(0, -point1.y()); | |
311 // TODO(edisonn): fix rotated text, and skewed too | |
312 mirror.postScale(SK_Scalar1, -SK_Scalar1); | |
313 // TODO(edisonn): post rotate, skew | |
314 mirror.postTranslate(0, point1.y()); | |
315 | |
316 matrix.postConcat(mirror); | |
317 | |
318 canvas->setMatrix(matrix); | |
319 | |
320 SkTraceMatrix(matrix, "mirrored"); | |
321 #endif | |
322 | |
323 skfont->drawText(decoded, &paint, pdfContext, canvas); | |
324 canvas->restore(); | |
325 | |
326 return kPartial_PdfResult; | |
327 } | |
328 | |
329 // TODO(edisonn): create header files with declarations! | |
330 PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo
per); | |
331 PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo
per); | |
332 PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo
oper); | |
333 PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo
oper); | |
334 | |
335 // TODO(edisonn): perf!!! | |
336 | |
337 static SkColorTable* getGrayColortable() { | |
338 static SkColorTable* grayColortable = NULL; | |
339 if (grayColortable == NULL) { | |
340 SkPMColor* colors = new SkPMColor[256]; | |
341 for (int i = 0 ; i < 256; i++) { | |
342 colors[i] = SkPreMultiplyARGB(255, i, i, i); | |
343 } | |
344 grayColortable = new SkColorTable(colors, 256); | |
345 } | |
346 return grayColortable; | |
347 } | |
348 | |
349 static SkBitmap transferImageStreamToBitmap(unsigned char* uncompressedStream, s
ize_t uncompressedStreamLength, | |
350 int width, int height, int bytesPerLine, | |
351 int bpc, const std::string& colorSpace, | |
352 bool transparencyMask) { | |
353 SkBitmap bitmap; | |
354 | |
355 //int components = GetColorSpaceComponents(colorSpace); | |
356 //#define MAX_COMPONENTS 10 | |
357 | |
358 // TODO(edisonn): assume start of lines are aligned at 32 bits? | |
359 // Is there a faster way to load the uncompressed stream into a bitmap? | |
360 | |
361 // minimal support for now | |
362 if ((colorSpace == "DeviceRGB" || colorSpace == "RGB") && bpc == 8) { | |
363 SkColor* uncompressedStreamArgb = (SkColor*)malloc(width * height * size
of(SkColor)); | |
364 | |
365 for (int h = 0 ; h < height; h++) { | |
366 long i = width * (h); | |
367 for (int w = 0 ; w < width; w++) { | |
368 uncompressedStreamArgb[i] = SkColorSetRGB(uncompressedStream[3 *
w], | |
369 uncompressedStream[3 *
w + 1], | |
370 uncompressedStream[3 *
w + 2]); | |
371 i++; | |
372 } | |
373 uncompressedStream += bytesPerLine; | |
374 } | |
375 | |
376 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
377 bitmap.setPixels(uncompressedStreamArgb); | |
378 } | |
379 else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) { | |
380 unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * hei
ght); | |
381 | |
382 for (int h = 0 ; h < height; h++) { | |
383 long i = width * (h); | |
384 for (int w = 0 ; w < width; w++) { | |
385 uncompressedStreamA8[i] = transparencyMask ? 255 - uncompressedS
tream[w] : | |
386 uncompressedStream[
w]; | |
387 i++; | |
388 } | |
389 uncompressedStream += bytesPerLine; | |
390 } | |
391 | |
392 bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIn
dex8_Config, | |
393 width, height); | |
394 bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGray
Colortable()); | |
395 } | |
396 | |
397 // TODO(edisonn): Report Warning, NYI, or error | |
398 return bitmap; | |
399 } | |
400 | |
401 // utils | |
402 | |
403 // TODO(edisonn): add cache, or put the bitmap property directly on the PdfObjec
t | |
404 // TODO(edisonn): deal with colorSpaces, we could add them to SkBitmap::Config | |
405 // TODO(edisonn): preserve A1 format that skia knows, + fast convert from 111, 2
22, 444 to closest | |
406 // skia format, through a table | |
407 | |
408 // this functions returns the image, it does not look at the smask. | |
409 | |
410 static SkBitmap getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary*
image, bool transparencyMask) { | |
411 if (image == NULL || !image->hasStream()) { | |
412 // TODO(edisonn): report warning to be used in testing. | |
413 return SkBitmap(); | |
414 } | |
415 | |
416 int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc); | |
417 int64_t width = image->Width(pdfContext->fPdfDoc); | |
418 int64_t height = image->Height(pdfContext->fPdfDoc); | |
419 std::string colorSpace = "DeviceRGB"; | |
420 | |
421 // TODO(edisonn): color space can be an array too! | |
422 if (image->isColorSpaceAName(pdfContext->fPdfDoc)) { | |
423 colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc); | |
424 } | |
425 | |
426 /* | |
427 bool imageMask = image->imageMask(); | |
428 | |
429 if (imageMask) { | |
430 if (bpc != 0 && bpc != 1) { | |
431 // TODO(edisonn): report warning to be used in testing. | |
432 return SkBitmap(); | |
433 } | |
434 bpc = 1; | |
435 } | |
436 */ | |
437 | |
438 unsigned char* uncompressedStream = NULL; | |
439 size_t uncompressedStreamLength = 0; | |
440 | |
441 SkPdfStream* stream = (SkPdfStream*)image; | |
442 | |
443 if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompres
sedStreamLength, pdfContext->fPdfDoc->allocator()) || | |
444 uncompressedStream == NULL || uncompressedStreamLength == 0) { | |
445 // TODO(edisonn): report warning to be used in testing. | |
446 return SkBitmap(); | |
447 } | |
448 | |
449 SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stre
am; | |
450 | |
451 if (streamDict->has_Filter() && ((streamDict->isFilterAName(NULL) && | |
452 streamDict->getFilterAsName(NULL) == "
DCTDecode") || | |
453 (streamDict->isFilterAArray(NULL) && | |
454 streamDict->getFilterAsArray(NULL)->si
ze() > 0 && | |
455 streamDict->getFilterAsArray(NULL)->ob
jAtAIndex(0)->isName() && | |
456 streamDict->getFilterAsArray(NULL)->ob
jAtAIndex(0)->nameValue2() == "DCTDecode"))) { | |
457 SkBitmap bitmap; | |
458 SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLengt
h, &bitmap); | |
459 return bitmap; | |
460 } | |
461 | |
462 | |
463 | |
464 // TODO (edisonn): Fast Jpeg(DCTDecode) draw, or fast PNG(FlateDecode) draw
... | |
465 // PdfObject* value = resolveReferenceObject(pdfContext->fPdfDoc, | |
466 // obj.GetDictionary().GetKey(PdfNa
me("Filter"))); | |
467 // if (value && value->IsArray() && value->GetArray().GetSize() == 1) { | |
468 // value = resolveReferenceObject(pdfContext->fPdfDoc, | |
469 // &value->GetArray()[0]); | |
470 // } | |
471 // if (value && value->IsName() && value->GetName().GetName() == "DCTDecode")
{ | |
472 // SkStream stream = SkStream:: | |
473 // SkImageDecoder::Factory() | |
474 // } | |
475 | |
476 int bytesPerLine = uncompressedStreamLength / height; | |
477 #ifdef PDF_TRACE | |
478 if (uncompressedStreamLength % height != 0) { | |
479 printf("Warning uncompressedStreamLength modulo height != 0 !!!\n"); | |
480 } | |
481 #endif | |
482 | |
483 SkBitmap bitmap = transferImageStreamToBitmap( | |
484 (unsigned char*)uncompressedStream, uncompressedStreamLength, | |
485 (int)width, (int)height, bytesPerLine, | |
486 (int)bpc, colorSpace, | |
487 transparencyMask); | |
488 | |
489 return bitmap; | |
490 } | |
491 | |
492 static SkBitmap getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary*
obj) { | |
493 SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc); | |
494 | |
495 if (sMask) { | |
496 return getImageFromObject(pdfContext, sMask, true); | |
497 } | |
498 | |
499 // TODO(edisonn): implement GS SMask. Default to empty right now. | |
500 return pdfContext->fGraphicsState.fSMask; | |
501 } | |
502 | |
503 static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdf
ImageDictionary* skpdfimage) { | |
504 if (skpdfimage == NULL) { | |
505 return kIgnoreError_PdfResult; | |
506 } | |
507 | |
508 SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false); | |
509 SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage); | |
510 | |
511 canvas->save(); | |
512 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
513 | |
514 #if 1 | |
515 SkScalar z = SkIntToScalar(0); | |
516 SkScalar one = SkIntToScalar(1); | |
517 | |
518 SkPoint from[4] = {SkPoint::Make(z, z), SkPoint::Make(one, z), SkPoint::Make
(one, one), SkPoint::Make(z, one)}; | |
519 SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Ma
ke(one, z), SkPoint::Make(z, z)}; | |
520 SkMatrix flip; | |
521 SkAssertResult(flip.setPolyToPoly(from, to, 4)); | |
522 SkMatrix solveImageFlip = pdfContext->fGraphicsState.fMatrix; | |
523 solveImageFlip.preConcat(flip); | |
524 canvas->setMatrix(solveImageFlip); | |
525 #endif | |
526 | |
527 SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0),
SkDoubleToScalar(1.0), SkDoubleToScalar(1.0)); | |
528 | |
529 if (sMask.empty()) { | |
530 canvas->drawBitmapRect(image, dst, NULL); | |
531 } else { | |
532 canvas->saveLayer(&dst, NULL); | |
533 canvas->drawBitmapRect(image, dst, NULL); | |
534 SkPaint xfer; | |
535 pdfContext->fGraphicsState.applyGraphicsState(&xfer, false); | |
536 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_M
ode | |
537 canvas->drawBitmapRect(sMask, dst, &xfer); | |
538 canvas->restore(); | |
539 } | |
540 | |
541 canvas->restore(); | |
542 | |
543 return kPartial_PdfResult; | |
544 } | |
545 | |
546 | |
547 | |
548 | |
549 static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfT
ype1FormDictionary* skobj) { | |
550 if (!skobj || !skobj->hasStream()) { | |
551 return kIgnoreError_PdfResult; | |
552 } | |
553 | |
554 PdfOp_q(pdfContext, canvas, NULL); | |
555 canvas->save(); | |
556 | |
557 | |
558 if (skobj->Resources(pdfContext->fPdfDoc)) { | |
559 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPd
fDoc); | |
560 } | |
561 | |
562 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix"); | |
563 | |
564 if (skobj->has_Matrix()) { | |
565 pdfContext->fGraphicsState.fMatrix.preConcat(skobj->Matrix(pdfContext->f
PdfDoc)); | |
566 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatri
x; | |
567 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatr
ix; | |
568 // TODO(edisonn) reset matrixTm and matricTlm also? | |
569 } | |
570 | |
571 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix"); | |
572 | |
573 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
574 | |
575 if (skobj->has_BBox()) { | |
576 canvas->clipRect(skobj->BBox(pdfContext->fPdfDoc), SkRegion::kIntersect_
Op, true); // TODO(edisonn): AA from settings. | |
577 } | |
578 | |
579 // TODO(edisonn): iterate smart on the stream even if it is compressed, toke
nize it as we go. | |
580 // For this PdfContentsTokenizer needs to be extended. | |
581 | |
582 SkPdfStream* stream = (SkPdfStream*)skobj; | |
583 | |
584 SkPdfNativeTokenizer* tokenizer = pdfContext->fPdfDoc->tokenizerOfStream(str
eam); | |
585 if (tokenizer != NULL) { | |
586 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); | |
587 looper.loop(); | |
588 delete tokenizer; | |
589 } | |
590 | |
591 // TODO(edisonn): should we restore the variable stack at the same state? | |
592 // There could be operands left, that could be consumed by a parent tokenize
r when we pop. | |
593 canvas->restore(); | |
594 PdfOp_Q(pdfContext, canvas, NULL); | |
595 return kPartial_PdfResult; | |
596 } | |
597 | |
598 //static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const
SkPdfObject* obj) { | |
599 // return kNYI_PdfResult; | |
600 //} | |
601 | |
602 PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObjec
t* skobj, SkRect bBox, SkMatrix matrix, double textSize) { | |
603 if (!skobj || !skobj->hasStream()) { | |
604 return kIgnoreError_PdfResult; | |
605 } | |
606 | |
607 PdfOp_q(pdfContext, canvas, NULL); | |
608 canvas->save(); | |
609 | |
610 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); | |
611 pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), Sk
DoubleToScalar(textSize)); | |
612 | |
613 pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm; | |
614 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix; | |
615 | |
616 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix"); | |
617 | |
618 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
619 | |
620 SkRect rm = bBox; | |
621 pdfContext->fGraphicsState.fMatrix.mapRect(&rm); | |
622 | |
623 SkTraceRect(rm, "bbox mapped"); | |
624 | |
625 canvas->clipRect(bBox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA
from settings. | |
626 | |
627 // TODO(edisonn): iterate smart on the stream even if it is compressed, toke
nize it as we go. | |
628 // For this PdfContentsTokenizer needs to be extended. | |
629 | |
630 SkPdfStream* stream = (SkPdfStream*)skobj; | |
631 | |
632 SkPdfNativeTokenizer* tokenizer = pdfContext->fPdfDoc->tokenizerOfStream(str
eam); | |
633 if (tokenizer != NULL) { | |
634 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); | |
635 looper.loop(); | |
636 delete tokenizer; | |
637 } | |
638 | |
639 // TODO(edisonn): should we restore the variable stack at the same state? | |
640 // There could be operands left, that could be consumed by a parent tokenize
r when we pop. | |
641 canvas->restore(); | |
642 PdfOp_Q(pdfContext, canvas, NULL); | |
643 | |
644 return kPartial_PdfResult; | |
645 } | |
646 | |
647 | |
648 // TODO(edisonn): make sure the pointer is unique | |
649 std::set<const SkPdfObject*> gInRendering; | |
650 | |
651 class CheckRecursiveRendering { | |
652 const SkPdfObject* fUniqueData; | |
653 public: | |
654 CheckRecursiveRendering(const SkPdfObject* obj) : fUniqueData(obj) { | |
655 gInRendering.insert(obj); | |
656 } | |
657 | |
658 ~CheckRecursiveRendering() { | |
659 //SkASSERT(fObj.fInRendering); | |
660 gInRendering.erase(fUniqueData); | |
661 } | |
662 | |
663 static bool IsInRendering(const SkPdfObject* obj) { | |
664 return gInRendering.find(obj) != gInRendering.end(); | |
665 } | |
666 }; | |
667 | |
668 static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdf
Object* obj) { | |
669 if (CheckRecursiveRendering::IsInRendering(obj)) { | |
670 // Oops, corrupt PDF! | |
671 return kIgnoreError_PdfResult; | |
672 } | |
673 | |
674 CheckRecursiveRendering checkRecursion(obj); | |
675 | |
676 switch (pdfContext->fPdfDoc->mapper()->mapXObjectDictionary(obj)) | |
677 { | |
678 case kImageDictionary_SkPdfObjectType: | |
679 return doXObject_Image(pdfContext, canvas, (SkPdfImageDictionary*)ob
j); | |
680 case kType1FormDictionary_SkPdfObjectType: | |
681 return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*
)obj); | |
682 //case kObjectDictionaryXObjectPS_SkPdfObjectType: | |
683 //return doXObject_PS(skxobj.asPS()); | |
684 default: | |
685 return kIgnoreError_PdfResult; | |
686 } | |
687 } | |
688 | |
689 PdfResult PdfOp_q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo
per) { | |
690 pdfContext->fStateStack.push(pdfContext->fGraphicsState); | |
691 canvas->save(); | |
692 return kOK_PdfResult; | |
693 } | |
694 | |
695 PdfResult PdfOp_Q(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** loo
per) { | |
696 pdfContext->fGraphicsState = pdfContext->fStateStack.top(); | |
697 pdfContext->fStateStack.pop(); | |
698 canvas->restore(); | |
699 return kOK_PdfResult; | |
700 } | |
701 | |
702 static PdfResult PdfOp_cm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
703 double array[6]; | |
704 for (int i = 0 ; i < 6 ; i++) { | |
705 array[5 - i] = pdfContext->fObjectStack.top()->numberValue(); | |
706 pdfContext->fObjectStack.pop(); | |
707 } | |
708 | |
709 // a b | |
710 // c d | |
711 // e f | |
712 | |
713 // 0 1 | |
714 // 2 3 | |
715 // 4 5 | |
716 | |
717 // sx ky | |
718 // kx sy | |
719 // tx ty | |
720 SkMatrix matrix = SkMatrixFromPdfMatrix(array); | |
721 | |
722 pdfContext->fGraphicsState.fMatrix.preConcat(matrix); | |
723 | |
724 #ifdef PDF_TRACE | |
725 printf("cm "); | |
726 for (int i = 0 ; i < 6 ; i++) { | |
727 printf("%f ", array[i]); | |
728 } | |
729 printf("\n"); | |
730 SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "cm"); | |
731 #endif | |
732 | |
733 return kOK_PdfResult; | |
734 } | |
735 | |
736 //leading TL Set the text leading, Tl | |
737 //, to leading, which is a number expressed in unscaled text | |
738 //space units. Text leading is used only by the T*, ', and " operators. Initial
value: 0. | |
739 static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
740 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fOb
jectStack.pop(); | |
741 | |
742 pdfContext->fGraphicsState.fTextLeading = ty; | |
743 | |
744 return kOK_PdfResult; | |
745 } | |
746 | |
747 static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
748 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObje
ctStack.pop(); | |
749 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObje
ctStack.pop(); | |
750 | |
751 double array[6] = {1, 0, 0, 1, tx, ty}; | |
752 SkMatrix matrix = SkMatrixFromPdfMatrix(array); | |
753 | |
754 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); | |
755 pdfContext->fGraphicsState.fMatrixTlm.preConcat(matrix); | |
756 | |
757 return kPartial_PdfResult; | |
758 } | |
759 | |
760 static PdfResult PdfOp_TD(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
761 double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObje
ctStack.pop(); | |
762 double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObje
ctStack.pop(); | |
763 | |
764 // TODO(edisonn): Create factory methods or constructors so native is hidden | |
765 SkPdfReal* _ty = pdfContext->fPdfDoc->createReal(-ty); | |
766 pdfContext->fObjectStack.push(_ty); | |
767 | |
768 PdfOp_TL(pdfContext, canvas, looper); | |
769 | |
770 SkPdfReal* vtx = pdfContext->fPdfDoc->createReal(tx); | |
771 pdfContext->fObjectStack.push(vtx); | |
772 | |
773 SkPdfReal* vty = pdfContext->fPdfDoc->createReal(ty); | |
774 pdfContext->fObjectStack.push(vty); | |
775 | |
776 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper); | |
777 | |
778 // TODO(edisonn): delete all the objects after rendering was complete, in th
is way pdf is rendered faster | |
779 // and the cleanup can happen while the user looks at the image | |
780 delete _ty; | |
781 delete vtx; | |
782 delete vty; | |
783 | |
784 return ret; | |
785 } | |
786 | |
787 static PdfResult PdfOp_Tm(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
788 double f = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
789 double e = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
790 double d = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
791 double c = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
792 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
793 double a = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjec
tStack.pop(); | |
794 | |
795 double array[6]; | |
796 array[0] = a; | |
797 array[1] = b; | |
798 array[2] = c; | |
799 array[3] = d; | |
800 array[4] = e; | |
801 array[5] = f; | |
802 | |
803 SkMatrix matrix = SkMatrixFromPdfMatrix(array); | |
804 matrix.postConcat(pdfContext->fGraphicsState.fMatrix); | |
805 | |
806 // TODO(edisonn): Text positioning. | |
807 pdfContext->fGraphicsState.fMatrixTm = matrix; | |
808 pdfContext->fGraphicsState.fMatrixTlm = matrix;; | |
809 | |
810 return kPartial_PdfResult; | |
811 } | |
812 | |
813 //— T* Move to the start of the next line. This operator has the same effect as
the code | |
814 //0 Tl Td | |
815 //where Tl is the current leading parameter in the text state | |
816 static PdfResult PdfOp_T_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
817 SkPdfReal* zero = pdfContext->fPdfDoc->createReal(0.0); | |
818 SkPdfReal* tl = pdfContext->fPdfDoc->createReal(pdfContext->fGraphicsState.f
TextLeading); | |
819 | |
820 pdfContext->fObjectStack.push(zero); | |
821 pdfContext->fObjectStack.push(tl); | |
822 | |
823 PdfResult ret = PdfOp_Td(pdfContext, canvas, looper); | |
824 | |
825 delete zero; // TODO(edisonn): do not alocate and delete constants! | |
826 delete tl; | |
827 | |
828 return ret; | |
829 } | |
830 | |
831 static PdfResult PdfOp_m(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
832 if (pdfContext->fGraphicsState.fPathClosed) { | |
833 pdfContext->fGraphicsState.fPath.reset(); | |
834 pdfContext->fGraphicsState.fPathClosed = false; | |
835 } | |
836 | |
837 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->number
Value(); pdfContext->fObjectStack.pop(); | |
838 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->number
Value(); pdfContext->fObjectStack.pop(); | |
839 | |
840 pdfContext->fGraphicsState.fPath.moveTo(SkDoubleToScalar(pdfContext->fGraphi
csState.fCurPosX), | |
841 SkDoubleToScalar(pdfContext->fGraphics
State.fCurPosY)); | |
842 | |
843 return kOK_PdfResult; | |
844 } | |
845 | |
846 static PdfResult PdfOp_l(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
847 if (pdfContext->fGraphicsState.fPathClosed) { | |
848 pdfContext->fGraphicsState.fPath.reset(); | |
849 pdfContext->fGraphicsState.fPathClosed = false; | |
850 } | |
851 | |
852 pdfContext->fGraphicsState.fCurPosY = pdfContext->fObjectStack.top()->number
Value(); pdfContext->fObjectStack.pop(); | |
853 pdfContext->fGraphicsState.fCurPosX = pdfContext->fObjectStack.top()->number
Value(); pdfContext->fObjectStack.pop(); | |
854 | |
855 pdfContext->fGraphicsState.fPath.lineTo(SkDoubleToScalar(pdfContext->fGraphi
csState.fCurPosX), | |
856 SkDoubleToScalar(pdfContext->fGraphics
State.fCurPosY)); | |
857 | |
858 return kOK_PdfResult; | |
859 } | |
860 | |
861 static PdfResult PdfOp_c(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
862 if (pdfContext->fGraphicsState.fPathClosed) { | |
863 pdfContext->fGraphicsState.fPath.reset(); | |
864 pdfContext->fGraphicsState.fPathClosed = false; | |
865 } | |
866 | |
867 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
868 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
869 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
870 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
871 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
872 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
873 | |
874 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca
lar(y1), | |
875 SkDoubleToScalar(x2), SkDoubleToScal
ar(y2), | |
876 SkDoubleToScalar(x3), SkDoubleToScal
ar(y3)); | |
877 | |
878 pdfContext->fGraphicsState.fCurPosX = x3; | |
879 pdfContext->fGraphicsState.fCurPosY = y3; | |
880 | |
881 return kOK_PdfResult; | |
882 } | |
883 | |
884 static PdfResult PdfOp_v(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
885 if (pdfContext->fGraphicsState.fPathClosed) { | |
886 pdfContext->fGraphicsState.fPath.reset(); | |
887 pdfContext->fGraphicsState.fPathClosed = false; | |
888 } | |
889 | |
890 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
891 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
892 double y2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
893 double x2 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
894 double y1 = pdfContext->fGraphicsState.fCurPosY; | |
895 double x1 = pdfContext->fGraphicsState.fCurPosX; | |
896 | |
897 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca
lar(y1), | |
898 SkDoubleToScalar(x2), SkDoubleToScal
ar(y2), | |
899 SkDoubleToScalar(x3), SkDoubleToScal
ar(y3)); | |
900 | |
901 pdfContext->fGraphicsState.fCurPosX = x3; | |
902 pdfContext->fGraphicsState.fCurPosY = y3; | |
903 | |
904 return kOK_PdfResult; | |
905 } | |
906 | |
907 static PdfResult PdfOp_y(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
908 if (pdfContext->fGraphicsState.fPathClosed) { | |
909 pdfContext->fGraphicsState.fPath.reset(); | |
910 pdfContext->fGraphicsState.fPathClosed = false; | |
911 } | |
912 | |
913 double y3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
914 double x3 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
915 double y2 = pdfContext->fGraphicsState.fCurPosY; | |
916 double x2 = pdfContext->fGraphicsState.fCurPosX; | |
917 double y1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
918 double x1 = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
919 | |
920 pdfContext->fGraphicsState.fPath.cubicTo(SkDoubleToScalar(x1), SkDoubleToSca
lar(y1), | |
921 SkDoubleToScalar(x2), SkDoubleToScal
ar(y2), | |
922 SkDoubleToScalar(x3), SkDoubleToScal
ar(y3)); | |
923 | |
924 pdfContext->fGraphicsState.fCurPosX = x3; | |
925 pdfContext->fGraphicsState.fCurPosY = y3; | |
926 | |
927 return kOK_PdfResult; | |
928 } | |
929 | |
930 static PdfResult PdfOp_re(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
931 if (pdfContext->fGraphicsState.fPathClosed) { | |
932 pdfContext->fGraphicsState.fPath.reset(); | |
933 pdfContext->fGraphicsState.fPathClosed = false; | |
934 } | |
935 | |
936 double height = pdfContext->fObjectStack.top()->numberValue(); pdfConte
xt->fObjectStack.pop(); | |
937 double width = pdfContext->fObjectStack.top()->numberValue(); pdfConte
xt->fObjectStack.pop(); | |
938 double y = pdfContext->fObjectStack.top()->numberValue(); pdfConte
xt->fObjectStack.pop(); | |
939 double x = pdfContext->fObjectStack.top()->numberValue(); pdfConte
xt->fObjectStack.pop(); | |
940 | |
941 pdfContext->fGraphicsState.fPath.addRect(SkDoubleToScalar(x), SkDoubleToScal
ar(y), | |
942 SkDoubleToScalar(x + width), SkDouble
ToScalar(y + height)); | |
943 | |
944 pdfContext->fGraphicsState.fCurPosX = x; | |
945 pdfContext->fGraphicsState.fCurPosY = y + height; | |
946 | |
947 return kOK_PdfResult; | |
948 } | |
949 | |
950 static PdfResult PdfOp_h(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
951 pdfContext->fGraphicsState.fPath.close(); | |
952 return kOK_PdfResult; | |
953 } | |
954 | |
955 static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, b
ool fill, bool stroke, bool close, bool evenOdd) { | |
956 SkPath path = pdfContext->fGraphicsState.fPath; | |
957 | |
958 if (close) { | |
959 path.close(); | |
960 } | |
961 | |
962 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
963 | |
964 SkPaint paint; | |
965 | |
966 SkPoint line[2]; | |
967 if (fill && !stroke && path.isLine(line)) { | |
968 paint.setStyle(SkPaint::kStroke_Style); | |
969 | |
970 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); | |
971 paint.setStrokeWidth(SkDoubleToScalar(0)); | |
972 | |
973 canvas->drawPath(path, paint); | |
974 } else { | |
975 if (fill) { | |
976 paint.setStyle(SkPaint::kFill_Style); | |
977 if (evenOdd) { | |
978 path.setFillType(SkPath::kEvenOdd_FillType); | |
979 } | |
980 | |
981 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); | |
982 | |
983 canvas->drawPath(path, paint); | |
984 } | |
985 | |
986 if (stroke) { | |
987 paint.setStyle(SkPaint::kStroke_Style); | |
988 | |
989 pdfContext->fGraphicsState.applyGraphicsState(&paint, true); | |
990 | |
991 path.setFillType(SkPath::kWinding_FillType); // reset it, just in c
ase it messes up the stroke | |
992 canvas->drawPath(path, paint); | |
993 } | |
994 } | |
995 | |
996 pdfContext->fGraphicsState.fPath.reset(); | |
997 // todo zoom ... other stuff ? | |
998 | |
999 if (pdfContext->fGraphicsState.fHasClipPathToApply) { | |
1000 #ifndef PDF_DEBUG_NO_CLIPING | |
1001 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kInters
ect_Op, true); | |
1002 #endif | |
1003 } | |
1004 | |
1005 //pdfContext->fGraphicsState.fClipPath.reset(); | |
1006 pdfContext->fGraphicsState.fHasClipPathToApply = false; | |
1007 | |
1008 return kPartial_PdfResult; | |
1009 | |
1010 } | |
1011 | |
1012 static PdfResult PdfOp_S(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1013 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, false, false); | |
1014 } | |
1015 | |
1016 static PdfResult PdfOp_s(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1017 return PdfOp_fillAndStroke(pdfContext, canvas, false, true, true, false); | |
1018 } | |
1019 | |
1020 static PdfResult PdfOp_F(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1021 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false); | |
1022 } | |
1023 | |
1024 static PdfResult PdfOp_f(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1025 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, false); | |
1026 } | |
1027 | |
1028 static PdfResult PdfOp_f_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
1029 return PdfOp_fillAndStroke(pdfContext, canvas, true, false, false, true); | |
1030 } | |
1031 | |
1032 static PdfResult PdfOp_B(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1033 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, false); | |
1034 } | |
1035 | |
1036 static PdfResult PdfOp_B_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
1037 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, false, true); | |
1038 } | |
1039 | |
1040 static PdfResult PdfOp_b(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1041 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, false); | |
1042 } | |
1043 | |
1044 static PdfResult PdfOp_b_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
1045 return PdfOp_fillAndStroke(pdfContext, canvas, true, true, true, true); | |
1046 } | |
1047 | |
1048 static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1049 canvas->setMatrix(pdfContext->fGraphicsState.fMatrix); | |
1050 if (pdfContext->fGraphicsState.fHasClipPathToApply) { | |
1051 #ifndef PDF_DEBUG_NO_CLIPING | |
1052 canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kInters
ect_Op, true); | |
1053 #endif | |
1054 } | |
1055 | |
1056 //pdfContext->fGraphicsState.fClipPath.reset(); | |
1057 pdfContext->fGraphicsState.fHasClipPathToApply = false; | |
1058 | |
1059 pdfContext->fGraphicsState.fPathClosed = true; | |
1060 | |
1061 return kOK_PdfResult; | |
1062 } | |
1063 | |
1064 static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1065 pdfContext->fGraphicsState.fTextBlock = true; | |
1066 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix; | |
1067 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix; | |
1068 | |
1069 return kPartial_PdfResult; | |
1070 } | |
1071 | |
1072 static PdfResult PdfOp_ET(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1073 if (!pdfContext->fGraphicsState.fTextBlock) { | |
1074 return kIgnoreError_PdfResult; | |
1075 } | |
1076 // TODO(edisonn): anything else to be done once we are done with draw text?
Like restore stack? | |
1077 return kPartial_PdfResult; | |
1078 } | |
1079 | |
1080 //font size Tf Set the text font, Tf | |
1081 //, to font and the text font size, Tfs, to size. font is the name of a | |
1082 //font resource in the Fontsubdictionary of the current resource dictionary; siz
e is | |
1083 //a number representing a scale factor. There is no initial value for either fon
t or | |
1084 //size; they must be specified explicitly using Tf before any text is shown. | |
1085 static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1086 pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->nu
mberValue(); pdfContext->fObjectStack.pop(); | |
1087 const char* fontName = pdfContext->fObjectStack.top()->nameValue();
pdfContext->fObjectStack.pop(); | |
1088 | |
1089 #ifdef PDF_TRACE | |
1090 printf("font name: %s\n", fontName); | |
1091 #endif | |
1092 | |
1093 if (pdfContext->fGraphicsState.fResources->Font(pdfContext->fPdfDoc)) { | |
1094 SkPdfObject* objFont = pdfContext->fGraphicsState.fResources->Font(pdfCo
ntext->fPdfDoc)->get(fontName); | |
1095 objFont = pdfContext->fPdfDoc->resolveReference(objFont); | |
1096 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapFontDicti
onary(objFont)) { | |
1097 // TODO(edisonn): try to recover and draw it any way? | |
1098 return kIgnoreError_PdfResult; | |
1099 } | |
1100 SkPdfFontDictionary* fd = (SkPdfFontDictionary*)objFont; | |
1101 | |
1102 SkPdfFont* skfont = SkPdfFont::fontFromPdfDictionary(pdfContext->fPdfDoc
, fd); | |
1103 | |
1104 if (skfont) { | |
1105 pdfContext->fGraphicsState.fSkFont = skfont; | |
1106 } | |
1107 } | |
1108 return kIgnoreError_PdfResult; | |
1109 } | |
1110 | |
1111 static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1112 if (!pdfContext->fGraphicsState.fTextBlock) { | |
1113 // TODO(edisonn): try to recover and draw it any way? | |
1114 return kIgnoreError_PdfResult; | |
1115 } | |
1116 | |
1117 PdfResult ret = DrawText(pdfContext, | |
1118 pdfContext->fObjectStack.top(), | |
1119 canvas); | |
1120 pdfContext->fObjectStack.pop(); | |
1121 | |
1122 return ret; | |
1123 } | |
1124 | |
1125 static PdfResult PdfOp_quote(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenL
ooper** looper) { | |
1126 if (!pdfContext->fGraphicsState.fTextBlock) { | |
1127 // TODO(edisonn): try to recover and draw it any way? | |
1128 return kIgnoreError_PdfResult; | |
1129 } | |
1130 | |
1131 PdfOp_T_star(pdfContext, canvas, looper); | |
1132 // Do not pop, and push, just transfer the param to Tj | |
1133 return PdfOp_Tj(pdfContext, canvas, looper); | |
1134 } | |
1135 | |
1136 static PdfResult PdfOp_doublequote(PdfContext* pdfContext, SkCanvas* canvas, Pdf
TokenLooper** looper) { | |
1137 if (!pdfContext->fGraphicsState.fTextBlock) { | |
1138 // TODO(edisonn): try to recover and draw it any way? | |
1139 return kIgnoreError_PdfResult; | |
1140 } | |
1141 | |
1142 SkPdfObject* str = pdfContext->fObjectStack.top(); pdfContext->fObject
Stack.pop(); | |
1143 SkPdfObject* ac = pdfContext->fObjectStack.top(); pdfContext->fObject
Stack.pop(); | |
1144 SkPdfObject* aw = pdfContext->fObjectStack.top(); pdfContext->fObject
Stack.pop(); | |
1145 | |
1146 pdfContext->fObjectStack.push(aw); | |
1147 PdfOp_Tw(pdfContext, canvas, looper); | |
1148 | |
1149 pdfContext->fObjectStack.push(ac); | |
1150 PdfOp_Tc(pdfContext, canvas, looper); | |
1151 | |
1152 pdfContext->fObjectStack.push(str); | |
1153 PdfOp_quote(pdfContext, canvas, looper); | |
1154 | |
1155 return kPartial_PdfResult; | |
1156 } | |
1157 | |
1158 static PdfResult PdfOp_TJ(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1159 if (!pdfContext->fGraphicsState.fTextBlock) { | |
1160 // TODO(edisonn): try to recover and draw it any way? | |
1161 return kIgnoreError_PdfResult; | |
1162 } | |
1163 | |
1164 SkPdfArray* array = (SkPdfArray*)pdfContext->fObjectStack.top(); | |
1165 pdfContext->fObjectStack.pop(); | |
1166 | |
1167 if (!array->isArray()) { | |
1168 return kIgnoreError_PdfResult; | |
1169 } | |
1170 | |
1171 for( int i=0; i<static_cast<int>(array->size()); i++ ) | |
1172 { | |
1173 if( (*array)[i]->isAnyString()) { | |
1174 SkPdfObject* obj = (*array)[i]; | |
1175 DrawText(pdfContext, | |
1176 obj, | |
1177 canvas); | |
1178 } else if ((*array)[i]->isNumber()) { | |
1179 double dx = (*array)[i]->numberValue(); | |
1180 SkMatrix matrix; | |
1181 matrix.setAll(SkDoubleToScalar(1), | |
1182 SkDoubleToScalar(0), | |
1183 // TODO(edisonn): use writing mode, vertical/horizonta
l. | |
1184 SkDoubleToScalar(-dx), // amount is substracted!!! | |
1185 SkDoubleToScalar(0), | |
1186 SkDoubleToScalar(1), | |
1187 SkDoubleToScalar(0), | |
1188 SkDoubleToScalar(0), | |
1189 SkDoubleToScalar(0), | |
1190 SkDoubleToScalar(1)); | |
1191 | |
1192 pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix); | |
1193 } | |
1194 } | |
1195 return kPartial_PdfResult; // TODO(edisonn): Implement fully DrawText befor
e returing OK. | |
1196 } | |
1197 | |
1198 static PdfResult PdfOp_CS_cs(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColo
rOperator* colorOperator) { | |
1199 colorOperator->fColorSpace = pdfContext->fObjectStack.top()->nameValue();
pdfContext->fObjectStack.pop(); | |
1200 return kOK_PdfResult; | |
1201 } | |
1202 | |
1203 static PdfResult PdfOp_CS(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1204 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking
); | |
1205 } | |
1206 | |
1207 static PdfResult PdfOp_cs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1208 return PdfOp_CS_cs(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok
ing); | |
1209 } | |
1210 | |
1211 static PdfResult PdfOp_SC_sc(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColo
rOperator* colorOperator) { | |
1212 double c[4]; | |
1213 // int64_t v[4]; | |
1214 | |
1215 int n = GetColorSpaceComponents(colorOperator->fColorSpace); | |
1216 | |
1217 bool doubles = true; | |
1218 if (strcmp(colorOperator->fColorSpace, "Indexed") == 0) { | |
1219 doubles = false; | |
1220 } | |
1221 | |
1222 #ifdef PDF_TRACE | |
1223 printf("color space = %s, N = %i\n", colorOperator->fColorSpace, n); | |
1224 #endif | |
1225 | |
1226 for (int i = n - 1; i >= 0 ; i--) { | |
1227 if (doubles) { | |
1228 c[i] = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
1229 // } else { | |
1230 // v[i] = pdfContext->fObjectStack.top()->intValue(); pdfConte
xt->fObjectStack.pop(); | |
1231 } | |
1232 } | |
1233 | |
1234 // TODO(edisonn): Now, set that color. Only DeviceRGB supported. | |
1235 // TODO(edisonn): do possible field values to enum at parsing time! | |
1236 // TODO(edisonn): support also abreviations /DeviceRGB == /RGB | |
1237 if (strcmp(colorOperator->fColorSpace, "DeviceRGB") == 0 || strcmp(colorOper
ator->fColorSpace, "RGB") == 0) { | |
1238 colorOperator->setRGBColor(SkColorSetRGB(255*c[0], 255*c[1], 255*c[2])); | |
1239 } | |
1240 return kPartial_PdfResult; | |
1241 } | |
1242 | |
1243 static PdfResult PdfOp_SC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1244 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking
); | |
1245 } | |
1246 | |
1247 static PdfResult PdfOp_sc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1248 return PdfOp_SC_sc(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok
ing); | |
1249 } | |
1250 | |
1251 static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfCo
lorOperator* colorOperator) { | |
1252 //SkPdfString* name; | |
1253 if (pdfContext->fObjectStack.top()->isName()) { | |
1254 // TODO(edisonn): get name, pass it | |
1255 pdfContext->fObjectStack.pop(); | |
1256 } | |
1257 | |
1258 // TODO(edisonn): SCN supports more color spaces than SCN. Read and implemen
t spec. | |
1259 PdfOp_SC_sc(pdfContext, canvas, colorOperator); | |
1260 | |
1261 return kPartial_PdfResult; | |
1262 } | |
1263 | |
1264 static PdfResult PdfOp_SCN(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
1265 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fStroki
ng); | |
1266 } | |
1267 | |
1268 static PdfResult PdfOp_scn(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
1269 return PdfOp_SCN_scn(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStr
oking); | |
1270 } | |
1271 | |
1272 static PdfResult PdfOp_G_g(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorO
perator* colorOperator) { | |
1273 /*double gray = */pdfContext->fObjectStack.top()->numberValue(); pdfCont
ext->fObjectStack.pop(); | |
1274 return kNYI_PdfResult; | |
1275 } | |
1276 | |
1277 static PdfResult PdfOp_G(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1278 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); | |
1279 } | |
1280 | |
1281 static PdfResult PdfOp_g(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1282 return PdfOp_G_g(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrokin
g); | |
1283 } | |
1284 | |
1285 static PdfResult PdfOp_RG_rg(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColo
rOperator* colorOperator) { | |
1286 double b = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
1287 double g = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
1288 double r = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fO
bjectStack.pop(); | |
1289 | |
1290 colorOperator->fColorSpace = "DeviceRGB"; | |
1291 colorOperator->setRGBColor(SkColorSetRGB(255*r, 255*g, 255*b)); | |
1292 return kOK_PdfResult; | |
1293 } | |
1294 | |
1295 static PdfResult PdfOp_RG(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1296 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking
); | |
1297 } | |
1298 | |
1299 static PdfResult PdfOp_rg(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1300 return PdfOp_RG_rg(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrok
ing); | |
1301 } | |
1302 | |
1303 static PdfResult PdfOp_K_k(PdfContext* pdfContext, SkCanvas* canvas, SkPdfColorO
perator* colorOperator) { | |
1304 // TODO(edisonn): spec has some rules about overprint, implement them. | |
1305 /*double k = */pdfContext->fObjectStack.top()->numberValue(); pdfContext
->fObjectStack.pop(); | |
1306 /*double y = */pdfContext->fObjectStack.top()->numberValue(); pdfContext
->fObjectStack.pop(); | |
1307 /*double m = */pdfContext->fObjectStack.top()->numberValue(); pdfContext
->fObjectStack.pop(); | |
1308 /*double c = */pdfContext->fObjectStack.top()->numberValue(); pdfContext
->fObjectStack.pop(); | |
1309 | |
1310 colorOperator->fColorSpace = "DeviceCMYK"; | |
1311 // TODO(edisonn): Set color. | |
1312 return kNYI_PdfResult; | |
1313 } | |
1314 | |
1315 static PdfResult PdfOp_K(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1316 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fStroking); | |
1317 } | |
1318 | |
1319 static PdfResult PdfOp_k(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1320 return PdfOp_K_k(pdfContext, canvas, &pdfContext->fGraphicsState.fNonStrokin
g); | |
1321 } | |
1322 | |
1323 static PdfResult PdfOp_W(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1324 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath; | |
1325 pdfContext->fGraphicsState.fHasClipPathToApply = true; | |
1326 | |
1327 return kOK_PdfResult; | |
1328 } | |
1329 | |
1330 static PdfResult PdfOp_W_star(PdfContext* pdfContext, SkCanvas* canvas, PdfToken
Looper** looper) { | |
1331 pdfContext->fGraphicsState.fClipPath = pdfContext->fGraphicsState.fPath; | |
1332 | |
1333 #ifdef PDF_TRACE | |
1334 if (pdfContext->fGraphicsState.fClipPath.isRect(NULL)) { | |
1335 printf("CLIP IS RECT\n"); | |
1336 } | |
1337 #endif | |
1338 | |
1339 // TODO(edisonn): there seem to be a bug with clipPath of a rect with even o
dd. | |
1340 pdfContext->fGraphicsState.fClipPath.setFillType(SkPath::kEvenOdd_FillType); | |
1341 pdfContext->fGraphicsState.fHasClipPathToApply = true; | |
1342 | |
1343 return kPartial_PdfResult; | |
1344 } | |
1345 | |
1346 static PdfResult PdfOp_BX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1347 *looper = new PdfCompatibilitySectionLooper(); | |
1348 return kOK_PdfResult; | |
1349 } | |
1350 | |
1351 static PdfResult PdfOp_EX(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1352 #ifdef ASSERT_BAD_PDF_OPS | |
1353 SkASSERT(false); // EX must be consumed by PdfCompatibilitySectionLooper, b
ut let's | |
1354 // have the assert when testing good pdfs. | |
1355 #endif | |
1356 return kIgnoreError_PdfResult; | |
1357 } | |
1358 | |
1359 static PdfResult PdfOp_BI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1360 *looper = new PdfInlineImageLooper(); | |
1361 return kOK_PdfResult; | |
1362 } | |
1363 | |
1364 static PdfResult PdfOp_ID(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1365 #ifdef ASSERT_BAD_PDF_OPS | |
1366 SkASSERT(false); // must be processed in inline image looper, but let's | |
1367 // have the assert when testing good pdfs. | |
1368 #endif | |
1369 return kIgnoreError_PdfResult; | |
1370 } | |
1371 | |
1372 static PdfResult PdfOp_EI(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1373 #ifdef ASSERT_BAD_PDF_OPS | |
1374 SkASSERT(false); // must be processed in inline image looper, but let's | |
1375 // have the assert when testing good pdfs. | |
1376 #endif | |
1377 return kIgnoreError_PdfResult; | |
1378 } | |
1379 | |
1380 //lineWidth w Set the line width in the graphics state (see “Line Width” on page
152). | |
1381 static PdfResult PdfOp_w(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1382 double lineWidth = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
1383 pdfContext->fGraphicsState.fLineWidth = lineWidth; | |
1384 | |
1385 return kOK_PdfResult; | |
1386 } | |
1387 | |
1388 //lineCap J Set the line cap style in the graphics state (see “Line Cap Style” o
n page 153). | |
1389 static PdfResult PdfOp_J(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1390 pdfContext->fObjectStack.pop(); | |
1391 //double lineCap = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
1392 | |
1393 return kNYI_PdfResult; | |
1394 } | |
1395 | |
1396 //lineJoin j Set the line join style in the graphics state (see “Line Join Style
” on page 153). | |
1397 static PdfResult PdfOp_j(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1398 pdfContext->fObjectStack.pop(); | |
1399 //double lineJoin = pdfContext->fObjectStack.top()->numberValue(); pdfCo
ntext->fObjectStack.pop(); | |
1400 | |
1401 return kNYI_PdfResult; | |
1402 } | |
1403 | |
1404 //miterLimit M Set the miter limit in the graphics state (see “Miter Limit” on p
age 153). | |
1405 static PdfResult PdfOp_M(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1406 pdfContext->fObjectStack.pop(); | |
1407 //double miterLimit = pdfContext->fObjectStack.top()->numberValue(); pdf
Context->fObjectStack.pop(); | |
1408 | |
1409 return kNYI_PdfResult; | |
1410 } | |
1411 | |
1412 //dashArray dashPhase d Set the line dash pattern in the graphics state (see “Li
ne Dash Pattern” on | |
1413 //page 155). | |
1414 static PdfResult PdfOp_d(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1415 pdfContext->fObjectStack.pop(); | |
1416 pdfContext->fObjectStack.pop(); | |
1417 | |
1418 return kNYI_PdfResult; | |
1419 } | |
1420 | |
1421 //intent ri (PDF 1.1) Set the color rendering intent in the graphics state (see
“Rendering Intents” on page 197). | |
1422 static PdfResult PdfOp_ri(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1423 pdfContext->fObjectStack.pop(); | |
1424 | |
1425 return kNYI_PdfResult; | |
1426 } | |
1427 | |
1428 //flatness i Set the flatness tolerance in the graphics state (see Section 6.5.1,
“Flatness | |
1429 //Tolerance”). flatness is a number in the range 0 to 100; a value of 0 speci- | |
1430 //fies the output device’s default flatness tolerance. | |
1431 static PdfResult PdfOp_i(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoope
r** looper) { | |
1432 pdfContext->fObjectStack.pop(); | |
1433 | |
1434 return kNYI_PdfResult; | |
1435 } | |
1436 | |
1437 //dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictN
ame is | |
1438 //the name of a graphics state parameter dictionary in the ExtGState subdictiona
ry of the current resource dictionary (see the next section). | |
1439 static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1440 const char* name = pdfContext->fObjectStack.top()->nameValue(); pdfContex
t->fObjectStack.pop(); | |
1441 | |
1442 #ifdef PDF_TRACE | |
1443 std::string str; | |
1444 #endif | |
1445 | |
1446 //Next, get the ExtGState Dictionary from the Resource Dictionary: | |
1447 SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources
->ExtGState(pdfContext->fPdfDoc); | |
1448 | |
1449 if (extGStateDictionary == NULL) { | |
1450 #ifdef PDF_TRACE | |
1451 printf("ExtGState is NULL!\n"); | |
1452 #endif | |
1453 return kIgnoreError_PdfResult; | |
1454 } | |
1455 | |
1456 SkPdfObject* value = pdfContext->fPdfDoc->resolveReference(extGStateDictiona
ry->get(name)); | |
1457 | |
1458 if (kNone_SkPdfObjectType == pdfContext->fPdfDoc->mapper()->mapGraphicsState
Dictionary(value)) { | |
1459 return kIgnoreError_PdfResult; | |
1460 } | |
1461 SkPdfGraphicsStateDictionary* gs = (SkPdfGraphicsStateDictionary*)value; | |
1462 | |
1463 // TODO(edisonn): now load all those properties in graphic state. | |
1464 if (gs == NULL) { | |
1465 return kIgnoreError_PdfResult; | |
1466 } | |
1467 | |
1468 if (gs->has_CA()) { | |
1469 pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA(pdfContext->fPdfD
oc); | |
1470 } | |
1471 | |
1472 if (gs->has_ca()) { | |
1473 pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca(pdfContext->fP
dfDoc); | |
1474 } | |
1475 | |
1476 if (gs->has_LW()) { | |
1477 pdfContext->fGraphicsState.fLineWidth = gs->LW(pdfContext->fPdfDoc); | |
1478 } | |
1479 | |
1480 return kNYI_PdfResult; | |
1481 } | |
1482 | |
1483 //charSpace Tc Set the character spacing, Tc | |
1484 //, to charSpace, which is a number expressed in unscaled text space units. Char
acter spacing is used by the Tj, TJ, and ' operators. | |
1485 //Initial value: 0. | |
1486 PdfResult PdfOp_Tc(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo
oper) { | |
1487 double charSpace = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
1488 pdfContext->fGraphicsState.fCharSpace = charSpace; | |
1489 | |
1490 return kOK_PdfResult; | |
1491 } | |
1492 | |
1493 //wordSpace Tw Set the word spacing, T | |
1494 //w | |
1495 //, to wordSpace, which is a number expressed in unscaled | |
1496 //text space units. Word spacing is used by the Tj, TJ, and ' operators. Initial | |
1497 //value: 0. | |
1498 PdfResult PdfOp_Tw(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** lo
oper) { | |
1499 double wordSpace = pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
1500 pdfContext->fGraphicsState.fWordSpace = wordSpace; | |
1501 | |
1502 return kOK_PdfResult; | |
1503 } | |
1504 | |
1505 //scale Tz Set the horizontal scaling, Th | |
1506 //, to (scale ˜ 100). scale is a number specifying the | |
1507 //percentage of the normal width. Initial value: 100 (normal width). | |
1508 static PdfResult PdfOp_Tz(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1509 /*double scale = */pdfContext->fObjectStack.top()->numberValue(); pdfCon
text->fObjectStack.pop(); | |
1510 | |
1511 return kNYI_PdfResult; | |
1512 } | |
1513 | |
1514 //render Tr Set the text rendering mode, T | |
1515 //mode, to render, which is an integer. Initial value: 0. | |
1516 static PdfResult PdfOp_Tr(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1517 /*double render = */pdfContext->fObjectStack.top()->numberValue(); pdfCo
ntext->fObjectStack.pop(); | |
1518 | |
1519 return kNYI_PdfResult; | |
1520 } | |
1521 //rise Ts Set the text rise, Trise, to rise, which is a number expressed in unsc
aled text space | |
1522 //units. Initial value: 0. | |
1523 static PdfResult PdfOp_Ts(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1524 /*double rise = */pdfContext->fObjectStack.top()->numberValue(); pdfCont
ext->fObjectStack.pop(); | |
1525 | |
1526 return kNYI_PdfResult; | |
1527 } | |
1528 | |
1529 //wx wy d0 | |
1530 static PdfResult PdfOp_d0(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1531 pdfContext->fObjectStack.pop(); | |
1532 pdfContext->fObjectStack.pop(); | |
1533 | |
1534 return kNYI_PdfResult; | |
1535 } | |
1536 | |
1537 //wx wy llx lly urx ury d1 | |
1538 static PdfResult PdfOp_d1(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1539 pdfContext->fObjectStack.pop(); | |
1540 pdfContext->fObjectStack.pop(); | |
1541 pdfContext->fObjectStack.pop(); | |
1542 pdfContext->fObjectStack.pop(); | |
1543 pdfContext->fObjectStack.pop(); | |
1544 pdfContext->fObjectStack.pop(); | |
1545 | |
1546 return kNYI_PdfResult; | |
1547 } | |
1548 | |
1549 //name sh | |
1550 static PdfResult PdfOp_sh(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1551 pdfContext->fObjectStack.pop(); | |
1552 | |
1553 return kNYI_PdfResult; | |
1554 } | |
1555 | |
1556 //name Do | |
1557 static PdfResult PdfOp_Do(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1558 const char* name = pdfContext->fObjectStack.top()->nameValue(); pdfContex
t->fObjectStack.pop(); | |
1559 | |
1560 SkPdfDictionary* xObject = pdfContext->fGraphicsState.fResources->XObject(p
dfContext->fPdfDoc); | |
1561 | |
1562 if (xObject == NULL) { | |
1563 #ifdef PDF_TRACE | |
1564 printf("XObject is NULL!\n"); | |
1565 #endif | |
1566 return kIgnoreError_PdfResult; | |
1567 } | |
1568 | |
1569 SkPdfObject* value = xObject->get(name); | |
1570 value = pdfContext->fPdfDoc->resolveReference(value); | |
1571 | |
1572 #ifdef PDF_TRACE | |
1573 // value->ToString(str); | |
1574 // printf("Do object value: %s\n", str); | |
1575 #endif | |
1576 | |
1577 return doXObject(pdfContext, canvas, value); | |
1578 } | |
1579 | |
1580 //tag MP Designate a marked-content point. tag is a name object indicating the r
ole or | |
1581 //significance of the point. | |
1582 static PdfResult PdfOp_MP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1583 pdfContext->fObjectStack.pop(); | |
1584 | |
1585 return kNYI_PdfResult; | |
1586 } | |
1587 | |
1588 //tag properties DP Designate a marked-content point with an associated property
list. tag is a | |
1589 //name object indicating the role or significance of the point; properties is | |
1590 //either an inline dictionary containing the property list or a name object | |
1591 //associated with it in the Properties subdictionary of the current resource | |
1592 //dictionary (see Section 9.5.1, “Property Lists”). | |
1593 static PdfResult PdfOp_DP(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
er** looper) { | |
1594 pdfContext->fObjectStack.pop(); | |
1595 pdfContext->fObjectStack.pop(); | |
1596 | |
1597 return kNYI_PdfResult; | |
1598 } | |
1599 | |
1600 //tag BMC Begin a marked-content sequence terminated by a balancing EMC operator
. | |
1601 //tag is a name object indicating the role or significance of the sequence. | |
1602 static PdfResult PdfOp_BMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
1603 pdfContext->fObjectStack.pop(); | |
1604 | |
1605 return kNYI_PdfResult; | |
1606 } | |
1607 | |
1608 //tag properties BDC Begin a marked-content sequence with an associated property
list, terminated | |
1609 //by a balancing EMCoperator. tag is a name object indicating the role or signif
icance of the sequence; propertiesis either an inline dictionary containing the | |
1610 //property list or a name object associated with it in the Properties subdiction
ary of the current resource dictionary (see Section 9.5.1, “Property Lists”). | |
1611 static PdfResult PdfOp_BDC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
1612 pdfContext->fObjectStack.pop(); | |
1613 pdfContext->fObjectStack.pop(); | |
1614 | |
1615 return kNYI_PdfResult; | |
1616 } | |
1617 | |
1618 //— EMC End a marked-content sequence begun by a BMC or BDC operator. | |
1619 static PdfResult PdfOp_EMC(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoo
per** looper) { | |
1620 return kNYI_PdfResult; | |
1621 } | |
1622 | |
1623 static void initPdfOperatorRenderes() { | |
1624 static bool gInitialized = false; | |
1625 if (gInitialized) { | |
1626 return; | |
1627 } | |
1628 | |
1629 gPdfOps["q"] = PdfOp_q; | |
1630 gPdfOps["Q"] = PdfOp_Q; | |
1631 gPdfOps["cm"] = PdfOp_cm; | |
1632 | |
1633 gPdfOps["TD"] = PdfOp_TD; | |
1634 gPdfOps["Td"] = PdfOp_Td; | |
1635 gPdfOps["Tm"] = PdfOp_Tm; | |
1636 gPdfOps["T*"] = PdfOp_T_star; | |
1637 | |
1638 gPdfOps["m"] = PdfOp_m; | |
1639 gPdfOps["l"] = PdfOp_l; | |
1640 gPdfOps["c"] = PdfOp_c; | |
1641 gPdfOps["v"] = PdfOp_v; | |
1642 gPdfOps["y"] = PdfOp_y; | |
1643 gPdfOps["h"] = PdfOp_h; | |
1644 gPdfOps["re"] = PdfOp_re; | |
1645 | |
1646 gPdfOps["S"] = PdfOp_S; | |
1647 gPdfOps["s"] = PdfOp_s; | |
1648 gPdfOps["f"] = PdfOp_f; | |
1649 gPdfOps["F"] = PdfOp_F; | |
1650 gPdfOps["f*"] = PdfOp_f_star; | |
1651 gPdfOps["B"] = PdfOp_B; | |
1652 gPdfOps["B*"] = PdfOp_B_star; | |
1653 gPdfOps["b"] = PdfOp_b; | |
1654 gPdfOps["b*"] = PdfOp_b_star; | |
1655 gPdfOps["n"] = PdfOp_n; | |
1656 | |
1657 gPdfOps["BT"] = PdfOp_BT; | |
1658 gPdfOps["ET"] = PdfOp_ET; | |
1659 | |
1660 gPdfOps["Tj"] = PdfOp_Tj; | |
1661 gPdfOps["'"] = PdfOp_quote; | |
1662 gPdfOps["\""] = PdfOp_doublequote; | |
1663 gPdfOps["TJ"] = PdfOp_TJ; | |
1664 | |
1665 gPdfOps["CS"] = PdfOp_CS; | |
1666 gPdfOps["cs"] = PdfOp_cs; | |
1667 gPdfOps["SC"] = PdfOp_SC; | |
1668 gPdfOps["SCN"] = PdfOp_SCN; | |
1669 gPdfOps["sc"] = PdfOp_sc; | |
1670 gPdfOps["scn"] = PdfOp_scn; | |
1671 gPdfOps["G"] = PdfOp_G; | |
1672 gPdfOps["g"] = PdfOp_g; | |
1673 gPdfOps["RG"] = PdfOp_RG; | |
1674 gPdfOps["rg"] = PdfOp_rg; | |
1675 gPdfOps["K"] = PdfOp_K; | |
1676 gPdfOps["k"] = PdfOp_k; | |
1677 | |
1678 gPdfOps["W"] = PdfOp_W; | |
1679 gPdfOps["W*"] = PdfOp_W_star; | |
1680 | |
1681 gPdfOps["BX"] = PdfOp_BX; | |
1682 gPdfOps["EX"] = PdfOp_EX; | |
1683 | |
1684 gPdfOps["BI"] = PdfOp_BI; | |
1685 gPdfOps["ID"] = PdfOp_ID; | |
1686 gPdfOps["EI"] = PdfOp_EI; | |
1687 | |
1688 gPdfOps["w"] = PdfOp_w; | |
1689 gPdfOps["J"] = PdfOp_J; | |
1690 gPdfOps["j"] = PdfOp_j; | |
1691 gPdfOps["M"] = PdfOp_M; | |
1692 gPdfOps["d"] = PdfOp_d; | |
1693 gPdfOps["ri"] = PdfOp_ri; | |
1694 gPdfOps["i"] = PdfOp_i; | |
1695 gPdfOps["gs"] = PdfOp_gs; | |
1696 | |
1697 gPdfOps["Tc"] = PdfOp_Tc; | |
1698 gPdfOps["Tw"] = PdfOp_Tw; | |
1699 gPdfOps["Tz"] = PdfOp_Tz; | |
1700 gPdfOps["TL"] = PdfOp_TL; | |
1701 gPdfOps["Tf"] = PdfOp_Tf; | |
1702 gPdfOps["Tr"] = PdfOp_Tr; | |
1703 gPdfOps["Ts"] = PdfOp_Ts; | |
1704 | |
1705 gPdfOps["d0"] = PdfOp_d0; | |
1706 gPdfOps["d1"] = PdfOp_d1; | |
1707 | |
1708 gPdfOps["sh"] = PdfOp_sh; | |
1709 | |
1710 gPdfOps["Do"] = PdfOp_Do; | |
1711 | |
1712 gPdfOps["MP"] = PdfOp_MP; | |
1713 gPdfOps["DP"] = PdfOp_DP; | |
1714 gPdfOps["BMC"] = PdfOp_BMC; | |
1715 gPdfOps["BDC"] = PdfOp_BDC; | |
1716 gPdfOps["EMC"] = PdfOp_EMC; | |
1717 | |
1718 gInitialized = true; | |
1719 } | |
1720 | |
1721 class InitPdfOps { | |
1722 public: | |
1723 InitPdfOps() { | |
1724 initPdfOperatorRenderes(); | |
1725 } | |
1726 }; | |
1727 | |
1728 InitPdfOps gInitPdfOps; | |
1729 | |
1730 void reportPdfRenderStats() { | |
1731 std::map<std::string, int>::iterator iter; | |
1732 | |
1733 for (int i = 0 ; i < kCount_PdfResult; i++) { | |
1734 for (iter = gRenderStats[i].begin(); iter != gRenderStats[i].end(); ++it
er) { | |
1735 printf("%s: %s -> count %i\n", gRenderStatsNames[i], iter->first.c_s
tr(), iter->second); | |
1736 } | |
1737 } | |
1738 } | |
1739 | |
1740 PdfResult PdfMainLooper::consumeToken(PdfToken& token) { | |
1741 char keyword[256]; | |
1742 | |
1743 if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256) | |
1744 { | |
1745 strncpy(keyword, token.fKeyword, token.fKeywordLength); | |
1746 keyword[token.fKeywordLength] = '\0'; | |
1747 // TODO(edisonn): log trace flag (verbose, error, info, warning, ...) | |
1748 PdfOperatorRenderer pdfOperatorRenderer = gPdfOps[keyword]; | |
1749 if (pdfOperatorRenderer) { | |
1750 // caller, main work is done by pdfOperatorRenderer(...) | |
1751 PdfTokenLooper* childLooper = NULL; | |
1752 gRenderStats[pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper)
][keyword]++; | |
1753 | |
1754 if (childLooper) { | |
1755 childLooper->setUp(this); | |
1756 childLooper->loop(); | |
1757 delete childLooper; | |
1758 } | |
1759 } else { | |
1760 gRenderStats[kUnsupported_PdfResult][keyword]++; | |
1761 } | |
1762 } | |
1763 else if (token.fType == kObject_TokenType) | |
1764 { | |
1765 fPdfContext->fObjectStack.push( token.fObject ); | |
1766 } | |
1767 else { | |
1768 // TODO(edisonn): deine or use assert not reached | |
1769 return kIgnoreError_PdfResult; | |
1770 } | |
1771 return kOK_PdfResult; | |
1772 } | |
1773 | |
1774 void PdfMainLooper::loop() { | |
1775 PdfToken token; | |
1776 while (readToken(fTokenizer, &token)) { | |
1777 consumeToken(token); | |
1778 } | |
1779 } | |
1780 | |
1781 PdfResult PdfInlineImageLooper::consumeToken(PdfToken& token) { | |
1782 //pdfContext.fInlineImage.fKeyValuePairs[key] = value; | |
1783 return kNYI_PdfResult; | |
1784 } | |
1785 | |
1786 void PdfInlineImageLooper::loop() { | |
1787 PdfToken token; | |
1788 while (readToken(fTokenizer, &token)) { | |
1789 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") ==
0) { | |
1790 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper(); | |
1791 looper->setUp(this); | |
1792 looper->loop(); | |
1793 } else { | |
1794 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EI"
) == 0) { | |
1795 done(); | |
1796 return; | |
1797 } | |
1798 | |
1799 consumeToken(token); | |
1800 } | |
1801 } | |
1802 // TODO(edisonn): report error/warning, EOF without EI. | |
1803 } | |
1804 | |
1805 PdfResult PdfInlineImageLooper::done() { | |
1806 | |
1807 // TODO(edisonn): long to short names | |
1808 // TODO(edisonn): set properties in a map | |
1809 // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilitie
s to uncompress | |
1810 // the stream. | |
1811 | |
1812 SkBitmap bitmap; | |
1813 setup_bitmap(&bitmap, 50, 50, SK_ColorRED); | |
1814 | |
1815 // TODO(edisonn): matrix use. | |
1816 // Draw dummy red square, to show the prezence of the inline image. | |
1817 fCanvas->drawBitmap(bitmap, | |
1818 SkDoubleToScalar(0), | |
1819 SkDoubleToScalar(0), | |
1820 NULL); | |
1821 return kNYI_PdfResult; | |
1822 } | |
1823 | |
1824 PdfResult PdfCompatibilitySectionLooper::consumeToken(PdfToken& token) { | |
1825 return fParent->consumeToken(token); | |
1826 } | |
1827 | |
1828 void PdfCompatibilitySectionLooper::loop() { | |
1829 // TODO(edisonn): save stacks position, or create a new stack? | |
1830 // TODO(edisonn): what happens if we pop out more variables then when we sta
rted? | |
1831 // restore them? fail? We could create a new operands stack for every new BX
/EX section, | |
1832 // pop-ing too much will not affect outside the section. | |
1833 PdfToken token; | |
1834 while (readToken(fTokenizer, &token)) { | |
1835 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") ==
0) { | |
1836 PdfTokenLooper* looper = new PdfCompatibilitySectionLooper(); | |
1837 looper->setUp(this); | |
1838 looper->loop(); | |
1839 delete looper; | |
1840 } else { | |
1841 if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "EX"
) == 0) break; | |
1842 fParent->consumeToken(token); | |
1843 } | |
1844 } | |
1845 // TODO(edisonn): restore stack. | |
1846 } | |
1847 | |
1848 // TODO(edisonn): fix PoDoFo load ~/crashing/Shading.pdf | |
1849 // TODO(edisonn): Add API for Forms viewing and editing | |
1850 // e.g. SkBitmap getPage(int page); | |
1851 // int formsCount(); | |
1852 // SkForm getForm(int formID); // SkForm(SkRect, .. other data) | |
1853 // TODO (edisonn): Add intend when loading pdf, for example: for viewing, parsin
g all content, ... | |
1854 // if we load the first page, and we zoom to fit to screen horizontally, then lo
ad only those | |
1855 // resources needed, so the preview is fast. | |
1856 // TODO (edisonn): hide parser/tokenizer behind and interface and a query langua
ge, and resolve | |
1857 // references automatically. | |
1858 | |
1859 bool SkPdfViewer::load(const SkString inputFileName, SkPicture* out) { | |
1860 std::cout << "PDF Loaded: " << inputFileName.c_str() << std::endl; | |
1861 | |
1862 SkNativeParsedPDF* doc = new SkNativeParsedPDF(inputFileName.c_str()); | |
1863 if (!doc->pages()) | |
1864 { | |
1865 std::cout << "ERROR: Empty PDF Document" << inputFileName.c_str() << std
::endl; | |
1866 return false; | |
1867 } else { | |
1868 | |
1869 for (int pn = 0; pn < doc->pages(); ++pn) { | |
1870 // TODO(edisonn): implement inheritance properties as per PDF spec | |
1871 //SkRect rect = page->MediaBox(); | |
1872 SkRect rect = doc->MediaBox(pn); | |
1873 | |
1874 #ifdef PDF_TRACE | |
1875 printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rect.wi
dth()), SkScalarToDouble(rect.height())); | |
1876 #endif | |
1877 | |
1878 // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to
use? | |
1879 | |
1880 SkBitmap bitmap; | |
1881 #ifdef PDF_DEBUG_3X | |
1882 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (
int)SkScalarToDouble(rect.height())); | |
1883 #else | |
1884 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkSc
alarToDouble(rect.height())); | |
1885 #endif | |
1886 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap))); | |
1887 SkCanvas canvas(device); | |
1888 | |
1889 gDumpBitmap = &bitmap; | |
1890 | |
1891 gDumpCanvas = &canvas; | |
1892 doc->drawPage(pn, &canvas); | |
1893 | |
1894 SkString out; | |
1895 if (doc->pages() > 1) { | |
1896 out.appendf("%s-%i.png", inputFileName.c_str(), pn); | |
1897 } else { | |
1898 out = inputFileName; | |
1899 // .pdf -> .png | |
1900 out[out.size() - 2] = 'n'; | |
1901 out[out.size() - 1] = 'g'; | |
1902 } | |
1903 SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG
_Type, 100); | |
1904 } | |
1905 return true; | |
1906 } | |
1907 | |
1908 return true; | |
1909 } | |
OLD | NEW |