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 #ifndef SkPDFDevice_DEFINED
      9 #define SkPDFDevice_DEFINED
     10 
     11 #include "SkBitmap.h"
     12 #include "SkCanvas.h"
     13 #include "SkClipStack.h"
     14 #include "SkClipStackDevice.h"
     15 #include "SkData.h"
     16 #include "SkPaint.h"
     17 #include "SkRect.h"
     18 #include "SkRefCnt.h"
     19 #include "SkSinglyLinkedList.h"
     20 #include "SkStream.h"
     21 #include "SkTDArray.h"
     22 #include "SkTextBlob.h"
     23 
     24 class SkImageSubset;
     25 class SkPath;
     26 class SkPDFArray;
     27 class SkPDFCanon;
     28 class SkPDFDevice;
     29 class SkPDFDocument;
     30 class SkPDFDict;
     31 class SkPDFFont;
     32 class SkPDFObject;
     33 class SkPDFStream;
     34 class SkRRect;
     35 
     36 /** \class SkPDFDevice
     37 
     38     The drawing context for the PDF backend.
     39 */
     40 class SkPDFDevice final : public SkClipStackDevice {
     41 public:
     42     /** Create a PDF drawing context.  SkPDFDevice applies a
     43      *  scale-and-translate transform to move the origin from the
     44      *  bottom left (PDF default) to the top left (Skia default).
     45      *  @param pageSize Page size in point units.
     46      *         1 point == 127/360 mm == 1/72 inch
     47      *  @param rasterDpi the DPI at which features without native PDF
     48      *         support will be rasterized (e.g. draw image with
     49      *         perspective, draw text with perspective, ...).  A
     50      *         larger DPI would create a PDF that reflects the
     51      *         original intent with better fidelity, but it can make
     52      *         for larger PDF files too, which would use more memory
     53      *         while rendering, and it would be slower to be processed
     54      *         or sent online or to printer.  A good choice is
     55      *         SK_ScalarDefaultRasterDPI(72.0f).
     56      *  @param SkPDFDocument.  A non-null pointer back to the
     57      *         document.  The document is repsonsible for
     58      *         de-duplicating across pages (via the SkPDFCanon) and
     59      *         for early serializing of large immutable objects, such
     60      *         as images (via SkPDFDocument::serialize()).
     61      */
     62     static SkPDFDevice* Create(SkISize pageSize,
     63                                SkScalar rasterDpi,
     64                                SkPDFDocument* doc) {
     65         return new SkPDFDevice(pageSize, rasterDpi, doc, true);
     66     }
     67 
     68     /** Create a PDF drawing context without fipping the y-axis. */
     69     static SkPDFDevice* CreateUnflipped(SkISize pageSize,
     70                                         SkScalar rasterDpi,
     71                                         SkPDFDocument* doc) {
     72         return new SkPDFDevice(pageSize, rasterDpi, doc, false);
     73     }
     74 
     75     ~SkPDFDevice() override;
     76 
     77     /** These are called inside the per-device-layer loop for each draw call.
     78      When these are called, we have already applied any saveLayer operations,
     79      and are handling any looping from the paint, and any effects from the
     80      DrawFilter.
     81      */
     82     void drawPaint(const SkPaint& paint) override;
     83     void drawPoints(SkCanvas::PointMode mode,
     84                     size_t count, const SkPoint[],
     85                     const SkPaint& paint) override;
     86     void drawRect(const SkRect& r, const SkPaint& paint) override;
     87     void drawOval(const SkRect& oval, const SkPaint& paint) override;
     88     void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
     89     void drawPath(const SkPath& origpath,
     90                   const SkPaint& paint, const SkMatrix* prePathMatrix,
     91                   bool pathIsMutable) override;
     92     void drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
     93                         const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override;
     94     void drawBitmap(const SkBitmap& bitmap,
     95                     const SkMatrix& matrix, const SkPaint&) override;
     96     void drawSprite(const SkBitmap& bitmap, int x, int y,
     97                     const SkPaint& paint) override;
     98     void drawImage(const SkImage*,
     99                    SkScalar x,
    100                    SkScalar y,
    101                    const SkPaint&) override;
    102     void drawImageRect(const SkImage*,
    103                        const SkRect* src,
    104                        const SkRect& dst,
    105                        const SkPaint&,
    106                        SkCanvas::SrcRectConstraint) override;
    107     void drawText(const void* text, size_t len,
    108                   SkScalar x, SkScalar y, const SkPaint&) override;
    109     void drawPosText(const void* text, size_t len,
    110                      const SkScalar pos[], int scalarsPerPos,
    111                      const SkPoint& offset, const SkPaint&) override;
    112     void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
    113                       const SkPaint &, SkDrawFilter*) override;
    114     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
    115     void drawDevice(SkBaseDevice*, int x, int y,
    116                     const SkPaint&) override;
    117 
    118     // PDF specific methods.
    119 
    120     /** Create the resource dictionary for this device. */
    121     sk_sp<SkPDFDict> makeResourceDict() const;
    122 
    123     /** Add our annotations (link to urls and destinations) to the supplied
    124      *  array.
    125      *  @param array Array to add annotations to.
    126      */
    127     void appendAnnotations(SkPDFArray* array) const;
    128 
    129     /** Add our named destinations to the supplied dictionary.
    130      *  @param dict  Dictionary to add destinations to.
    131      *  @param page  The PDF object representing the page for this device.
    132      */
    133     void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
    134 
    135     /** Returns a copy of the media box for this device. */
    136     sk_sp<SkPDFArray> copyMediaBox() const;
    137 
    138     /** Returns a SkStream with the page contents.
    139      */
    140     std::unique_ptr<SkStreamAsset> content() const;
    141 
    142     SkPDFCanon* getCanon() const;
    143 
    144     // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
    145     // later being our representation of an object in the PDF file.
    146     struct GraphicStateEntry {
    147         GraphicStateEntry();
    148 
    149         // Compare the fields we care about when setting up a new content entry.
    150         bool compareInitialState(const GraphicStateEntry& b);
    151 
    152         SkMatrix fMatrix;
    153         // We can't do set operations on Paths, though PDF natively supports
    154         // intersect.  If the clip stack does anything other than intersect,
    155         // we have to fall back to the region.  Treat fClipStack as authoritative.
    156         // See https://bugs.skia.org/221
    157         SkClipStack fClipStack;
    158 
    159         // When emitting the content entry, we will ensure the graphic state
    160         // is set to these values first.
    161         SkColor fColor;
    162         SkScalar fTextScaleX;  // Zero means we don't care what the value is.
    163         SkPaint::Style fTextFill;  // Only if TextScaleX is non-zero.
    164         int fShaderIndex;
    165         int fGraphicStateIndex;
    166     };
    167 
    168 protected:
    169     sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
    170 
    171     void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
    172 
    173     void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override;
    174     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
    175     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
    176     sk_sp<SkSpecialImage> snapSpecial() override;
    177     SkImageFilterCache* getImageFilterCache() override;
    178 
    179 private:
    180     struct RectWithData {
    181         SkRect rect;
    182         sk_sp<SkData> data;
    183         RectWithData(const SkRect& rect, SkData* data)
    184             : rect(rect), data(SkRef(data)) {}
    185         RectWithData(RectWithData&&) = default;
    186         RectWithData& operator=(RectWithData&& other) = default;
    187     };
    188 
    189     struct NamedDestination {
    190         sk_sp<SkData> nameData;
    191         SkPoint point;
    192         NamedDestination(SkData* nameData, const SkPoint& point)
    193             : nameData(SkRef(nameData)), point(point) {}
    194         NamedDestination(NamedDestination&&) = default;
    195         NamedDestination& operator=(NamedDestination&&) = default;
    196     };
    197 
    198     // TODO(vandebo): push most of SkPDFDevice's state into a core object in
    199     // order to get the right access levels without using friend.
    200     friend class ScopedContentEntry;
    201 
    202     SkISize fPageSize;
    203     SkMatrix fInitialTransform;
    204     SkClipStack fExistingClipStack;
    205 
    206     SkTArray<RectWithData> fLinkToURLs;
    207     SkTArray<RectWithData> fLinkToDestinations;
    208     SkTArray<NamedDestination> fNamedDestinations;
    209 
    210     SkTDArray<SkPDFObject*> fGraphicStateResources;
    211     SkTDArray<SkPDFObject*> fXObjectResources;
    212     SkTDArray<SkPDFFont*> fFontResources;
    213     SkTDArray<SkPDFObject*> fShaderResources;
    214 
    215     struct ContentEntry {
    216         GraphicStateEntry fState;
    217         SkDynamicMemoryWStream fContent;
    218     };
    219     SkSinglyLinkedList<ContentEntry> fContentEntries;
    220 
    221     SkScalar fRasterDpi;
    222 
    223     SkPDFDocument* fDocument;
    224     ////////////////////////////////////////////////////////////////////////////
    225 
    226     SkPDFDevice(SkISize pageSize,
    227                 SkScalar rasterDpi,
    228                 SkPDFDocument* doc,
    229                 bool flip);
    230 
    231     SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
    232 
    233     void init();
    234     void cleanUp();
    235     sk_sp<SkPDFObject> makeFormXObjectFromDevice();
    236 
    237     void drawFormXObjectWithMask(int xObjectIndex,
    238                                  sk_sp<SkPDFObject> mask,
    239                                  const SkClipStack& clipStack,
    240                                  SkBlendMode,
    241                                  bool invertClip);
    242 
    243     // If the paint or clip is such that we shouldn't draw anything, this
    244     // returns nullptr and does not create a content entry.
    245     // setUpContentEntry and finishContentEntry can be used directly, but
    246     // the preferred method is to use the ScopedContentEntry helper class.
    247     ContentEntry* setUpContentEntry(const SkClipStack& clipStack,
    248                                     const SkMatrix& matrix,
    249                                     const SkPaint& paint,
    250                                     bool hasText,
    251                                     sk_sp<SkPDFObject>* dst);
    252     void finishContentEntry(SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
    253     bool isContentEmpty();
    254 
    255     void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
    256                                             const SkClipStack& clipStack,
    257                                             const SkPaint& paint,
    258                                             bool hasText,
    259                                             GraphicStateEntry* entry);
    260     int addGraphicStateResource(SkPDFObject* gs);
    261     int addXObjectResource(SkPDFObject* xObject);
    262 
    263     int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
    264 
    265 
    266     void internalDrawText( const void*, size_t, const SkScalar pos[],
    267                           SkTextBlob::GlyphPositioning, SkPoint, const SkPaint&,
    268                           const uint32_t*, uint32_t, const char*);
    269 
    270     void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
    271 
    272     void internalDrawImage(const SkMatrix& origMatrix,
    273                            const SkClipStack& clipStack,
    274                            SkImageSubset imageSubset,
    275                            const SkPaint& paint);
    276 
    277     void internalDrawPath(const SkClipStack&,
    278                           const SkMatrix&,
    279                           const SkPath&,
    280                           const SkPaint&,
    281                           const SkMatrix* prePathMatrix,
    282                           bool pathIsMutable);
    283 
    284     bool handleInversePath(const SkPath& origPath,
    285                            const SkPaint& paint, bool pathIsMutable,
    286                            const SkMatrix* prePathMatrix = nullptr);
    287 
    288     typedef SkClipStackDevice INHERITED;
    289 
    290     // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
    291     // an SkPDFDevice
    292     //friend class SkDocument_PDF;
    293     //friend class SkPDFImageShader;
    294 };
    295 
    296 #endif
    297