Home | History | Annotate | Download | only in pdf
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkData.h"
     11 #include "SkGeometry.h"
     12 #include "SkPaint.h"
     13 #include "SkPath.h"
     14 #include "SkPDFResourceDict.h"
     15 #include "SkPDFUtils.h"
     16 #include "SkStream.h"
     17 #include "SkString.h"
     18 #include "SkPDFTypes.h"
     19 
     20 //static
     21 SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) {
     22     SkPDFArray* result = new SkPDFArray();
     23     result->reserve(4);
     24     result->appendScalar(rect.fLeft);
     25     result->appendScalar(rect.fTop);
     26     result->appendScalar(rect.fRight);
     27     result->appendScalar(rect.fBottom);
     28     return result;
     29 }
     30 
     31 // static
     32 SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
     33     SkScalar values[6];
     34     if (!matrix.asAffine(values)) {
     35         SkMatrix::SetAffineIdentity(values);
     36     }
     37 
     38     SkPDFArray* result = new SkPDFArray;
     39     result->reserve(6);
     40     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
     41         result->appendScalar(values[i]);
     42     }
     43     return result;
     44 }
     45 
     46 // static
     47 void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
     48     SkScalar values[6];
     49     if (!matrix.asAffine(values)) {
     50         SkMatrix::SetAffineIdentity(values);
     51     }
     52     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
     53         SkPDFScalar::Append(values[i], content);
     54         content->writeText(" ");
     55     }
     56     content->writeText("cm\n");
     57 }
     58 
     59 // static
     60 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
     61     SkPDFScalar::Append(x, content);
     62     content->writeText(" ");
     63     SkPDFScalar::Append(y, content);
     64     content->writeText(" m\n");
     65 }
     66 
     67 // static
     68 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
     69     SkPDFScalar::Append(x, content);
     70     content->writeText(" ");
     71     SkPDFScalar::Append(y, content);
     72     content->writeText(" l\n");
     73 }
     74 
     75 // static
     76 void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
     77                              SkScalar ctl2X, SkScalar ctl2Y,
     78                              SkScalar dstX, SkScalar dstY, SkWStream* content) {
     79     SkString cmd("y\n");
     80     SkPDFScalar::Append(ctl1X, content);
     81     content->writeText(" ");
     82     SkPDFScalar::Append(ctl1Y, content);
     83     content->writeText(" ");
     84     if (ctl2X != dstX || ctl2Y != dstY) {
     85         cmd.set("c\n");
     86         SkPDFScalar::Append(ctl2X, content);
     87         content->writeText(" ");
     88         SkPDFScalar::Append(ctl2Y, content);
     89         content->writeText(" ");
     90     }
     91     SkPDFScalar::Append(dstX, content);
     92     content->writeText(" ");
     93     SkPDFScalar::Append(dstY, content);
     94     content->writeText(" ");
     95     content->writeText(cmd.c_str());
     96 }
     97 
     98 // static
     99 void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
    100     // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
    101     SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
    102 
    103     SkPDFScalar::Append(rect.fLeft, content);
    104     content->writeText(" ");
    105     SkPDFScalar::Append(bottom, content);
    106     content->writeText(" ");
    107     SkPDFScalar::Append(rect.width(), content);
    108     content->writeText(" ");
    109     SkPDFScalar::Append(rect.height(), content);
    110     content->writeText(" re\n");
    111 }
    112 
    113 // static
    114 void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
    115                           SkWStream* content) {
    116     // Filling a path with no area results in a drawing in PDF renderers but
    117     // Chrome expects to be able to draw some such entities with no visible
    118     // result, so we detect those cases and discard the drawing for them.
    119     // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
    120     enum SkipFillState {
    121         kEmpty_SkipFillState         = 0,
    122         kSingleLine_SkipFillState    = 1,
    123         kNonSingleLine_SkipFillState = 2,
    124     };
    125     SkipFillState fillState = kEmpty_SkipFillState;
    126     if (paintStyle != SkPaint::kFill_Style) {
    127         fillState = kNonSingleLine_SkipFillState;
    128     }
    129     SkPoint lastMovePt = SkPoint::Make(0,0);
    130     SkDynamicMemoryWStream currentSegment;
    131     SkPoint args[4];
    132     SkPath::Iter iter(path, false);
    133     for (SkPath::Verb verb = iter.next(args);
    134          verb != SkPath::kDone_Verb;
    135          verb = iter.next(args)) {
    136         // args gets all the points, even the implicit first point.
    137         switch (verb) {
    138             case SkPath::kMove_Verb:
    139                 MoveTo(args[0].fX, args[0].fY, &currentSegment);
    140                 lastMovePt = args[0];
    141                 fillState = kEmpty_SkipFillState;
    142                 break;
    143             case SkPath::kLine_Verb:
    144                 AppendLine(args[1].fX, args[1].fY, &currentSegment);
    145                 if (fillState == kEmpty_SkipFillState) {
    146                    if (args[0] != lastMovePt) {
    147                        fillState = kSingleLine_SkipFillState;
    148                    }
    149                 } else if (fillState == kSingleLine_SkipFillState) {
    150                     fillState = kNonSingleLine_SkipFillState;
    151                 }
    152                 break;
    153             case SkPath::kQuad_Verb: {
    154                 SkPoint cubic[4];
    155                 SkConvertQuadToCubic(args, cubic);
    156                 AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
    157                             cubic[3].fX, cubic[3].fY, &currentSegment);
    158                 fillState = kNonSingleLine_SkipFillState;
    159                 break;
    160             }
    161             case SkPath::kCubic_Verb:
    162                 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
    163                             args[3].fX, args[3].fY, &currentSegment);
    164                 fillState = kNonSingleLine_SkipFillState;
    165                 break;
    166             case SkPath::kClose_Verb:
    167                 if (fillState != kSingleLine_SkipFillState) {
    168                     ClosePath(&currentSegment);
    169                     SkData* data = currentSegment.copyToData();
    170                     content->write(data->data(), data->size());
    171                     data->unref();
    172                 }
    173                 currentSegment.reset();
    174                 break;
    175             default:
    176                 SkASSERT(false);
    177                 break;
    178         }
    179     }
    180     if (currentSegment.bytesWritten() > 0) {
    181         SkData* data = currentSegment.copyToData();
    182         content->write(data->data(), data->size());
    183         data->unref();
    184     }
    185 }
    186 
    187 // static
    188 void SkPDFUtils::ClosePath(SkWStream* content) {
    189     content->writeText("h\n");
    190 }
    191 
    192 // static
    193 void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
    194                            SkWStream* content) {
    195     if (style == SkPaint::kFill_Style) {
    196         content->writeText("f");
    197     } else if (style == SkPaint::kStrokeAndFill_Style) {
    198         content->writeText("B");
    199     } else if (style == SkPaint::kStroke_Style) {
    200         content->writeText("S");
    201     }
    202 
    203     if (style != SkPaint::kStroke_Style) {
    204         NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
    205         NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
    206         if (fill == SkPath::kEvenOdd_FillType) {
    207             content->writeText("*");
    208         }
    209     }
    210     content->writeText("\n");
    211 }
    212 
    213 // static
    214 void SkPDFUtils::StrokePath(SkWStream* content) {
    215     SkPDFUtils::PaintPath(
    216         SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
    217 }
    218 
    219 // static
    220 void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
    221     content->writeText("/");
    222     content->writeText(SkPDFResourceDict::getResourceName(
    223             SkPDFResourceDict::kXObject_ResourceType,
    224             objectIndex).c_str());
    225     content->writeText(" Do\n");
    226 }
    227 
    228 // static
    229 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
    230     content->writeText("/");
    231     content->writeText(SkPDFResourceDict::getResourceName(
    232             SkPDFResourceDict::kExtGState_ResourceType,
    233             objectIndex).c_str());
    234     content->writeText(" gs\n");
    235 }
    236 
    237 // static
    238 void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
    239     // Select Pattern color space (CS, cs) and set pattern object as current
    240     // color (SCN, scn)
    241     SkString resourceName = SkPDFResourceDict::getResourceName(
    242             SkPDFResourceDict::kPattern_ResourceType,
    243             objectIndex);
    244     content->writeText("/Pattern CS/Pattern cs/");
    245     content->writeText(resourceName.c_str());
    246     content->writeText(" SCN/");
    247     content->writeText(resourceName.c_str());
    248     content->writeText(" scn\n");
    249 }
    250