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 "SkKeyedImage.h"
17 #include "SkPDFTypes.h"
18 #include "SkPaint.h"
19 #include "SkRect.h"
20 #include "SkRefCnt.h"
21 #include "SkStream.h"
22 #include "SkTHash.h"
23 #include "SkTextBlobPriv.h"
24 
25 #include <vector>
26 
27 class SkGlyphRunList;
28 class SkKeyedImage;
29 class SkPDFArray;
30 class SkPDFDevice;
31 class SkPDFDict;
32 class SkPDFDocument;
33 class SkPDFFont;
34 class SkPDFObject;
35 class SkPath;
36 class SkRRect;
37 struct SkPDFIndirectReference;
38 
39 /**
40  *  \class SkPDFDevice
41  *
42  *  An SkPDFDevice is the drawing context for a page or layer of PDF
43  *  content.
44  */
45 class SkPDFDevice final : public SkClipStackDevice {
46 public:
47     /**
48      *  @param pageSize Page size in point units.
49      *         1 point == 127/360 mm == 1/72 inch
50      *  @param document  A non-null pointer back to the
51      *         PDFDocument object.  The document is responsible for
52      *         de-duplicating across pages (via the SkPDFDocument) and
53      *         for early serializing of large immutable objects, such
54      *         as images (via SkPDFDocument::serialize()).
55      *  @param initialTransform Transform to be applied to the entire page.
56      */
57     SkPDFDevice(SkISize pageSize, SkPDFDocument* document,
58                 const SkMatrix& initialTransform = SkMatrix::I());
59 
60     sk_sp<SkPDFDevice> makeCongruentDevice() {
61         return sk_make_sp<SkPDFDevice>(this->size(), fDocument);
62     }
63 
64     ~SkPDFDevice() override;
65 
66     /**
67      *  These are called inside the per-device-layer loop for each draw call.
68      *  When these are called, we have already applied any saveLayer
69      *  operations, and are handling any looping from the paint.
70      */
71     void drawPaint(const SkPaint& paint) override;
72     void drawPoints(SkCanvas::PointMode mode,
73                     size_t count, const SkPoint[],
74                     const SkPaint& paint) override;
75     void drawRect(const SkRect& r, const SkPaint& paint) override;
76     void drawOval(const SkRect& oval, const SkPaint& paint) override;
77     void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
78     void drawPath(const SkPath& origpath, const SkPaint& paint, bool pathIsMutable) override;
79     void drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
80                         const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override;
81     void drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint&) override;
82     void drawSprite(const SkBitmap& bitmap, int x, int y,
83                     const SkPaint& paint) override;
84     void drawImage(const SkImage*,
85                    SkScalar x,
86                    SkScalar y,
87                    const SkPaint&) override;
88     void drawImageRect(const SkImage*,
89                        const SkRect* src,
90                        const SkRect& dst,
91                        const SkPaint&,
92                        SkCanvas::SrcRectConstraint) override;
93     void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
94     void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
95                       const SkPaint&) override;
96     void drawDevice(SkBaseDevice*, int x, int y,
97                     const SkPaint&) override;
98 
99     // PDF specific methods.
100 
101     /** Create the resource dictionary for this device. Destructive. */
102     std::unique_ptr<SkPDFDict> makeResourceDict();
103 
104     /** return annotations (link to urls and destinations) or nulltpr */
105     std::unique_ptr<SkPDFArray> getAnnotations();
106 
107     /** Add our named destinations to the supplied dictionary.
108      *  @param dict  Dictionary to add destinations to.
109      *  @param page  The PDF object representing the page for this device.
110      */
111     void appendDestinations(SkPDFDict* dict, SkPDFIndirectReference page) const;
112 
113     /** Returns a SkStream with the page contents.
114      */
115     std::unique_ptr<SkStreamAsset> content();
116 
117     SkISize size() const { return this->imageInfo().dimensions(); }
118     SkIRect bounds() const { return this->imageInfo().bounds(); }
119 
120     // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
121     // later being our representation of an object in the PDF file.
122     struct GraphicStateEntry {
123         SkMatrix fMatrix = SkMatrix::I();
124         uint32_t fClipStackGenID = SkClipStack::kWideOpenGenID;
125         SkColor4f fColor = {0, 0, 0, 1};
126         SkScalar fTextScaleX = 1;  // Zero means we don't care what the value is.
127         SkPaint::Style fTextFill = SkPaint::kFill_Style;  // Only if TextScaleX is non-zero.
128         int fShaderIndex = -1;
129         int fGraphicStateIndex = -1;
130     };
131 
132     void DrawGlyphRunAsPath(SkPDFDevice* dev, const SkGlyphRun& glyphRun, SkPoint offset);
133 
134 protected:
135     sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
136 
137     void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
138 
139     void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
140                      SkImage*, const SkMatrix&) override;
141     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
142     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
143     sk_sp<SkSpecialImage> snapSpecial() override;
144     SkImageFilterCache* getImageFilterCache() override;
145 
146 private:
147     struct RectWithData {
148         SkRect rect;
149         sk_sp<SkData> data;
150     };
151 
152     struct NamedDestination {
153         sk_sp<SkData> nameData;
154         SkPoint point;
155     };
156 
157     // TODO(vandebo): push most of SkPDFDevice's state into a core object in
158     // order to get the right access levels without using friend.
159     friend class ScopedContentEntry;
160 
161     SkMatrix fInitialTransform;
162 
163     std::vector<RectWithData> fLinkToURLs;
164     std::vector<RectWithData> fLinkToDestinations;
165     std::vector<NamedDestination> fNamedDestinations;
166 
167     SkTHashSet<SkPDFIndirectReference> fGraphicStateResources;
168     SkTHashSet<SkPDFIndirectReference> fXObjectResources;
169     SkTHashSet<SkPDFIndirectReference> fShaderResources;
170     SkTHashSet<SkPDFIndirectReference> fFontResources;
171     int fNodeId;
172 
173     SkDynamicMemoryWStream fContent;
174     SkDynamicMemoryWStream fContentBuffer;
175     bool fNeedsExtraSave = false;
176     struct GraphicStackState {
177         GraphicStackState(SkDynamicMemoryWStream* s = nullptr);
178         void updateClip(const SkClipStack* clipStack, const SkIRect& bounds);
179         void updateMatrix(const SkMatrix& matrix);
180         void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state);
181         void push();
182         void pop();
183         void drainStack();
184         SkPDFDevice::GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
185         // Must use stack for matrix, and for clip, plus one for no matrix or clip.
186         static constexpr int kMaxStackDepth = 2;
187         SkPDFDevice::GraphicStateEntry fEntries[kMaxStackDepth + 1];
188         int fStackDepth = 0;
189         SkDynamicMemoryWStream* fContentStream;
190     };
191     GraphicStackState fActiveStackState;
192     SkPDFDocument* fDocument;
193 
194     ////////////////////////////////////////////////////////////////////////////
195 
196     SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
197 
198     // Set alpha to true if making a transparency group form x-objects.
199     SkPDFIndirectReference makeFormXObjectFromDevice(bool alpha = false);
200 
201     void drawFormXObjectWithMask(SkPDFIndirectReference xObject,
202                                  SkPDFIndirectReference sMask,
203                                  SkBlendMode,
204                                  bool invertClip);
205 
206     // If the paint or clip is such that we shouldn't draw anything, this
207     // returns nullptr and does not create a content entry.
208     // setUpContentEntry and finishContentEntry can be used directly, but
209     // the preferred method is to use the ScopedContentEntry helper class.
210     SkDynamicMemoryWStream* setUpContentEntry(const SkClipStack* clipStack,
211                                               const SkMatrix& matrix,
212                                               const SkPaint& paint,
213                                               SkScalar,
214                                               SkPDFIndirectReference* dst);
215     void finishContentEntry(const SkClipStack*, SkBlendMode, SkPDFIndirectReference, SkPath*);
216     bool isContentEmpty();
217 
218     void internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
219     void drawGlyphRunAsPath(const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
220 
221     void internalDrawImageRect(SkKeyedImage,
222                                const SkRect* src,
223                                const SkRect& dst,
224                                const SkPaint&,
225                                const SkMatrix& canvasTransformationMatrix);
226 
227     void internalDrawPath(const SkClipStack&,
228                           const SkMatrix&,
229                           const SkPath&,
230                           const SkPaint&,
231                           bool pathIsMutable);
232 
233     void internalDrawPathWithFilter(const SkClipStack& clipStack,
234                                     const SkMatrix& ctm,
235                                     const SkPath& origPath,
236                                     const SkPaint& paint);
237 
238     bool handleInversePath(const SkPath& origPath, const SkPaint& paint, bool pathIsMutable);
239 
240     void addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice, SkDynamicMemoryWStream*);
241     void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
242     void setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream*);
243     void drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream*);
244 
245     bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }
246 
247     void reset();
248 
249     typedef SkClipStackDevice INHERITED;
250 };
251 
252 #endif
253