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 "SkCanvas.h"
14#include "SkDevice.h"
15#include "SkPaint.h"
16#include "SkPath.h"
17#include "SkRect.h"
18#include "SkRefCnt.h"
19#include "SkStream.h"
20#include "SkTDArray.h"
21#include "SkTScopedPtr.h"
22
23class SkPDFArray;
24class SkPDFDevice;
25class SkPDFDict;
26class SkPDFFont;
27class SkPDFFormXObject;
28class SkPDFGlyphSetMap;
29class SkPDFGraphicState;
30class SkPDFObject;
31class SkPDFResourceDict;
32class SkPDFShader;
33class SkPDFStream;
34template <typename T> class SkTSet;
35
36// Private classes.
37struct ContentEntry;
38struct GraphicStateEntry;
39struct NamedDestination;
40
41typedef bool (*EncodeToDCTStream)(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect);
42
43/** \class SkPDFDevice
44
45    The drawing context for the PDF backend.
46*/
47class SkPDFDevice : public SkDevice {
48public:
49    /** Create a PDF drawing context with the given width and height.
50     *  72 points/in means letter paper is 612x792.
51     *  @param pageSize Page size in points.
52     *  @param contentSize The content size of the page in points. This will be
53     *         combined with the initial transform to determine the drawing area
54     *         (as reported by the width and height methods). Anything outside
55     *         of the drawing area will be clipped.
56     *  @param initialTransform The initial transform to apply to the page.
57     *         This may be useful to, for example, move the origin in and
58     *         over a bit to account for a margin, scale the canvas,
59     *         or apply a rotation.  Note1: the SkPDFDevice also applies
60     *         a scale+translate transform to move the origin from the
61     *         bottom left (PDF default) to the top left.  Note2: drawDevice
62     *         (used by layer restore) draws the device after this initial
63     *         transform is applied, so the PDF device does an
64     *         inverse scale+translate to accommodate the one that SkPDFDevice
65     *         always does.
66     */
67    // TODO(vandebo): The sizes should be SkSize and not SkISize.
68    SK_API SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
69                       const SkMatrix& initialTransform);
70    SK_API virtual ~SkPDFDevice();
71
72    virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
73
74    virtual void clear(SkColor color) SK_OVERRIDE;
75
76    /** These are called inside the per-device-layer loop for each draw call.
77     When these are called, we have already applied any saveLayer operations,
78     and are handling any looping from the paint, and any effects from the
79     DrawFilter.
80     */
81    virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE;
82    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
83                            size_t count, const SkPoint[],
84                            const SkPaint& paint) SK_OVERRIDE;
85    virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint);
86    virtual void drawPath(const SkDraw&, const SkPath& origpath,
87                          const SkPaint& paint, const SkMatrix* prePathMatrix,
88                          bool pathIsMutable) SK_OVERRIDE;
89    virtual void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
90                                const SkRect* src, const SkRect& dst,
91                                const SkPaint& paint) SK_OVERRIDE;
92    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
93                            const SkMatrix& matrix, const SkPaint&) SK_OVERRIDE;
94    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
95                            const SkPaint& paint) SK_OVERRIDE;
96    virtual void drawText(const SkDraw&, const void* text, size_t len,
97                          SkScalar x, SkScalar y, const SkPaint&) SK_OVERRIDE;
98    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
99                             const SkScalar pos[], SkScalar constY,
100                             int scalarsPerPos, const SkPaint&) SK_OVERRIDE;
101    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
102                                const SkPath& path, const SkMatrix* matrix,
103                                const SkPaint& paint) SK_OVERRIDE;
104    virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
105                              int vertexCount, const SkPoint verts[],
106                              const SkPoint texs[], const SkColor colors[],
107                              SkXfermode* xmode, const uint16_t indices[],
108                              int indexCount, const SkPaint& paint) SK_OVERRIDE;
109    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
110                            const SkPaint&) SK_OVERRIDE;
111
112    virtual void onAttachToCanvas(SkCanvas* canvas) SK_OVERRIDE;
113    virtual void onDetachFromCanvas() SK_OVERRIDE;
114
115    enum DrawingArea {
116        kContent_DrawingArea,  // Drawing area for the page content.
117        kMargin_DrawingArea,   // Drawing area for the margin content.
118    };
119
120    /** Sets the drawing area for the device. Subsequent draw calls are directed
121     *  to the specific drawing area (margin or content). The default drawing
122     *  area is the content drawing area.
123     *
124     *  Currently if margin content is drawn and then a complex (for PDF) xfer
125     *  mode is used, like SrcIn, Clear, etc, the margin content will get
126     *  clipped. A simple way to avoid the bug is to always draw the margin
127     *  content last.
128     */
129    SK_API void setDrawingArea(DrawingArea drawingArea);
130
131    /** Sets the DCTEncoder for images.
132     *  @param encoder The encoder to encode a bitmap as JPEG (DCT).
133     *         Result of encodings are cached, if the encoder changes the
134     *         behaivor dynamically and an image is added to a second catalog,
135     *         we will likely use the result of the first encoding call.
136     *         By returning false from the encoder function, the encoder result
137     *         is not used.
138     *         Callers might not want to encode small images, as the time spent
139     *         encoding and decoding might not be worth the space savings,
140     *         if any at all.
141     */
142    void setDCTEncoder(EncodeToDCTStream encoder) {
143        fEncoder = encoder;
144    }
145
146    // PDF specific methods.
147
148    /** Returns the resource dictionary for this device.
149     */
150    SK_API SkPDFResourceDict* getResourceDict();
151
152    /** Get the fonts used on this device.
153     */
154    SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
155
156    /** Add our named destinations to the supplied dictionary.
157     *  @param dict  Dictionary to add destinations to.
158     *  @param page  The PDF object representing the page for this device.
159     */
160    void appendDestinations(SkPDFDict* dict, SkPDFObject* page);
161
162    /** Returns a copy of the media box for this device. The caller is required
163     *  to unref() this when it is finished.
164     */
165    SK_API SkPDFArray* copyMediaBox() const;
166
167    /** Get the annotations from this page, or NULL if there are none.
168     */
169    SK_API SkPDFArray* getAnnotations() const { return fAnnotations; }
170
171    /** Returns a SkStream with the page contents.  The caller is responsible
172        for a reference to the returned value.
173        DEPRECATED: use copyContentToData()
174     */
175    SK_API SkStream* content() const;
176
177    /** Returns a SkStream with the page contents.  The caller is responsible
178     *  for calling data->unref() when it is finished.
179     */
180    SK_API SkData* copyContentToData() const;
181
182    SK_API const SkMatrix& initialTransform() const {
183        return fInitialTransform;
184    }
185
186    /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
187     *  that shows on this device.
188     */
189    const SkPDFGlyphSetMap& getFontGlyphUsage() const {
190        return *(fFontGlyphUsage.get());
191    }
192
193protected:
194    virtual bool onReadPixels(const SkBitmap& bitmap, int x, int y,
195                              SkCanvas::Config8888) SK_OVERRIDE;
196
197    virtual bool allowImageFilter(SkImageFilter*) SK_OVERRIDE;
198
199private:
200    // TODO(vandebo): push most of SkPDFDevice's state into a core object in
201    // order to get the right access levels without using friend.
202    friend class ScopedContentEntry;
203
204    SkISize fPageSize;
205    SkISize fContentSize;
206    SkMatrix fInitialTransform;
207    SkClipStack fExistingClipStack;
208    SkRegion fExistingClipRegion;
209    SkPDFArray* fAnnotations;
210    SkPDFResourceDict* fResourceDict;
211    SkTDArray<NamedDestination*> fNamedDestinations;
212
213    SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
214    SkTDArray<SkPDFObject*> fXObjectResources;
215    SkTDArray<SkPDFFont*> fFontResources;
216    SkTDArray<SkPDFObject*> fShaderResources;
217
218    SkTScopedPtr<ContentEntry> fContentEntries;
219    ContentEntry* fLastContentEntry;
220    SkTScopedPtr<ContentEntry> fMarginContentEntries;
221    ContentEntry* fLastMarginContentEntry;
222    DrawingArea fDrawingArea;
223
224    const SkClipStack* fClipStack;
225
226    // Accessor and setter functions based on the current DrawingArea.
227    SkTScopedPtr<ContentEntry>* getContentEntries();
228    ContentEntry* getLastContentEntry();
229    void setLastContentEntry(ContentEntry* contentEntry);
230
231    // Glyph ids used for each font on this device.
232    SkTScopedPtr<SkPDFGlyphSetMap> fFontGlyphUsage;
233
234    EncodeToDCTStream fEncoder;
235
236    SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
237                const SkRegion& existingClipRegion);
238
239    // override from SkDevice
240    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
241                                               int width, int height,
242                                               bool isOpaque,
243                                               Usage usage) SK_OVERRIDE;
244
245    void init();
246    void cleanUp(bool clearFontUsage);
247    SkPDFFormXObject* createFormXObjectFromDevice();
248
249    // Clear the passed clip from all existing content entries.
250    void clearClipFromContent(const SkClipStack* clipStack,
251                              const SkRegion& clipRegion);
252    void drawFormXObjectWithClip(SkPDFFormXObject* form,
253                                 const SkClipStack* clipStack,
254                                 const SkRegion& clipRegion,
255                                 bool invertClip);
256
257    // If the paint or clip is such that we shouldn't draw anything, this
258    // returns NULL and does not create a content entry.
259    // setUpContentEntry and finishContentEntry can be used directly, but
260    // the preferred method is to use the ScopedContentEntry helper class.
261    ContentEntry* setUpContentEntry(const SkClipStack* clipStack,
262                                    const SkRegion& clipRegion,
263                                    const SkMatrix& matrix,
264                                    const SkPaint& paint,
265                                    bool hasText,
266                                    SkPDFFormXObject** dst);
267    void finishContentEntry(SkXfermode::Mode xfermode,
268                            SkPDFFormXObject* dst);
269    bool isContentEmpty();
270
271    void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
272                                            const SkClipStack& clipStack,
273                                            const SkRegion& clipRegion,
274                                            const SkPaint& paint,
275                                            bool hasText,
276                                            GraphicStateEntry* entry);
277    int addGraphicStateResource(SkPDFGraphicState* gs);
278
279    void updateFont(const SkPaint& paint, uint16_t glyphID,
280                    ContentEntry* contentEntry);
281    int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
282
283    void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
284    void internalDrawBitmap(const SkMatrix& matrix,
285                            const SkClipStack* clipStack,
286                            const SkRegion& clipRegion,
287                            const SkBitmap& bitmap,
288                            const SkIRect* srcRect,
289                            const SkPaint& paint);
290
291    /** Helper method for copyContentToData. It is responsible for copying the
292     *  list of content entries |entry| to |data|.
293     */
294    void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
295
296#ifdef SK_PDF_USE_PATHOPS
297    bool handleInversePath(const SkDraw& d, const SkPath& origPath,
298                           const SkPaint& paint, bool pathIsMutable);
299#endif
300    bool handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
301                              const SkPaint& paint);
302    bool handlePointAnnotation(const SkPoint* points, size_t count,
303                               const SkMatrix& matrix, const SkPaint& paint);
304    SkPDFDict* createLinkAnnotation(const SkRect& r, const SkMatrix& matrix);
305    void handleLinkToURL(SkData* urlData, const SkRect& r,
306                         const SkMatrix& matrix);
307    void handleLinkToNamedDest(SkData* nameData, const SkRect& r,
308                               const SkMatrix& matrix);
309    void defineNamedDestination(SkData* nameData, const SkPoint& point,
310                                const SkMatrix& matrix);
311
312    typedef SkDevice INHERITED;
313};
314
315#endif
316