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