SkPDFDevice.h revision 562fe4767cc73e08a4e039362bc0336aea66ecfb
1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#ifndef SkPDFDevice_DEFINED
11#define SkPDFDevice_DEFINED
12
13#include "SkDevice.h"
14#include "SkBitmap.h"
15#include "SkCanvas.h"
16#include "SkPaint.h"
17#include "SkPath.h"
18#include "SkPicture.h"
19#include "SkRect.h"
20#include "SkRefCnt.h"
21#include "SkStream.h"
22#include "SkTDArray.h"
23#include "SkTemplates.h"
24
25class SkPDFArray;
26class SkPDFCanon;
27class SkPDFDevice;
28class SkPDFDict;
29class SkPDFFont;
30class SkPDFFormXObject;
31class SkPDFGlyphSetMap;
32class SkPDFGraphicState;
33class SkPDFObject;
34class SkPDFShader;
35class SkPDFStream;
36class SkRRect;
37
38// Private classes.
39struct ContentEntry;
40struct GraphicStateEntry;
41struct NamedDestination;
42
43/** \class SkPDFDevice
44
45    The drawing context for the PDF backend.
46*/
47class SkPDFDevice : public SkBaseDevice {
48public:
49    /** Create a PDF drawing context.  SkPDFDevice applies a
50     *  scale-and-translate transform to move the origin from the
51     *  bottom left (PDF default) to the top left (Skia default).
52     *  @param pageSize Page size in point units.
53     *         1 point == 127/360 mm == 1/72 inch
54     *  @param rasterDpi the DPI at which features without native PDF
55     *         support will be rasterized (e.g. draw image with
56     *         perspective, draw text with perspective, ...).  A
57     *         larger DPI would create a PDF that reflects the
58     *         original intent with better fidelity, but it can make
59     *         for larger PDF files too, which would use more memory
60     *         while rendering, and it would be slower to be processed
61     *         or sent online or to printer.  A good choice is
62     *         SK_ScalarDefaultRasterDPI(72.0f).
63     *  @param SkPDFCanon.  Should be non-null, and shared by all
64     *         devices in a document.
65     */
66    static SkPDFDevice* Create(SkISize pageSize,
67                               SkScalar rasterDpi,
68                               SkPDFCanon* canon) {
69        return SkNEW_ARGS(SkPDFDevice, (pageSize, rasterDpi, canon, true));
70    }
71
72    /** Create a PDF drawing context without fipping the y-axis. */
73    static SkPDFDevice* CreateUnflipped(SkISize pageSize,
74                                        SkScalar rasterDpi,
75                                        SkPDFCanon* canon) {
76        return SkNEW_ARGS(SkPDFDevice, (pageSize, rasterDpi, canon, false));
77    }
78
79    virtual ~SkPDFDevice();
80
81    /** These are called inside the per-device-layer loop for each draw call.
82     When these are called, we have already applied any saveLayer operations,
83     and are handling any looping from the paint, and any effects from the
84     DrawFilter.
85     */
86    void drawPaint(const SkDraw&, const SkPaint& paint) override;
87    void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
88                    size_t count, const SkPoint[],
89                    const SkPaint& paint) override;
90    void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint) override;
91    void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) override;
92    void drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) override;
93    void drawPath(const SkDraw&, const SkPath& origpath,
94                  const SkPaint& paint, const SkMatrix* prePathMatrix,
95                  bool pathIsMutable) override;
96    void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src,
97                        const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override;
98    void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
99                    const SkMatrix& matrix, const SkPaint&) override;
100    void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
101                    const SkPaint& paint) override;
102    void drawText(const SkDraw&, const void* text, size_t len,
103                  SkScalar x, SkScalar y, const SkPaint&) override;
104    void drawPosText(const SkDraw&, const void* text, size_t len,
105                     const SkScalar pos[], int scalarsPerPos,
106                     const SkPoint& offset, const SkPaint&) override;
107    void drawVertices(const SkDraw&, SkCanvas::VertexMode,
108                      int vertexCount, const SkPoint verts[],
109                      const SkPoint texs[], const SkColor colors[],
110                      SkXfermode* xmode, const uint16_t indices[],
111                      int indexCount, const SkPaint& paint) override;
112    void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
113                    const SkPaint&) override;
114
115    void onAttachToCanvas(SkCanvas* canvas) override;
116    void onDetachFromCanvas() override;
117    SkImageInfo imageInfo() const override;
118
119    enum DrawingArea {
120        kContent_DrawingArea,  // Drawing area for the page content.
121        kMargin_DrawingArea,   // Drawing area for the margin content.
122    };
123
124    /** Sets the drawing area for the device. Subsequent draw calls are directed
125     *  to the specific drawing area (margin or content). The default drawing
126     *  area is the content drawing area.
127     *
128     *  Currently if margin content is drawn and then a complex (for PDF) xfer
129     *  mode is used, like SrcIn, Clear, etc, the margin content will get
130     *  clipped. A simple way to avoid the bug is to always draw the margin
131     *  content last.
132     */
133    void setDrawingArea(DrawingArea drawingArea);
134
135    // PDF specific methods.
136
137    /** Create the resource dictionary for this device.
138     */
139    SkPDFDict* createResourceDict() const;
140
141    /** Get the fonts used on this device.
142     */
143    const SkTDArray<SkPDFFont*>& getFontResources() const;
144
145    /** Add our named destinations to the supplied dictionary.
146     *  @param dict  Dictionary to add destinations to.
147     *  @param page  The PDF object representing the page for this device.
148     */
149    void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
150
151    /** Returns a copy of the media box for this device. The caller is required
152     *  to unref() this when it is finished.
153     */
154    SkPDFArray* copyMediaBox() const;
155
156    /** Get the annotations from this page, or NULL if there are none.
157     */
158    SkPDFArray* getAnnotations() const { return fAnnotations; }
159
160    /** Returns a SkStream with the page contents.  The caller is responsible
161     *  for a deleting the returned value.
162     */
163    SkStreamAsset* content() const;
164
165    /** Writes the page contents to the stream. */
166    void writeContent(SkWStream*) const;
167
168    const SkMatrix& initialTransform() const {
169        return fInitialTransform;
170    }
171
172    /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
173     *  that shows on this device.
174     */
175    const SkPDFGlyphSetMap& getFontGlyphUsage() const {
176        return *(fFontGlyphUsage.get());
177    }
178
179#ifdef SK_DEBUG
180    SkPDFCanon* getCanon() const { return fCanon; }
181#endif  // SK_DEBUG
182
183protected:
184    const SkBitmap& onAccessBitmap() override {
185        return fLegacyBitmap;
186    }
187
188    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) override;
189
190private:
191    // TODO(vandebo): push most of SkPDFDevice's state into a core object in
192    // order to get the right access levels without using friend.
193    friend class ScopedContentEntry;
194
195    SkISize fPageSize;
196    SkISize fContentSize;
197    SkMatrix fInitialTransform;
198    SkClipStack fExistingClipStack;
199    SkRegion fExistingClipRegion;
200    SkPDFArray* fAnnotations;
201    SkTDArray<NamedDestination*> fNamedDestinations;
202
203    SkTDArray<SkPDFObject*> fGraphicStateResources;
204    SkTDArray<SkPDFObject*> fXObjectResources;
205    SkTDArray<SkPDFFont*> fFontResources;
206    SkTDArray<SkPDFObject*> fShaderResources;
207
208    SkAutoTDelete<ContentEntry> fContentEntries;
209    ContentEntry* fLastContentEntry;
210    SkAutoTDelete<ContentEntry> fMarginContentEntries;
211    ContentEntry* fLastMarginContentEntry;
212    DrawingArea fDrawingArea;
213
214    const SkClipStack* fClipStack;
215
216    // Accessor and setter functions based on the current DrawingArea.
217    SkAutoTDelete<ContentEntry>* getContentEntries();
218
219    // Glyph ids used for each font on this device.
220    SkAutoTDelete<SkPDFGlyphSetMap> fFontGlyphUsage;
221
222    SkScalar fRasterDpi;
223
224    SkBitmap fLegacyBitmap;
225
226    SkPDFCanon* fCanon;  // Owned by SkDocument_PDF
227    ////////////////////////////////////////////////////////////////////////////
228
229    SkPDFDevice(SkISize pageSize,
230                SkScalar rasterDpi,
231                SkPDFCanon* canon,
232                bool flip);
233
234    ContentEntry* getLastContentEntry();
235    void setLastContentEntry(ContentEntry* contentEntry);
236
237    SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
238
239    void init();
240    void cleanUp(bool clearFontUsage);
241    SkPDFFormXObject* createFormXObjectFromDevice();
242
243    void drawFormXObjectWithMask(int xObjectIndex,
244                                 SkPDFFormXObject* mask,
245                                 const SkClipStack* clipStack,
246                                 const SkRegion& clipRegion,
247                                 SkXfermode::Mode mode,
248                                 bool invertClip);
249
250    // If the paint or clip is such that we shouldn't draw anything, this
251    // returns NULL and does not create a content entry.
252    // setUpContentEntry and finishContentEntry can be used directly, but
253    // the preferred method is to use the ScopedContentEntry helper class.
254    ContentEntry* setUpContentEntry(const SkClipStack* clipStack,
255                                    const SkRegion& clipRegion,
256                                    const SkMatrix& matrix,
257                                    const SkPaint& paint,
258                                    bool hasText,
259                                    SkPDFFormXObject** dst);
260    void finishContentEntry(SkXfermode::Mode xfermode,
261                            SkPDFFormXObject* dst,
262                            SkPath* shape);
263    bool isContentEmpty();
264
265    void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
266                                            const SkClipStack& clipStack,
267                                            const SkRegion& clipRegion,
268                                            const SkPaint& paint,
269                                            bool hasText,
270                                            GraphicStateEntry* entry);
271    int addGraphicStateResource(SkPDFObject* gs);
272    int addXObjectResource(SkPDFObject* xObject);
273
274    void updateFont(const SkPaint& paint, uint16_t glyphID, ContentEntry* contentEntry);
275    int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
276
277    void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
278    void internalDrawBitmap(const SkMatrix& matrix,
279                            const SkClipStack* clipStack,
280                            const SkRegion& clipRegion,
281                            const SkBitmap& bitmap,
282                            const SkIRect* srcRect,
283                            const SkPaint& paint);
284
285    /** Helper method for copyContentToData. It is responsible for copying the
286     *  list of content entries |entry| to |data|.
287     */
288    void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
289
290    bool handleInversePath(const SkDraw& d, const SkPath& origPath,
291                           const SkPaint& paint, bool pathIsMutable,
292                           const SkMatrix* prePathMatrix = NULL);
293    bool handlePointAnnotation(const SkPoint* points, size_t count,
294                               const SkMatrix& matrix, SkAnnotation* annot);
295    void addAnnotation(SkPDFDict*);
296
297    typedef SkBaseDevice INHERITED;
298
299    // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
300    // an SkPDFDevice
301    //friend class SkDocument_PDF;
302    //friend class SkPDFImageShader;
303};
304
305#endif
306