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