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