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 #include <cmath>
     20 
     21 //static
     22 SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) {
     23     SkPDFArray* result = new SkPDFArray();
     24     result->reserve(4);
     25     result->appendScalar(rect.fLeft);
     26     result->appendScalar(rect.fTop);
     27     result->appendScalar(rect.fRight);
     28     result->appendScalar(rect.fBottom);
     29     return result;
     30 }
     31 
     32 // static
     33 SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
     34     SkScalar values[6];
     35     if (!matrix.asAffine(values)) {
     36         SkMatrix::SetAffineIdentity(values);
     37     }
     38 
     39     SkPDFArray* result = new SkPDFArray;
     40     result->reserve(6);
     41     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
     42         result->appendScalar(values[i]);
     43     }
     44     return result;
     45 }
     46 
     47 // static
     48 void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
     49     SkScalar values[6];
     50     if (!matrix.asAffine(values)) {
     51         SkMatrix::SetAffineIdentity(values);
     52     }
     53     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
     54         SkPDFUtils::AppendScalar(values[i], content);
     55         content->writeText(" ");
     56     }
     57     content->writeText("cm\n");
     58 }
     59 
     60 // static
     61 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
     62     SkPDFUtils::AppendScalar(x, content);
     63     content->writeText(" ");
     64     SkPDFUtils::AppendScalar(y, content);
     65     content->writeText(" m\n");
     66 }
     67 
     68 // static
     69 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
     70     SkPDFUtils::AppendScalar(x, content);
     71     content->writeText(" ");
     72     SkPDFUtils::AppendScalar(y, content);
     73     content->writeText(" l\n");
     74 }
     75 
     76 // static
     77 void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
     78                              SkScalar ctl2X, SkScalar ctl2Y,
     79                              SkScalar dstX, SkScalar dstY, SkWStream* content) {
     80     SkString cmd("y\n");
     81     SkPDFUtils::AppendScalar(ctl1X, content);
     82     content->writeText(" ");
     83     SkPDFUtils::AppendScalar(ctl1Y, content);
     84     content->writeText(" ");
     85     if (ctl2X != dstX || ctl2Y != dstY) {
     86         cmd.set("c\n");
     87         SkPDFUtils::AppendScalar(ctl2X, content);
     88         content->writeText(" ");
     89         SkPDFUtils::AppendScalar(ctl2Y, content);
     90         content->writeText(" ");
     91     }
     92     SkPDFUtils::AppendScalar(dstX, content);
     93     content->writeText(" ");
     94     SkPDFUtils::AppendScalar(dstY, content);
     95     content->writeText(" ");
     96     content->writeText(cmd.c_str());
     97 }
     98 
     99 static void append_quad(const SkPoint quad[], SkWStream* content) {
    100     SkPoint cubic[4];
    101     SkConvertQuadToCubic(quad, cubic);
    102     SkPDFUtils::AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
    103                             cubic[3].fX, cubic[3].fY, content);
    104 }
    105 
    106 // static
    107 void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
    108     // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
    109     SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
    110 
    111     SkPDFUtils::AppendScalar(rect.fLeft, content);
    112     content->writeText(" ");
    113     SkPDFUtils::AppendScalar(bottom, content);
    114     content->writeText(" ");
    115     SkPDFUtils::AppendScalar(rect.width(), content);
    116     content->writeText(" ");
    117     SkPDFUtils::AppendScalar(rect.height(), content);
    118     content->writeText(" re\n");
    119 }
    120 
    121 // static
    122 void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
    123                           bool doConsumeDegerates, SkWStream* content) {
    124     // Filling a path with no area results in a drawing in PDF renderers but
    125     // Chrome expects to be able to draw some such entities with no visible
    126     // result, so we detect those cases and discard the drawing for them.
    127     // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
    128     enum SkipFillState {
    129         kEmpty_SkipFillState,
    130         kSingleLine_SkipFillState,
    131         kNonSingleLine_SkipFillState,
    132     };
    133     SkipFillState fillState = kEmpty_SkipFillState;
    134     //if (paintStyle != SkPaint::kFill_Style) {
    135     //    fillState = kNonSingleLine_SkipFillState;
    136     //}
    137     SkPoint lastMovePt = SkPoint::Make(0,0);
    138     SkDynamicMemoryWStream currentSegment;
    139     SkPoint args[4];
    140     SkPath::Iter iter(path, false);
    141     for (SkPath::Verb verb = iter.next(args, doConsumeDegerates);
    142          verb != SkPath::kDone_Verb;
    143          verb = iter.next(args, doConsumeDegerates)) {
    144         // args gets all the points, even the implicit first point.
    145         switch (verb) {
    146             case SkPath::kMove_Verb:
    147                 MoveTo(args[0].fX, args[0].fY, &currentSegment);
    148                 lastMovePt = args[0];
    149                 fillState = kEmpty_SkipFillState;
    150                 break;
    151             case SkPath::kLine_Verb:
    152                 AppendLine(args[1].fX, args[1].fY, &currentSegment);
    153                 if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) {
    154                     fillState = kSingleLine_SkipFillState;
    155                     break;
    156                 }
    157                 fillState = kNonSingleLine_SkipFillState;
    158                 break;
    159             case SkPath::kQuad_Verb:
    160                 append_quad(args, &currentSegment);
    161                 fillState = kNonSingleLine_SkipFillState;
    162                 break;
    163             case SkPath::kConic_Verb: {
    164                 const SkScalar tol = SK_Scalar1 / 4;
    165                 SkAutoConicToQuads converter;
    166                 const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tol);
    167                 for (int i = 0; i < converter.countQuads(); ++i) {
    168                     append_quad(&quads[i * 2], &currentSegment);
    169                 }
    170                 fillState = kNonSingleLine_SkipFillState;
    171             } break;
    172             case SkPath::kCubic_Verb:
    173                 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
    174                             args[3].fX, args[3].fY, &currentSegment);
    175                 fillState = kNonSingleLine_SkipFillState;
    176                 break;
    177             case SkPath::kClose_Verb:
    178 
    179                     ClosePath(&currentSegment);
    180 
    181                 currentSegment.writeToStream(content);
    182                 currentSegment.reset();
    183                 break;
    184             default:
    185                 SkASSERT(false);
    186                 break;
    187         }
    188     }
    189     if (currentSegment.bytesWritten() > 0) {
    190         currentSegment.writeToStream(content);
    191     }
    192 }
    193 
    194 // static
    195 void SkPDFUtils::ClosePath(SkWStream* content) {
    196     content->writeText("h\n");
    197 }
    198 
    199 // static
    200 void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
    201                            SkWStream* content) {
    202     if (style == SkPaint::kFill_Style) {
    203         content->writeText("f");
    204     } else if (style == SkPaint::kStrokeAndFill_Style) {
    205         content->writeText("B");
    206     } else if (style == SkPaint::kStroke_Style) {
    207         content->writeText("S");
    208     }
    209 
    210     if (style != SkPaint::kStroke_Style) {
    211         NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
    212         NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
    213         if (fill == SkPath::kEvenOdd_FillType) {
    214             content->writeText("*");
    215         }
    216     }
    217     content->writeText("\n");
    218 }
    219 
    220 // static
    221 void SkPDFUtils::StrokePath(SkWStream* content) {
    222     SkPDFUtils::PaintPath(
    223         SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
    224 }
    225 
    226 // static
    227 void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
    228     content->writeText("/");
    229     content->writeText(SkPDFResourceDict::getResourceName(
    230             SkPDFResourceDict::kXObject_ResourceType,
    231             objectIndex).c_str());
    232     content->writeText(" Do\n");
    233 }
    234 
    235 // static
    236 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
    237     content->writeText("/");
    238     content->writeText(SkPDFResourceDict::getResourceName(
    239             SkPDFResourceDict::kExtGState_ResourceType,
    240             objectIndex).c_str());
    241     content->writeText(" gs\n");
    242 }
    243 
    244 // static
    245 void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
    246     // Select Pattern color space (CS, cs) and set pattern object as current
    247     // color (SCN, scn)
    248     SkString resourceName = SkPDFResourceDict::getResourceName(
    249             SkPDFResourceDict::kPattern_ResourceType,
    250             objectIndex);
    251     content->writeText("/Pattern CS/Pattern cs/");
    252     content->writeText(resourceName.c_str());
    253     content->writeText(" SCN/");
    254     content->writeText(resourceName.c_str());
    255     content->writeText(" scn\n");
    256 }
    257 
    258 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) {
    259     char result[kMaximumFloatDecimalLength];
    260     size_t len = SkPDFUtils::FloatToDecimal(SkScalarToFloat(value), result);
    261     SkASSERT(len < kMaximumFloatDecimalLength);
    262     stream->write(result, len);
    263 }
    264 
    265 /** Write a string into result, includeing a terminating '\0' (for
    266     unit testing).  Return strlen(result) (for SkWStream::write) The
    267     resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
    268     sscanf(result, "%f", &x) will return the original value iff the
    269     value is finite. This function accepts all possible input values.
    270 
    271     Motivation: "PDF does not support [numbers] in exponential format
    272     (such as 6.02e23)."  Otherwise, this function would rely on a
    273     sprintf-type function from the standard library. */
    274 size_t SkPDFUtils::FloatToDecimal(float value,
    275                                   char result[kMaximumFloatDecimalLength]) {
    276     /* The longest result is -FLT_MIN.
    277        We serialize it as "-.0000000000000000000000000000000000000117549435"
    278        which has 48 characters plus a terminating '\0'. */
    279 
    280     /* section C.1 of the PDF1.4 spec (http://goo.gl/0SCswJ) says that
    281        most PDF rasterizers will use fixed-point scalars that lack the
    282        dynamic range of floats.  Even if this is the case, I want to
    283        serialize these (uncommon) very small and very large scalar
    284        values with enough precision to allow a floating-point
    285        rasterizer to read them in with perfect accuracy.
    286        Experimentally, rasterizers such as pdfium do seem to benefit
    287        from this.  Rasterizers that rely on fixed-point scalars should
    288        gracefully ignore these values that they can not parse. */
    289     char* output = &result[0];
    290     const char* const end = &result[kMaximumFloatDecimalLength - 1];
    291     // subtract one to leave space for '\0'.
    292 
    293     /* This function is written to accept any possible input value,
    294        including non-finite values such as INF and NAN.  In that case,
    295        we ignore value-correctness and and output a syntacticly-valid
    296        number. */
    297     if (value == SK_FloatInfinity) {
    298         value = FLT_MAX;  // nearest finite float.
    299     }
    300     if (value == SK_FloatNegativeInfinity) {
    301         value = -FLT_MAX;  // nearest finite float.
    302     }
    303     if (!std::isfinite(value) || value == 0.0f) {
    304         // NAN is unsupported in PDF.  Always output a valid number.
    305         // Also catch zero here, as a special case.
    306         *output++ = '0';
    307         *output = '\0';
    308         return output - result;
    309     }
    310     // Inspired by:
    311     // http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/
    312 
    313     if (value < 0.0) {
    314         *output++ = '-';
    315         value = -value;
    316     }
    317     SkASSERT(value >= 0.0f);
    318 
    319     // Must use double math to keep precision right.
    320     double intPart;
    321     double fracPart = std::modf(static_cast<double>(value), &intPart);
    322     SkASSERT(intPart + fracPart == static_cast<double>(value));
    323     size_t significantDigits = 0;
    324     const size_t maxSignificantDigits = 9;
    325     // Any fewer significant digits loses precision.  The unit test
    326     // checks round-trip correctness.
    327     SkASSERT(intPart >= 0.0 && fracPart >= 0.0);  // negative handled already.
    328     SkASSERT(intPart > 0.0 || fracPart > 0.0);  // zero already caught.
    329     if (intPart > 0.0) {
    330         // put the intPart digits onto a stack for later reversal.
    331         char reversed[1 + FLT_MAX_10_EXP];  // 39 == 1 + FLT_MAX_10_EXP
    332         // the largest integer part is FLT_MAX; it has 39 decimal digits.
    333         size_t reversedIndex = 0;
    334         do {
    335             SkASSERT(reversedIndex < sizeof(reversed));
    336             int digit = static_cast<int>(std::fmod(intPart, 10.0));
    337             SkASSERT(digit >= 0 && digit <= 9);
    338             reversed[reversedIndex++] = '0' + digit;
    339             intPart = std::floor(intPart / 10.0);
    340         } while (intPart > 0.0);
    341         significantDigits = reversedIndex;
    342         SkASSERT(reversedIndex <= sizeof(reversed));
    343         SkASSERT(output + reversedIndex <= end);
    344         while (reversedIndex-- > 0) {  // pop from stack, append to result
    345             *output++ = reversed[reversedIndex];
    346         }
    347     }
    348     if (fracPart > 0 && significantDigits < maxSignificantDigits) {
    349         *output++ = '.';
    350         SkASSERT(output <= end);
    351         do {
    352             fracPart = std::modf(fracPart * 10.0, &intPart);
    353             int digit = static_cast<int>(intPart);
    354             SkASSERT(digit >= 0 && digit <= 9);
    355             *output++ = '0' + digit;
    356             SkASSERT(output <= end);
    357             if (digit > 0 || significantDigits > 0) {
    358                 // start counting significantDigits after first non-zero digit.
    359                 ++significantDigits;
    360             }
    361         } while (fracPart > 0.0
    362                  && significantDigits < maxSignificantDigits
    363                  && output < end);
    364         // When fracPart == 0, additional digits will be zero.
    365         // When significantDigits == maxSignificantDigits, we can stop.
    366         // when output == end, we have filed the string.
    367         // Note: denormalized numbers will not have the same number of
    368         // significantDigits, but do not need them to round-trip.
    369     }
    370     SkASSERT(output <= end);
    371     *output = '\0';
    372     return output - result;
    373 }
    374 
    375 SkString SkPDFUtils::FormatString(const char* cin, size_t len) {
    376     SkDEBUGCODE(static const size_t kMaxLen = 65535;)
    377     SkASSERT(len <= kMaxLen);
    378 
    379     // 7-bit clean is a heuristic to decide what string format to use;
    380     // a 7-bit clean string should require little escaping.
    381     bool sevenBitClean = true;
    382     size_t characterCount = 2 + len;
    383     for (size_t i = 0; i < len; i++) {
    384         if (cin[i] > '~' || cin[i] < ' ') {
    385             sevenBitClean = false;
    386             break;
    387         }
    388         if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
    389             ++characterCount;
    390         }
    391     }
    392     SkString result;
    393     if (sevenBitClean) {
    394         result.resize(characterCount);
    395         char* str = result.writable_str();
    396         *str++ = '(';
    397         for (size_t i = 0; i < len; i++) {
    398             if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
    399                 *str++ = '\\';
    400             }
    401             *str++ = cin[i];
    402         }
    403         *str++ = ')';
    404     } else {
    405         result.resize(2 * len + 2);
    406         char* str = result.writable_str();
    407         *str++ = '<';
    408         for (size_t i = 0; i < len; i++) {
    409             uint8_t c = static_cast<uint8_t>(cin[i]);
    410             static const char gHex[] = "0123456789ABCDEF";
    411             *str++ = gHex[(c >> 4) & 0xF];
    412             *str++ = gHex[(c     ) & 0xF];
    413         }
    414         *str++ = '>';
    415     }
    416     return result;
    417 }
    418