Line data Source code
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 0 : static SkPDFDevice* Create(SkISize pageSize,
63 : SkScalar rasterDpi,
64 : SkPDFDocument* doc) {
65 0 : return new SkPDFDevice(pageSize, rasterDpi, doc, true);
66 : }
67 :
68 : /** Create a PDF drawing context without fipping the y-axis. */
69 0 : static SkPDFDevice* CreateUnflipped(SkISize pageSize,
70 : SkScalar rasterDpi,
71 : SkPDFDocument* doc) {
72 0 : 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 0 : 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 0 : struct RectWithData {
181 : SkRect rect;
182 : sk_sp<SkData> data;
183 0 : RectWithData(const SkRect& rect, SkData* data)
184 0 : : rect(rect), data(SkRef(data)) {}
185 0 : RectWithData(RectWithData&&) = default;
186 : RectWithData& operator=(RectWithData&& other) = default;
187 : };
188 :
189 0 : struct NamedDestination {
190 : sk_sp<SkData> nameData;
191 : SkPoint point;
192 0 : NamedDestination(SkData* nameData, const SkPoint& point)
193 0 : : nameData(SkRef(nameData)), point(point) {}
194 0 : 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 0 : 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
|