blob: 7c7ddf43cbe93520fef281bbfc9d184ee4dca6f1 [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.org6112c212011-05-13 03:50:38 +000032class SkPDFFormXObject;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000033class SkPDFObject;
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 void onAttachToCanvas(SkCanvas* canvas) override;
123 void onDetachFromCanvas() override;
124 SkImageInfo imageInfo() const override;
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000125
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000126 // PDF specific methods.
127
halcanary8103a342016-03-08 15:10:16 -0800128 /** Create the resource dictionary for this device. */
129 sk_sp<SkPDFDict> makeResourceDict() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000130
vandebo@chromium.orgf0ec2662011-05-29 05:55:42 +0000131 /** Get the fonts used on this device.
132 */
halcanary4e4e8162015-02-25 08:59:48 -0800133 const SkTDArray<SkPDFFont*>& getFontResources() const;
vandebo@chromium.orgf0ec2662011-05-29 05:55:42 +0000134
wangxianzhuef6c50a2015-09-17 20:38:02 -0700135 /** Add our annotations (link to urls and destinations) to the supplied
136 * array.
137 * @param array Array to add annotations to.
138 */
139 void appendAnnotations(SkPDFArray* array) const;
140
epoger@google.comb58772f2013-03-08 09:09:10 +0000141 /** Add our named destinations to the supplied dictionary.
142 * @param dict Dictionary to add destinations to.
143 * @param page The PDF object representing the page for this device.
144 */
halcanary6d622702015-03-25 08:45:42 -0700145 void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
epoger@google.comb58772f2013-03-08 09:09:10 +0000146
halcanary8103a342016-03-08 15:10:16 -0800147 /** Returns a copy of the media box for this device. */
148 sk_sp<SkPDFArray> copyMediaBox() const;
halcanary51d04d32016-03-08 13:03:55 -0800149
halcanary8103a342016-03-08 15:10:16 -0800150 /** Returns a SkStream with the page contents.
halcanary51d04d32016-03-08 13:03:55 -0800151 */
mtklein5f939ab2016-03-16 10:28:35 -0700152 std::unique_ptr<SkStreamAsset> content() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000153
halcanary334fcbc2015-02-24 12:56:16 -0800154 /** Writes the page contents to the stream. */
halcanary4e4e8162015-02-25 08:59:48 -0800155 void writeContent(SkWStream*) const;
vandebo@chromium.org98594282011-07-25 22:34:12 +0000156
halcanary4e4e8162015-02-25 08:59:48 -0800157 const SkMatrix& initialTransform() const {
vandebo@chromium.org3509f052011-05-30 20:52:33 +0000158 return fInitialTransform;
159 }
vandebo@chromium.org61d26782011-05-24 23:02:07 +0000160
halcanary989da4a2016-03-21 14:33:17 -0700161 SkPDFCanon* getCanon() const;
halcanary26b5d152015-03-25 08:38:03 -0700162
halcanary2be7e012016-03-28 11:58:08 -0700163 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
164 // later being our representation of an object in the PDF file.
165 struct GraphicStateEntry {
166 GraphicStateEntry();
167
168 // Compare the fields we care about when setting up a new content entry.
169 bool compareInitialState(const GraphicStateEntry& b);
170
171 SkMatrix fMatrix;
172 // We can't do set operations on Paths, though PDF natively supports
173 // intersect. If the clip stack does anything other than intersect,
174 // we have to fall back to the region. Treat fClipStack as authoritative.
175 // See https://bugs.skia.org/221
176 SkClipStack fClipStack;
177 SkRegion fClipRegion;
178
179 // When emitting the content entry, we will ensure the graphic state
180 // is set to these values first.
181 SkColor fColor;
182 SkScalar fTextScaleX; // Zero means we don't care what the value is.
183 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
184 int fShaderIndex;
185 int fGraphicStateIndex;
186
187 // We may change the font (i.e. for Type1 support) within a
188 // ContentEntry. This is the one currently in effect, or nullptr if none.
189 SkPDFFont* fFont;
190 // In PDF, text size has no default value. It is only valid if fFont is
191 // not nullptr.
192 SkScalar fTextSize;
193 };
194
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000195protected:
mtklein36352bf2015-03-25 18:17:31 -0700196 const SkBitmap& onAccessBitmap() override {
reed89443ab2014-06-27 11:34:19 -0700197 return fLegacyBitmap;
198 }
199
reede8f30622016-03-23 18:59:25 -0700200 sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000201
reedf70b5312016-03-04 16:36:20 -0800202 void drawAnnotation(const SkDraw&, const SkRect&, const char key[], SkData* value) override;
203
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000204private:
halcanary91fcb3e2016-03-04 13:53:22 -0800205 struct RectWithData {
206 SkRect rect;
halcanaryd7b28852016-03-07 12:39:14 -0800207 sk_sp<SkData> data;
halcanary91fcb3e2016-03-04 13:53:22 -0800208 RectWithData(const SkRect& rect, SkData* data)
209 : rect(rect), data(SkRef(data)) {}
halcanaryd7b28852016-03-07 12:39:14 -0800210 RectWithData(RectWithData&& other)
211 : rect(other.rect), data(std::move(other.data)) {}
212 RectWithData& operator=(RectWithData&& other) {
213 rect = other.rect;
214 data = std::move(other.data);
215 return *this;
216 }
halcanary91fcb3e2016-03-04 13:53:22 -0800217 };
218
219 struct NamedDestination {
halcanaryd7b28852016-03-07 12:39:14 -0800220 sk_sp<SkData> nameData;
halcanary91fcb3e2016-03-04 13:53:22 -0800221 SkPoint point;
222 NamedDestination(SkData* nameData, const SkPoint& point)
223 : nameData(SkRef(nameData)), point(point) {}
halcanaryd7b28852016-03-07 12:39:14 -0800224 NamedDestination(NamedDestination&& other)
225 : nameData(std::move(other.nameData)), point(other.point) {}
226 NamedDestination& operator=(NamedDestination&& other) {
227 nameData = std::move(other.nameData);
228 point = other.point;
229 return *this;
230 }
halcanary91fcb3e2016-03-04 13:53:22 -0800231 };
232
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000233 // TODO(vandebo): push most of SkPDFDevice's state into a core object in
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000234 // order to get the right access levels without using friend.
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000235 friend class ScopedContentEntry;
vandebo@chromium.orga0c7edb2011-05-09 07:58:08 +0000236
ctguil@chromium.org15261292011-04-29 17:54:16 +0000237 SkISize fPageSize;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000238 SkISize fContentSize;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000239 SkMatrix fInitialTransform;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000240 SkClipStack fExistingClipStack;
241 SkRegion fExistingClipRegion;
wangxianzhuef6c50a2015-09-17 20:38:02 -0700242
halcanary91fcb3e2016-03-04 13:53:22 -0800243 SkTArray<RectWithData> fLinkToURLs;
244 SkTArray<RectWithData> fLinkToDestinations;
245 SkTArray<NamedDestination> fNamedDestinations;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000246
halcanarybe27a112015-04-01 13:31:19 -0700247 SkTDArray<SkPDFObject*> fGraphicStateResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000248 SkTDArray<SkPDFObject*> fXObjectResources;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000249 SkTDArray<SkPDFFont*> fFontResources;
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000250 SkTDArray<SkPDFObject*> fShaderResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000251
halcanary2be7e012016-03-28 11:58:08 -0700252 struct ContentEntry {
253 GraphicStateEntry fState;
254 SkDynamicMemoryWStream fContent;
255 };
256 SkSinglyLinkedList<ContentEntry> fContentEntries;
ctguil@chromium.org8dcf74f2011-07-12 21:56:27 +0000257
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000258 const SkClipStack* fClipStack;
259
commit-bot@chromium.org8c294902013-10-21 17:14:37 +0000260 SkScalar fRasterDpi;
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000261
reed89443ab2014-06-27 11:34:19 -0700262 SkBitmap fLegacyBitmap;
263
halcanary989da4a2016-03-21 14:33:17 -0700264 SkPDFDocument* fDocument;
halcanarya1f1ee92015-02-20 06:17:26 -0800265 ////////////////////////////////////////////////////////////////////////////
266
267 SkPDFDevice(SkISize pageSize,
268 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -0700269 SkPDFDocument* doc,
halcanarya1f1ee92015-02-20 06:17:26 -0800270 bool flip);
271
mtklein36352bf2015-03-25 18:17:31 -0700272 SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
bsalomon@google.come97f0852011-06-17 13:10:25 +0000273
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000274 void init();
halcanary3c35fb32016-06-30 11:55:07 -0700275 void cleanUp();
reed@google.comfc641d02012-09-20 17:52:20 +0000276 SkPDFFormXObject* createFormXObjectFromDevice();
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000277
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000278 void drawFormXObjectWithMask(int xObjectIndex,
279 SkPDFFormXObject* mask,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000280 const SkClipStack* clipStack,
281 const SkRegion& clipRegion,
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000282 SkXfermode::Mode mode,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000283 bool invertClip);
vandebo@chromium.org466f3d62011-05-18 23:06:29 +0000284
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000285 // If the paint or clip is such that we shouldn't draw anything, this
halcanary96fcdcc2015-08-27 07:41:13 -0700286 // returns nullptr and does not create a content entry.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000287 // setUpContentEntry and finishContentEntry can be used directly, but
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000288 // the preferred method is to use the ScopedContentEntry helper class.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000289 ContentEntry* setUpContentEntry(const SkClipStack* clipStack,
290 const SkRegion& clipRegion,
291 const SkMatrix& matrix,
292 const SkPaint& paint,
293 bool hasText,
reed@google.comfc641d02012-09-20 17:52:20 +0000294 SkPDFFormXObject** dst);
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000295 void finishContentEntry(SkXfermode::Mode xfermode,
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000296 SkPDFFormXObject* dst,
297 SkPath* shape);
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000298 bool isContentEmpty();
299
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000300 void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
301 const SkClipStack& clipStack,
302 const SkRegion& clipRegion,
303 const SkPaint& paint,
304 bool hasText,
305 GraphicStateEntry* entry);
halcanarybe27a112015-04-01 13:31:19 -0700306 int addGraphicStateResource(SkPDFObject* gs);
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000307 int addXObjectResource(SkPDFObject* xObject);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000308
robertphillips8e0c1502015-07-07 10:28:43 -0700309 void updateFont(const SkPaint& paint, uint16_t glyphID, ContentEntry* contentEntry);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000310 int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000311
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000312 void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
halcanarya50151d2016-03-25 11:57:49 -0700313
314 void internalDrawImage(const SkMatrix& origMatrix,
halcanary7a14b312015-10-01 07:28:13 -0700315 const SkClipStack* clipStack,
halcanarya50151d2016-03-25 11:57:49 -0700316 const SkRegion& origClipRegion,
317 SkImageBitmap imageBitmap,
halcanary7a14b312015-10-01 07:28:13 -0700318 const SkPaint& paint);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000319
commit-bot@chromium.org92ffe7d2013-07-31 22:54:31 +0000320 bool handleInversePath(const SkDraw& d, const SkPath& origPath,
edisonn@google.coma9ebd162013-10-07 13:22:21 +0000321 const SkPaint& paint, bool pathIsMutable,
halcanary96fcdcc2015-08-27 07:41:13 -0700322 const SkMatrix* prePathMatrix = nullptr);
reedf70b5312016-03-04 16:36:20 -0800323 void handlePointAnnotation(const SkPoint&, const SkMatrix&, const char key[], SkData* value);
324 void handlePathAnnotation(const SkPath&, const SkDraw& d, const char key[], SkData* value);
vandebo@chromium.org238be8c2012-07-13 20:06:02 +0000325
reed89443ab2014-06-27 11:34:19 -0700326 typedef SkBaseDevice INHERITED;
commit-bot@chromium.org5e009892013-10-14 13:42:12 +0000327
328 // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
329 // an SkPDFDevice
330 //friend class SkDocument_PDF;
331 //friend class SkPDFImageShader;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000332};
333
334#endif