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 "include/core/SkBitmap.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkStream.h"
18 #include "include/private/SkTHash.h"
19 #include "src/core/SkClipStack.h"
20 #include "src/core/SkClipStackDevice.h"
21 #include "src/core/SkTextBlobPriv.h"
22 #include "src/pdf/SkKeyedImage.h"
23 #include "src/pdf/SkPDFGraphicStackState.h"
24 #include "src/pdf/SkPDFTypes.h"
25 
26 #include <vector>
27 
28 class SkGlyphRunList;
29 class SkKeyedImage;
30 class SkPDFArray;
31 class SkPDFDevice;
32 class SkPDFDict;
33 class SkPDFDocument;
34 class SkPDFFont;
35 class SkPDFObject;
36 class SkPath;
37 class SkRRect;
38 struct SkPDFIndirectReference;
39 
40 /**
41  *  \class SkPDFDevice
42  *
43  *  An SkPDFDevice is the drawing context for a page or layer of PDF
44  *  content.
45  */
46 class SkPDFDevice final : public SkClipStackDevice {
47 public:
48     /**
49      *  @param pageSize Page size in point units.
50      *         1 point == 127/360 mm == 1/72 inch
51      *  @param document  A non-null pointer back to the
52      *         PDFDocument object.  The document is responsible for
53      *         de-duplicating across pages (via the SkPDFDocument) and
54      *         for early serializing of large immutable objects, such
55      *         as images (via SkPDFDocument::serialize()).
56      *  @param initialTransform Transform to be applied to the entire page.
57      */
58     SkPDFDevice(SkISize pageSize, SkPDFDocument* document,
59                 const SkMatrix& initialTransform = SkMatrix::I());
60 
makeCongruentDevice()61     sk_sp<SkPDFDevice> makeCongruentDevice() {
62         return sk_make_sp<SkPDFDevice>(this->size(), fDocument);
63     }
64 
65     ~SkPDFDevice() override;
66 
67     /**
68      *  These are called inside the per-device-layer loop for each draw call.
69      *  When these are called, we have already applied any saveLayer
70      *  operations, and are handling any looping from the paint.
71      */
72     void drawPaint(const SkPaint& paint) override;
73     void drawPoints(SkCanvas::PointMode mode,
74                     size_t count, const SkPoint[],
75                     const SkPaint& paint) override;
76     void drawRect(const SkRect& r, const SkPaint& paint) override;
77     void drawOval(const SkRect& oval, const SkPaint& paint) override;
78     void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
79     void drawPath(const SkPath& origpath, const SkPaint& paint, bool pathIsMutable) override;
80 
81     void drawImageRect(const SkImage*,
82                        const SkRect* src,
83                        const SkRect& dst,
84                        const SkPaint&,
85                        SkCanvas::SrcRectConstraint) override;
86     void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
87     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
88     void drawDevice(SkBaseDevice*, int x, int y,
89                     const SkPaint&) override;
90 
91     // PDF specific methods.
92     void drawSprite(const SkBitmap& bitmap, int x, int y,
93                     const SkPaint& paint);
94 
95     /** Create the resource dictionary for this device. Destructive. */
96     std::unique_ptr<SkPDFDict> makeResourceDict();
97 
98     /** Returns a SkStream with the page contents.
99      */
100     std::unique_ptr<SkStreamAsset> content();
101 
size()102     SkISize size() const { return this->imageInfo().dimensions(); }
bounds()103     SkIRect bounds() const { return this->imageInfo().bounds(); }
104 
105     void DrawGlyphRunAsPath(SkPDFDevice* dev, const SkGlyphRun& glyphRun, SkPoint offset);
106 
initialTransform()107     const SkMatrix& initialTransform() const { return fInitialTransform; }
108 
109 protected:
110     sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
111 
112     void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
113 
114     void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
115                      SkImage*, const SkMatrix&) override;
116     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
117     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
118     SkImageFilterCache* getImageFilterCache() override;
119 
120 private:
121     // TODO(vandebo): push most of SkPDFDevice's state into a core object in
122     // order to get the right access levels without using friend.
123     friend class ScopedContentEntry;
124 
125     SkMatrix fInitialTransform;
126 
127     SkTHashSet<SkPDFIndirectReference> fGraphicStateResources;
128     SkTHashSet<SkPDFIndirectReference> fXObjectResources;
129     SkTHashSet<SkPDFIndirectReference> fShaderResources;
130     SkTHashSet<SkPDFIndirectReference> fFontResources;
131     int fNodeId;
132 
133     SkDynamicMemoryWStream fContent;
134     SkDynamicMemoryWStream fContentBuffer;
135     bool fNeedsExtraSave = false;
136     SkPDFGraphicStackState fActiveStackState;
137     SkPDFDocument* fDocument;
138 
139     ////////////////////////////////////////////////////////////////////////////
140 
141     SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
142 
143     // Set alpha to true if making a transparency group form x-objects.
144     SkPDFIndirectReference makeFormXObjectFromDevice(bool alpha = false);
145     SkPDFIndirectReference makeFormXObjectFromDevice(SkIRect bbox, bool alpha = false);
146 
147     void drawFormXObjectWithMask(SkPDFIndirectReference xObject,
148                                  SkPDFIndirectReference sMask,
149                                  SkBlendMode,
150                                  bool invertClip);
151 
152     // If the paint or clip is such that we shouldn't draw anything, this
153     // returns nullptr and does not create a content entry.
154     // setUpContentEntry and finishContentEntry can be used directly, but
155     // the preferred method is to use the ScopedContentEntry helper class.
156     SkDynamicMemoryWStream* setUpContentEntry(const SkClipStack* clipStack,
157                                               const SkMatrix& matrix,
158                                               const SkPaint& paint,
159                                               SkScalar,
160                                               SkPDFIndirectReference* dst);
161     void finishContentEntry(const SkClipStack*, SkBlendMode, SkPDFIndirectReference, SkPath*);
162     bool isContentEmpty();
163 
164     void internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
165     void drawGlyphRunAsPath(const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
166 
167     void internalDrawImageRect(SkKeyedImage,
168                                const SkRect* src,
169                                const SkRect& dst,
170                                const SkPaint&,
171                                const SkMatrix& canvasTransformationMatrix);
172 
173     void internalDrawPath(const SkClipStack&,
174                           const SkMatrix&,
175                           const SkPath&,
176                           const SkPaint&,
177                           bool pathIsMutable);
178 
179     void internalDrawPathWithFilter(const SkClipStack& clipStack,
180                                     const SkMatrix& ctm,
181                                     const SkPath& origPath,
182                                     const SkPaint& paint);
183 
184     bool handleInversePath(const SkPath& origPath, const SkPaint& paint, bool pathIsMutable);
185 
186     void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
187     void setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream*);
188     void drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream*);
189 
hasEmptyClip()190     bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }
191 
192     void reset();
193 
194     typedef SkClipStackDevice INHERITED;
195 };
196 
197 #endif
198