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