1/*
2 * Copyright (C) 2011 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef SkPDFDevice_DEFINED
18#define SkPDFDevice_DEFINED
19
20#include "SkCanvas.h"
21#include "SkDevice.h"
22#include "SkPaint.h"
23#include "SkPath.h"
24#include "SkRefCnt.h"
25#include "SkStream.h"
26#include "SkTScopedPtr.h"
27
28class SkPDFArray;
29class SkPDFDevice;
30class SkPDFDict;
31class SkPDFFont;
32class SkPDFFormXObject;
33class SkPDFGraphicState;
34class SkPDFObject;
35class SkPDFShader;
36class SkPDFStream;
37
38// Private classes.
39struct ContentEntry;
40struct GraphicStateEntry;
41
42class SkPDFDeviceFactory : public SkDeviceFactory {
43public:
44    virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width,
45                                int height, bool isOpaque, bool isForLayer);
46};
47
48/** \class SkPDFDevice
49
50    The drawing context for the PDF backend.
51*/
52class SkPDFDevice : public SkDevice {
53public:
54    /** Create a PDF drawing context with the given width and height.
55     *  72 points/in means letter paper is 612x792.
56     *  @param pageSize Page size in points.
57     *  @param contentSize The content size of the page in points. This will be
58     *         combined with the initial transform to determine the drawing area
59     *         (as reported by the width and height methods). Anything outside
60     *         of the drawing area will be clipped.
61     *  @param initialTransform The initial transform to apply to the page.
62     *         This may be useful to, for example, move the origin in and
63     *         over a bit to account for a margin, scale the canvas,
64     *         or apply a rotation.  Note1: the SkPDFDevice also applies
65     *         a scale+translate transform to move the origin from the
66     *         bottom left (PDF default) to the top left.  Note2: drawDevice
67     *         (used by layer restore) draws the device after this initial
68     *         transform is applied, so the PDF device factory does an
69     *         inverse scale+translate to accommodate the one that SkPDFDevice
70     *         always does.
71     */
72    // TODO(vandebo) The sizes should be SkSize and not SkISize.
73    SK_API SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
74                       const SkMatrix& initialTransform);
75    SK_API virtual ~SkPDFDevice();
76
77    virtual uint32_t getDeviceCapabilities() { return kVector_Capability; }
78
79    virtual void clear(SkColor color);
80
81    virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
82        return false;
83    }
84
85    /** These are called inside the per-device-layer loop for each draw call.
86     When these are called, we have already applied any saveLayer operations,
87     and are handling any looping from the paint, and any effects from the
88     DrawFilter.
89     */
90    virtual void drawPaint(const SkDraw&, const SkPaint& paint);
91    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
92                            size_t count, const SkPoint[],
93                            const SkPaint& paint);
94    virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint);
95    virtual void drawPath(const SkDraw&, const SkPath& origpath,
96                          const SkPaint& paint, const SkMatrix* prePathMatrix,
97                          bool pathIsMutable);
98    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
99                            const SkIRect* srcRectOrNull,
100                            const SkMatrix& matrix, const SkPaint& paint);
101    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
102                            const SkPaint& paint);
103    virtual void drawText(const SkDraw&, const void* text, size_t len,
104                          SkScalar x, SkScalar y, const SkPaint& paint);
105    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
106                             const SkScalar pos[], SkScalar constY,
107                             int scalarsPerPos, const SkPaint& paint);
108    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
109                                const SkPath& path, const SkMatrix* matrix,
110                                const SkPaint& paint);
111    virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
112                              int vertexCount, const SkPoint verts[],
113                              const SkPoint texs[], const SkColor colors[],
114                              SkXfermode* xmode, const uint16_t indices[],
115                              int indexCount, const SkPaint& paint);
116    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
117                            const SkPaint&);
118
119    // PDF specific methods.
120
121    /** Returns a reference to the resource dictionary for this device.
122     */
123    SK_API const SkRefPtr<SkPDFDict>& getResourceDict();
124
125    /** Get the list of resources (PDF objects) used on this page.
126     *  @param resourceList A list to append the resources to.
127     */
128    SK_API void getResources(SkTDArray<SkPDFObject*>* resourceList) const;
129
130    /** Get the fonts used on this device.
131     */
132    SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
133
134    /** Returns the media box for this device.
135     */
136    SK_API SkRefPtr<SkPDFArray> getMediaBox() const;
137
138    /** Returns a SkStream with the page contents.  The caller is responsible
139        for a reference to the returned value.
140     */
141    SK_API SkStream* content() const;
142
143    SK_API const SkMatrix& initialTransform() const {
144        return fInitialTransform;
145    }
146
147protected:
148    // override
149    virtual SkDeviceFactory* onNewDeviceFactory();
150
151private:
152    friend class SkPDFDeviceFactory;
153    // TODO(vandebo) push most of SkPDFDevice's state into a core object in
154    // order to get the right access levels without using friend.
155    friend class ScopedContentEntry;
156
157    SkISize fPageSize;
158    SkISize fContentSize;
159    SkMatrix fInitialTransform;
160    SkClipStack fExistingClipStack;
161    SkRegion fExistingClipRegion;
162    SkRefPtr<SkPDFDict> fResourceDict;
163
164    SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
165    SkTDArray<SkPDFObject*> fXObjectResources;
166    SkTDArray<SkPDFFont*> fFontResources;
167    SkTDArray<SkPDFShader*> fShaderResources;
168
169    SkTScopedPtr<ContentEntry> fContentEntries;
170    ContentEntry* fLastContentEntry;
171
172    // For use by the DeviceFactory.
173    SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
174                const SkRegion& existingClipRegion);
175
176    void init();
177    void cleanUp();
178    void createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject>* xobject);
179
180    // Clear the passed clip from all existing content entries.
181    void clearClipFromContent(const SkClipStack* clipStack,
182                              const SkRegion& clipRegion);
183    void drawFormXObjectWithClip(SkPDFFormXObject* form,
184                                 const SkClipStack* clipStack,
185                                 const SkRegion& clipRegion,
186                                 bool invertClip);
187
188    // If the paint or clip is such that we shouldn't draw anything, this
189    // returns NULL and does not create a content entry.
190    // setUpContentEntry and finishContentEntry can be used directly, but
191    // the preferred method is to use the ScopedContentEntry helper class.
192    ContentEntry* setUpContentEntry(const SkClipStack* clipStack,
193                                    const SkRegion& clipRegion,
194                                    const SkMatrix& matrix,
195                                    const SkPaint& paint,
196                                    bool hasText,
197                                    SkRefPtr<SkPDFFormXObject>* dst);
198    void finishContentEntry(SkXfermode::Mode xfermode,
199                            SkPDFFormXObject* dst);
200    bool isContentEmpty();
201
202    void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
203                                            const SkClipStack& clipStack,
204                                            const SkRegion& clipRegion,
205                                            const SkPaint& paint,
206                                            bool hasText,
207                                            GraphicStateEntry* entry);
208    int addGraphicStateResource(SkPDFGraphicState* gs);
209
210    void updateFont(const SkPaint& paint, uint16_t glyphID,
211                    ContentEntry* contentEntry);
212    int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
213
214    void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
215    void internalDrawBitmap(const SkMatrix& matrix,
216                            const SkClipStack* clipStack,
217                            const SkRegion& clipRegion,
218                            const SkBitmap& bitmap,
219                            const SkIRect* srcRect,
220                            const SkPaint& paint);
221
222    // Disable the default copy and assign implementation.
223    SkPDFDevice(const SkPDFDevice&);
224    void operator=(const SkPDFDevice&);
225};
226
227#endif
228