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