• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
Create(SkISize pageSize,SkScalar rasterDpi,SkPDFDocument * doc)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. */
CreateUnflipped(SkISize pageSize,SkScalar rasterDpi,SkPDFDocument * doc)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;
RectWithDataRectWithData183         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;
NamedDestinationNamedDestination192         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