Home | History | Annotate | Download | only in pdf
      1 /*
      2  * Copyright 2011 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 
      9 #include "SkData.h"
     10 #include "SkGeometry.h"
     11 #include "SkPaint.h"
     12 #include "SkPath.h"
     13 #include "SkPDFResourceDict.h"
     14 #include "SkPDFUtils.h"
     15 #include "SkStream.h"
     16 #include "SkString.h"
     17 #include "SkPDFTypes.h"
     18 
     19 //static
     20 SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) {
     21     SkPDFArray* result = new SkPDFArray();
     22     result->reserve(4);
     23     result->appendScalar(rect.fLeft);
     24     result->appendScalar(rect.fTop);
     25     result->appendScalar(rect.fRight);
     26     result->appendScalar(rect.fBottom);
     27     return result;
     28 }
     29 
     30 // static
     31 SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
     32     SkScalar values[6];
     33     if (!matrix.asAffine(values)) {
     34         SkMatrix::SetAffineIdentity(values);
     35     }
     36 
     37     SkPDFArray* result = new SkPDFArray;
     38     result->reserve(6);
     39     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
     40         result->appendScalar(values[i]);
     41     }
     42     return result;
     43 }
     44 
     45 // static
     46 void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
     47     SkScalar values[6];
     48     if (!matrix.asAffine(values)) {
     49         SkMatrix::SetAffineIdentity(values);
     50     }
     51     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
     52         SkPDFUtils::AppendScalar(values[i], content);
     53         content->writeText(" ");
     54     }
     55     content->writeText("cm\n");
     56 }
     57 
     58 // static
     59 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
     60     SkPDFUtils::AppendScalar(x, content);
     61     content->writeText(" ");
     62     SkPDFUtils::AppendScalar(y, content);
     63     content->writeText(" m\n");
     64 }
     65 
     66 // static
     67 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
     68     SkPDFUtils::AppendScalar(x, content);
     69     content->writeText(" ");
     70     SkPDFUtils::AppendScalar(y, content);
     71     content->writeText(" l\n");
     72 }
     73 
     74 // static
     75 void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
     76                              SkScalar ctl2X, SkScalar ctl2Y,
     77                              SkScalar dstX, SkScalar dstY, SkWStream* content) {
     78     SkString cmd("y\n");
     79     SkPDFUtils::AppendScalar(ctl1X, content);
     80     content->writeText(" ");
     81     SkPDFUtils::AppendScalar(ctl1Y, content);
     82     content->writeText(" ");
     83     if (ctl2X != dstX || ctl2Y != dstY) {
     84         cmd.set("c\n");
     85         SkPDFUtils::AppendScalar(ctl2X, content);
     86         content->writeText(" ");
     87         SkPDFUtils::AppendScalar(ctl2Y, content);
     88         content->writeText(" ");
     89     }
     90     SkPDFUtils::AppendScalar(dstX, content);
     91     content->writeText(" ");
     92     SkPDFUtils::AppendScalar(dstY, content);
     93     content->writeText(" ");
     94     content->writeText(cmd.c_str());
     95 }
     96 
     97 static void append_quad(const SkPoint quad[], SkWStream* content) {
     98     SkPoint cubic[4];
     99     SkConvertQuadToCubic(quad, cubic);
    100     SkPDFUtils::AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
    101                             cubic[3].fX, cubic[3].fY, content);
    102 }
    103 
    104 // static
    105 void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
    106     // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
    107     SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
    108 
    109     SkPDFUtils::AppendScalar(rect.fLeft, content);
    110     content->writeText(" ");
    111     SkPDFUtils::AppendScalar(bottom, content);
    112     content->writeText(" ");
    113     SkPDFUtils::AppendScalar(rect.width(), content);
    114     content->writeText(" ");
    115     SkPDFUtils::AppendScalar(rect.height(), content);
    116     content->writeText(" re\n");
    117 }
    118 
    119 // static
    120 void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
    121                           SkWStream* content) {
    122     // Filling a path with no area results in a drawing in PDF renderers but
    123     // Chrome expects to be able to draw some such entities with no visible
    124     // result, so we detect those cases and discard the drawing for them.
    125     // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
    126     enum SkipFillState {
    127         kEmpty_SkipFillState         = 0,
    128         kSingleLine_SkipFillState    = 1,
    129         kNonSingleLine_SkipFillState = 2,
    130     };
    131     SkipFillState fillState = kEmpty_SkipFillState;
    132     if (paintStyle != SkPaint::kFill_Style) {
    133         fillState = kNonSingleLine_SkipFillState;
    134     }
    135     SkPoint lastMovePt = SkPoint::Make(0,0);
    136     SkDynamicMemoryWStream currentSegment;
    137     SkPoint args[4];
    138     SkPath::Iter iter(path, false);
    139     for (SkPath::Verb verb = iter.next(args); verb != SkPath::kDone_Verb; verb = iter.next(args)) {
    140         // args gets all the points, even the implicit first point.
    141         switch (verb) {
    142             case SkPath::kMove_Verb:
    143                 MoveTo(args[0].fX, args[0].fY, &currentSegment);
    144                 lastMovePt = args[0];
    145                 fillState = kEmpty_SkipFillState;
    146                 break;
    147             case SkPath::kLine_Verb:
    148                 AppendLine(args[1].fX, args[1].fY, &currentSegment);
    149                 if (fillState == kEmpty_SkipFillState) {
    150                    if (args[0] != lastMovePt) {
    151                        fillState = kSingleLine_SkipFillState;
    152                    }
    153                 } else if (fillState == kSingleLine_SkipFillState) {
    154                     fillState = kNonSingleLine_SkipFillState;
    155                 }
    156                 break;
    157             case SkPath::kQuad_Verb:
    158                 append_quad(args, &currentSegment);
    159                 fillState = kNonSingleLine_SkipFillState;
    160                 break;
    161             case SkPath::kConic_Verb: {
    162                 const SkScalar tol = SK_Scalar1 / 4;
    163                 SkAutoConicToQuads converter;
    164                 const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tol);
    165                 for (int i = 0; i < converter.countQuads(); ++i) {
    166                     append_quad(&quads[i * 2], &currentSegment);
    167                 }
    168             } break;
    169             case SkPath::kCubic_Verb:
    170                 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
    171                             args[3].fX, args[3].fY, &currentSegment);
    172                 fillState = kNonSingleLine_SkipFillState;
    173                 break;
    174             case SkPath::kClose_Verb:
    175                 if (fillState != kSingleLine_SkipFillState) {
    176                     ClosePath(&currentSegment);
    177                     currentSegment.writeToStream(content);
    178                 }
    179                 currentSegment.reset();
    180                 break;
    181             default:
    182                 SkASSERT(false);
    183                 break;
    184         }
    185     }
    186     if (currentSegment.bytesWritten() > 0) {
    187         currentSegment.writeToStream(content);
    188     }
    189 }
    190 
    191 // static
    192 void SkPDFUtils::ClosePath(SkWStream* content) {
    193     content->writeText("h\n");
    194 }
    195 
    196 // static
    197 void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
    198                            SkWStream* content) {
    199     if (style == SkPaint::kFill_Style) {
    200         content->writeText("f");
    201     } else if (style == SkPaint::kStrokeAndFill_Style) {
    202         content->writeText("B");
    203     } else if (style == SkPaint::kStroke_Style) {
    204         content->writeText("S");
    205     }
    206 
    207     if (style != SkPaint::kStroke_Style) {
    208         NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
    209         NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
    210         if (fill == SkPath::kEvenOdd_FillType) {
    211             content->writeText("*");
    212         }
    213     }
    214     content->writeText("\n");
    215 }
    216 
    217 // static
    218 void SkPDFUtils::StrokePath(SkWStream* content) {
    219     SkPDFUtils::PaintPath(
    220         SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
    221 }
    222 
    223 // static
    224 void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
    225     content->writeText("/");
    226     content->writeText(SkPDFResourceDict::getResourceName(
    227             SkPDFResourceDict::kXObject_ResourceType,
    228             objectIndex).c_str());
    229     content->writeText(" Do\n");
    230 }
    231 
    232 // static
    233 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
    234     content->writeText("/");
    235     content->writeText(SkPDFResourceDict::getResourceName(
    236             SkPDFResourceDict::kExtGState_ResourceType,
    237             objectIndex).c_str());
    238     content->writeText(" gs\n");
    239 }
    240 
    241 // static
    242 void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
    243     // Select Pattern color space (CS, cs) and set pattern object as current
    244     // color (SCN, scn)
    245     SkString resourceName = SkPDFResourceDict::getResourceName(
    246             SkPDFResourceDict::kPattern_ResourceType,
    247             objectIndex);
    248     content->writeText("/Pattern CS/Pattern cs/");
    249     content->writeText(resourceName.c_str());
    250     content->writeText(" SCN/");
    251     content->writeText(resourceName.c_str());
    252     content->writeText(" scn\n");
    253 }
    254 
    255 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) {
    256     // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
    257     // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
    258     // When using floats that are outside the whole value range, we can use
    259     // integers instead.
    260 
    261 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
    262     if (value > 32767 || value < -32767) {
    263         stream->writeDecAsText(SkScalarRoundToInt(value));
    264         return;
    265     }
    266 
    267     char buffer[SkStrAppendScalar_MaxSize];
    268     char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
    269     stream->write(buffer, end - buffer);
    270     return;
    271 #endif  // !SK_ALLOW_LARGE_PDF_SCALARS
    272 
    273 #if defined(SK_ALLOW_LARGE_PDF_SCALARS)
    274     // Floats have 24bits of significance, so anything outside that range is
    275     // no more precise than an int. (Plus PDF doesn't support scientific
    276     // notation, so this clamps to SK_Max/MinS32).
    277     if (value > (1 << 24) || value < -(1 << 24)) {
    278         stream->writeDecAsText(value);
    279         return;
    280     }
    281     // Continue to enforce the PDF limits for small floats.
    282     if (value < 1.0f/65536 && value > -1.0f/65536) {
    283         stream->writeDecAsText(0);
    284         return;
    285     }
    286     // SkStrAppendFloat might still use scientific notation, so use snprintf
    287     // directly..
    288     static const int kFloat_MaxSize = 19;
    289     char buffer[kFloat_MaxSize];
    290     int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
    291     // %f always prints trailing 0s, so strip them.
    292     for (; buffer[len - 1] == '0' && len > 0; len--) {
    293         buffer[len - 1] = '\0';
    294     }
    295     if (buffer[len - 1] == '.') {
    296         buffer[len - 1] = '\0';
    297     }
    298     stream->writeText(buffer);
    299     return;
    300 #endif  // SK_ALLOW_LARGE_PDF_SCALARS
    301 }
    302 
    303 SkString SkPDFUtils::FormatString(const char* cin, size_t len) {
    304     SkDEBUGCODE(static const size_t kMaxLen = 65535;)
    305     SkASSERT(len <= kMaxLen);
    306 
    307     // 7-bit clean is a heuristic to decide what string format to use;
    308     // a 7-bit clean string should require little escaping.
    309     bool sevenBitClean = true;
    310     size_t characterCount = 2 + len;
    311     for (size_t i = 0; i < len; i++) {
    312         if (cin[i] > '~' || cin[i] < ' ') {
    313             sevenBitClean = false;
    314             break;
    315         }
    316         if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
    317             ++characterCount;
    318         }
    319     }
    320     SkString result;
    321     if (sevenBitClean) {
    322         result.resize(characterCount);
    323         char* str = result.writable_str();
    324         *str++ = '(';
    325         for (size_t i = 0; i < len; i++) {
    326             if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
    327                 *str++ = '\\';
    328             }
    329             *str++ = cin[i];
    330         }
    331         *str++ = ')';
    332     } else {
    333         result.resize(2 * len + 2);
    334         char* str = result.writable_str();
    335         *str++ = '<';
    336         for (size_t i = 0; i < len; i++) {
    337             uint8_t c = static_cast<uint8_t>(cin[i]);
    338             static const char gHex[] = "0123456789ABCDEF";
    339             *str++ = gHex[(c >> 4) & 0xF];
    340             *str++ = gHex[(c     ) & 0xF];
    341         }
    342         *str++ = '>';
    343     }
    344     return result;
    345 }
    346