blob: d5d52f98e350a93d8f64e664f325f9aed5c917d0 [file] [log] [blame]
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2011 Google Inc.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00006 */
7
8#ifndef SkPDFDevice_DEFINED
9#define SkPDFDevice_DEFINED
10
commit-bot@chromium.org5e009892013-10-14 13:42:12 +000011#include "SkBitmap.h"
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +000012#include "SkCanvas.h"
bungemand3ebb482015-08-05 13:57:49 -070013#include "SkClipStack.h"
halcanary91fcb3e2016-03-04 13:53:22 -080014#include "SkData.h"
bungemand3ebb482015-08-05 13:57:49 -070015#include "SkDevice.h"
vandebo@chromium.orga5180862010-10-26 19:48:49 +000016#include "SkPaint.h"
vandebo@chromium.org238be8c2012-07-13 20:06:02 +000017#include "SkRect.h"
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +000018#include "SkRefCnt.h"
19#include "SkStream.h"
epoger@google.comb58772f2013-03-08 09:09:10 +000020#include "SkTDArray.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000021
halcanary2be7e012016-03-28 11:58:08 -070022#include "SkSinglyLinkedList.h"
23
martina.kollarovab8d6af12016-06-29 05:12:31 -070024class SkImageBitmap;
25class SkPath;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000026class SkPDFArray;
halcanarya1f1ee92015-02-20 06:17:26 -080027class SkPDFCanon;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000028class SkPDFDevice;
halcanary989da4a2016-03-21 14:33:17 -070029class SkPDFDocument;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000030class SkPDFDict;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000031class SkPDFFont;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000032class SkPDFObject;
halcanary4b1e17e2016-07-27 14:49:46 -070033class SkPDFStream;
scroggo@google.coma8e33a92013-11-08 18:02:53 +000034class SkRRect;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000035
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000036/** \class SkPDFDevice
37
38 The drawing context for the PDF backend.
39*/
halcanary70d15542015-11-22 12:55:04 -080040class SkPDFDevice final : public SkBaseDevice {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000041public:
halcanarya1f1ee92015-02-20 06:17:26 -080042 /** 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).
halcanary989da4a2016-03-21 14:33:17 -070056 * @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()).
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000061 */
halcanarya1f1ee92015-02-20 06:17:26 -080062 static SkPDFDevice* Create(SkISize pageSize,
63 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -070064 SkPDFDocument* doc) {
65 return new SkPDFDevice(pageSize, rasterDpi, doc, true);
halcanarya1f1ee92015-02-20 06:17:26 -080066 }
67
68 /** Create a PDF drawing context without fipping the y-axis. */
69 static SkPDFDevice* CreateUnflipped(SkISize pageSize,
70 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -070071 SkPDFDocument* doc) {
72 return new SkPDFDevice(pageSize, rasterDpi, doc, false);
halcanarya1f1ee92015-02-20 06:17:26 -080073 }
74
75 virtual ~SkPDFDevice();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000076
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000077 /** 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 */
mtklein36352bf2015-03-25 18:17:31 -070082 void drawPaint(const SkDraw&, const SkPaint& paint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -080083 void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
84 size_t count, const SkPoint[],
mtklein36352bf2015-03-25 18:17:31 -070085 const SkPaint& paint) override;
86 void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint) override;
87 void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) override;
88 void drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -080089 void drawPath(const SkDraw&, const SkPath& origpath,
90 const SkPaint& paint, const SkMatrix* prePathMatrix,
mtklein36352bf2015-03-25 18:17:31 -070091 bool pathIsMutable) override;
reed562fe472015-07-28 07:35:14 -070092 void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src,
93 const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -080094 void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
mtklein36352bf2015-03-25 18:17:31 -070095 const SkMatrix& matrix, const SkPaint&) override;
tfarinafa4f6cb2014-12-21 10:27:07 -080096 void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -070097 const SkPaint& paint) override;
halcanary7a14b312015-10-01 07:28:13 -070098 void drawImage(const SkDraw&,
99 const SkImage*,
100 SkScalar x,
101 SkScalar y,
102 const SkPaint&) override;
103 void drawImageRect(const SkDraw&,
104 const SkImage*,
105 const SkRect* src,
106 const SkRect& dst,
107 const SkPaint&,
108 SkCanvas::SrcRectConstraint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -0800109 void drawText(const SkDraw&, const void* text, size_t len,
mtklein36352bf2015-03-25 18:17:31 -0700110 SkScalar x, SkScalar y, const SkPaint&) override;
tfarinafa4f6cb2014-12-21 10:27:07 -0800111 void drawPosText(const SkDraw&, const void* text, size_t len,
112 const SkScalar pos[], int scalarsPerPos,
mtklein36352bf2015-03-25 18:17:31 -0700113 const SkPoint& offset, const SkPaint&) override;
tfarinafa4f6cb2014-12-21 10:27:07 -0800114 void drawVertices(const SkDraw&, SkCanvas::VertexMode,
115 int vertexCount, const SkPoint verts[],
116 const SkPoint texs[], const SkColor colors[],
117 SkXfermode* xmode, const uint16_t indices[],
mtklein36352bf2015-03-25 18:17:31 -0700118 int indexCount, const SkPaint& paint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -0800119 void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -0700120 const SkPaint&) override;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000121
mtklein36352bf2015-03-25 18:17:31 -0700122 SkImageInfo imageInfo() const override;
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000123
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000124 // PDF specific methods.
125
halcanary8103a342016-03-08 15:10:16 -0800126 /** Create the resource dictionary for this device. */
127 sk_sp<SkPDFDict> makeResourceDict() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000128
vandebo@chromium.orgf0ec2662011-05-29 05:55:42 +0000129 /** Get the fonts used on this device.
130 */
halcanary4e4e8162015-02-25 08:59:48 -0800131 const SkTDArray<SkPDFFont*>& getFontResources() const;
vandebo@chromium.orgf0ec2662011-05-29 05:55:42 +0000132
wangxianzhuef6c50a2015-09-17 20:38:02 -0700133 /** Add our annotations (link to urls and destinations) to the supplied
134 * array.
135 * @param array Array to add annotations to.
136 */
137 void appendAnnotations(SkPDFArray* array) const;
138
epoger@google.comb58772f2013-03-08 09:09:10 +0000139 /** Add our named destinations to the supplied dictionary.
140 * @param dict Dictionary to add destinations to.
141 * @param page The PDF object representing the page for this device.
142 */
halcanary6d622702015-03-25 08:45:42 -0700143 void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
epoger@google.comb58772f2013-03-08 09:09:10 +0000144
halcanary8103a342016-03-08 15:10:16 -0800145 /** Returns a copy of the media box for this device. */
146 sk_sp<SkPDFArray> copyMediaBox() const;
halcanary51d04d32016-03-08 13:03:55 -0800147
halcanary8103a342016-03-08 15:10:16 -0800148 /** Returns a SkStream with the page contents.
halcanary51d04d32016-03-08 13:03:55 -0800149 */
mtklein5f939ab2016-03-16 10:28:35 -0700150 std::unique_ptr<SkStreamAsset> content() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000151
halcanary334fcbc2015-02-24 12:56:16 -0800152 /** Writes the page contents to the stream. */
halcanary4e4e8162015-02-25 08:59:48 -0800153 void writeContent(SkWStream*) const;
vandebo@chromium.org98594282011-07-25 22:34:12 +0000154
halcanary4e4e8162015-02-25 08:59:48 -0800155 const SkMatrix& initialTransform() const {
vandebo@chromium.org3509f052011-05-30 20:52:33 +0000156 return fInitialTransform;
157 }
vandebo@chromium.org61d26782011-05-24 23:02:07 +0000158
halcanary989da4a2016-03-21 14:33:17 -0700159 SkPDFCanon* getCanon() const;
halcanary26b5d152015-03-25 08:38:03 -0700160
halcanary2be7e012016-03-28 11:58:08 -0700161 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
162 // later being our representation of an object in the PDF file.
163 struct GraphicStateEntry {
164 GraphicStateEntry();
165
166 // Compare the fields we care about when setting up a new content entry.
167 bool compareInitialState(const GraphicStateEntry& b);
168
169 SkMatrix fMatrix;
170 // We can't do set operations on Paths, though PDF natively supports
171 // intersect. If the clip stack does anything other than intersect,
172 // we have to fall back to the region. Treat fClipStack as authoritative.
173 // See https://bugs.skia.org/221
174 SkClipStack fClipStack;
175 SkRegion fClipRegion;
176
177 // When emitting the content entry, we will ensure the graphic state
178 // is set to these values first.
179 SkColor fColor;
180 SkScalar fTextScaleX; // Zero means we don't care what the value is.
181 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
182 int fShaderIndex;
183 int fGraphicStateIndex;
184
185 // We may change the font (i.e. for Type1 support) within a
186 // ContentEntry. This is the one currently in effect, or nullptr if none.
187 SkPDFFont* fFont;
188 // In PDF, text size has no default value. It is only valid if fFont is
189 // not nullptr.
190 SkScalar fTextSize;
191 };
192
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000193protected:
reede8f30622016-03-23 18:59:25 -0700194 sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000195
reedf70b5312016-03-04 16:36:20 -0800196 void drawAnnotation(const SkDraw&, const SkRect&, const char key[], SkData* value) override;
197
reede51c3562016-07-19 14:33:20 -0700198 void drawSpecial(const SkDraw&, SkSpecialImage*, int x, int y, const SkPaint&) override;
199 sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
200 sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
201 sk_sp<SkSpecialImage> snapSpecial() override;
202
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000203private:
halcanary91fcb3e2016-03-04 13:53:22 -0800204 struct RectWithData {
205 SkRect rect;
halcanaryd7b28852016-03-07 12:39:14 -0800206 sk_sp<SkData> data;
halcanary91fcb3e2016-03-04 13:53:22 -0800207 RectWithData(const SkRect& rect, SkData* data)
208 : rect(rect), data(SkRef(data)) {}
halcanaryd7b28852016-03-07 12:39:14 -0800209 RectWithData(RectWithData&& other)
210 : rect(other.rect), data(std::move(other.data)) {}
211 RectWithData& operator=(RectWithData&& other) {
212 rect = other.rect;
213 data = std::move(other.data);
214 return *this;
215 }
halcanary91fcb3e2016-03-04 13:53:22 -0800216 };
217
218 struct NamedDestination {
halcanaryd7b28852016-03-07 12:39:14 -0800219 sk_sp<SkData> nameData;
halcanary91fcb3e2016-03-04 13:53:22 -0800220 SkPoint point;
221 NamedDestination(SkData* nameData, const SkPoint& point)
222 : nameData(SkRef(nameData)), point(point) {}
halcanaryd7b28852016-03-07 12:39:14 -0800223 NamedDestination(NamedDestination&& other)
224 : nameData(std::move(other.nameData)), point(other.point) {}
225 NamedDestination& operator=(NamedDestination&& other) {
226 nameData = std::move(other.nameData);
227 point = other.point;
228 return *this;
229 }
halcanary91fcb3e2016-03-04 13:53:22 -0800230 };
231
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000232 // TODO(vandebo): push most of SkPDFDevice's state into a core object in
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000233 // order to get the right access levels without using friend.
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000234 friend class ScopedContentEntry;
vandebo@chromium.orga0c7edb2011-05-09 07:58:08 +0000235
ctguil@chromium.org15261292011-04-29 17:54:16 +0000236 SkISize fPageSize;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000237 SkISize fContentSize;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000238 SkMatrix fInitialTransform;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000239 SkClipStack fExistingClipStack;
240 SkRegion fExistingClipRegion;
wangxianzhuef6c50a2015-09-17 20:38:02 -0700241
halcanary91fcb3e2016-03-04 13:53:22 -0800242 SkTArray<RectWithData> fLinkToURLs;
243 SkTArray<RectWithData> fLinkToDestinations;
244 SkTArray<NamedDestination> fNamedDestinations;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000245
halcanarybe27a112015-04-01 13:31:19 -0700246 SkTDArray<SkPDFObject*> fGraphicStateResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000247 SkTDArray<SkPDFObject*> fXObjectResources;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000248 SkTDArray<SkPDFFont*> fFontResources;
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000249 SkTDArray<SkPDFObject*> fShaderResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000250
halcanary2be7e012016-03-28 11:58:08 -0700251 struct ContentEntry {
252 GraphicStateEntry fState;
253 SkDynamicMemoryWStream fContent;
254 };
255 SkSinglyLinkedList<ContentEntry> fContentEntries;
ctguil@chromium.org8dcf74f2011-07-12 21:56:27 +0000256
commit-bot@chromium.org8c294902013-10-21 17:14:37 +0000257 SkScalar fRasterDpi;
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000258
halcanary989da4a2016-03-21 14:33:17 -0700259 SkPDFDocument* fDocument;
halcanarya1f1ee92015-02-20 06:17:26 -0800260 ////////////////////////////////////////////////////////////////////////////
261
262 SkPDFDevice(SkISize pageSize,
263 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -0700264 SkPDFDocument* doc,
halcanarya1f1ee92015-02-20 06:17:26 -0800265 bool flip);
266
mtklein36352bf2015-03-25 18:17:31 -0700267 SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
bsalomon@google.come97f0852011-06-17 13:10:25 +0000268
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000269 void init();
halcanary3c35fb32016-06-30 11:55:07 -0700270 void cleanUp();
halcanary4b1e17e2016-07-27 14:49:46 -0700271 sk_sp<SkPDFObject> makeFormXObjectFromDevice();
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000272
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000273 void drawFormXObjectWithMask(int xObjectIndex,
halcanary4b1e17e2016-07-27 14:49:46 -0700274 SkPDFObject* mask,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000275 const SkClipStack* clipStack,
276 const SkRegion& clipRegion,
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000277 SkXfermode::Mode mode,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000278 bool invertClip);
vandebo@chromium.org466f3d62011-05-18 23:06:29 +0000279
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000280 // If the paint or clip is such that we shouldn't draw anything, this
halcanary96fcdcc2015-08-27 07:41:13 -0700281 // returns nullptr and does not create a content entry.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000282 // setUpContentEntry and finishContentEntry can be used directly, but
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000283 // the preferred method is to use the ScopedContentEntry helper class.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000284 ContentEntry* setUpContentEntry(const SkClipStack* clipStack,
285 const SkRegion& clipRegion,
286 const SkMatrix& matrix,
287 const SkPaint& paint,
288 bool hasText,
halcanary4b1e17e2016-07-27 14:49:46 -0700289 SkPDFObject** dst);
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000290 void finishContentEntry(SkXfermode::Mode xfermode,
halcanary4b1e17e2016-07-27 14:49:46 -0700291 SkPDFObject* dst,
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000292 SkPath* shape);
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000293 bool isContentEmpty();
294
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000295 void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
296 const SkClipStack& clipStack,
297 const SkRegion& clipRegion,
298 const SkPaint& paint,
299 bool hasText,
300 GraphicStateEntry* entry);
halcanarybe27a112015-04-01 13:31:19 -0700301 int addGraphicStateResource(SkPDFObject* gs);
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000302 int addXObjectResource(SkPDFObject* xObject);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000303
robertphillips8e0c1502015-07-07 10:28:43 -0700304 void updateFont(const SkPaint& paint, uint16_t glyphID, ContentEntry* contentEntry);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000305 int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000306
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000307 void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
halcanarya50151d2016-03-25 11:57:49 -0700308
309 void internalDrawImage(const SkMatrix& origMatrix,
halcanary7a14b312015-10-01 07:28:13 -0700310 const SkClipStack* clipStack,
halcanarya50151d2016-03-25 11:57:49 -0700311 const SkRegion& origClipRegion,
312 SkImageBitmap imageBitmap,
halcanary7a14b312015-10-01 07:28:13 -0700313 const SkPaint& paint);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000314
commit-bot@chromium.org92ffe7d2013-07-31 22:54:31 +0000315 bool handleInversePath(const SkDraw& d, const SkPath& origPath,
edisonn@google.coma9ebd162013-10-07 13:22:21 +0000316 const SkPaint& paint, bool pathIsMutable,
halcanary96fcdcc2015-08-27 07:41:13 -0700317 const SkMatrix* prePathMatrix = nullptr);
reedf70b5312016-03-04 16:36:20 -0800318 void handlePointAnnotation(const SkPoint&, const SkMatrix&, const char key[], SkData* value);
319 void handlePathAnnotation(const SkPath&, const SkDraw& d, const char key[], SkData* value);
vandebo@chromium.org238be8c2012-07-13 20:06:02 +0000320
reed89443ab2014-06-27 11:34:19 -0700321 typedef SkBaseDevice INHERITED;
commit-bot@chromium.org5e009892013-10-14 13:42:12 +0000322
323 // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
324 // an SkPDFDevice
325 //friend class SkDocument_PDF;
326 //friend class SkPDFImageShader;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000327};
328
329#endif