blob: 93eb78dc50a8213ea6e6b4b5fa7d7a69894fc1df [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"
Mike Reedf0fb9292017-02-21 13:15:07 -050014#include "SkClipStackDevice.h"
halcanary91fcb3e2016-03-04 13:53:22 -080015#include "SkData.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"
halcanary4ed2f012016-08-15 18:40:07 -070019#include "SkSinglyLinkedList.h"
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +000020#include "SkStream.h"
epoger@google.comb58772f2013-03-08 09:09:10 +000021#include "SkTDArray.h"
halcanary4ed2f012016-08-15 18:40:07 -070022#include "SkTextBlob.h"
halcanary2be7e012016-03-28 11:58:08 -070023
Hal Canaryf50ff392016-09-30 10:25:39 -040024class SkImageSubset;
martina.kollarovab8d6af12016-06-29 05:12:31 -070025class 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*/
Mike Reedf0fb9292017-02-21 13:15:07 -050040class SkPDFDevice final : public SkClipStackDevice {
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
Brian Salomond3b65972017-03-22 12:05:03 -040075 ~SkPDFDevice() override;
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 */
Mike Reeda1361362017-03-07 09:37:29 -050082 void drawPaint(const SkPaint& paint) override;
83 void drawPoints(SkCanvas::PointMode mode,
tfarinafa4f6cb2014-12-21 10:27:07 -080084 size_t count, const SkPoint[],
mtklein36352bf2015-03-25 18:17:31 -070085 const SkPaint& paint) override;
Mike Reeda1361362017-03-07 09:37:29 -050086 void drawRect(const SkRect& r, const SkPaint& paint) override;
87 void drawOval(const SkRect& oval, const SkPaint& paint) override;
88 void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
89 void drawPath(const SkPath& origpath,
tfarinafa4f6cb2014-12-21 10:27:07 -080090 const SkPaint& paint, const SkMatrix* prePathMatrix,
mtklein36352bf2015-03-25 18:17:31 -070091 bool pathIsMutable) override;
Mike Reeda1361362017-03-07 09:37:29 -050092 void drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed562fe472015-07-28 07:35:14 -070093 const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override;
Mike Reeda1361362017-03-07 09:37:29 -050094 void drawBitmap(const SkBitmap& bitmap,
mtklein36352bf2015-03-25 18:17:31 -070095 const SkMatrix& matrix, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -050096 void drawSprite(const SkBitmap& bitmap, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -070097 const SkPaint& paint) override;
Mike Reeda1361362017-03-07 09:37:29 -050098 void drawImage(const SkImage*,
halcanary7a14b312015-10-01 07:28:13 -070099 SkScalar x,
100 SkScalar y,
101 const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500102 void drawImageRect(const SkImage*,
halcanary7a14b312015-10-01 07:28:13 -0700103 const SkRect* src,
104 const SkRect& dst,
105 const SkPaint&,
106 SkCanvas::SrcRectConstraint) override;
Mike Reeda1361362017-03-07 09:37:29 -0500107 void drawText(const void* text, size_t len,
mtklein36352bf2015-03-25 18:17:31 -0700108 SkScalar x, SkScalar y, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500109 void drawPosText(const void* text, size_t len,
tfarinafa4f6cb2014-12-21 10:27:07 -0800110 const SkScalar pos[], int scalarsPerPos,
mtklein36352bf2015-03-25 18:17:31 -0700111 const SkPoint& offset, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500112 void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
halcanarye06ca962016-09-09 05:34:55 -0700113 const SkPaint &, SkDrawFilter*) override;
Mike Reed2f6b5a42017-03-19 15:04:17 -0400114 void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500115 void drawDevice(SkBaseDevice*, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -0700116 const SkPaint&) override;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000117
118 // PDF specific methods.
119
halcanary8103a342016-03-08 15:10:16 -0800120 /** Create the resource dictionary for this device. */
121 sk_sp<SkPDFDict> makeResourceDict() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000122
wangxianzhuef6c50a2015-09-17 20:38:02 -0700123 /** Add our annotations (link to urls and destinations) to the supplied
124 * array.
125 * @param array Array to add annotations to.
126 */
127 void appendAnnotations(SkPDFArray* array) const;
128
epoger@google.comb58772f2013-03-08 09:09:10 +0000129 /** Add our named destinations to the supplied dictionary.
130 * @param dict Dictionary to add destinations to.
131 * @param page The PDF object representing the page for this device.
132 */
halcanary6d622702015-03-25 08:45:42 -0700133 void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
epoger@google.comb58772f2013-03-08 09:09:10 +0000134
halcanary8103a342016-03-08 15:10:16 -0800135 /** Returns a copy of the media box for this device. */
136 sk_sp<SkPDFArray> copyMediaBox() const;
halcanary51d04d32016-03-08 13:03:55 -0800137
halcanary8103a342016-03-08 15:10:16 -0800138 /** Returns a SkStream with the page contents.
halcanary51d04d32016-03-08 13:03:55 -0800139 */
mtklein5f939ab2016-03-16 10:28:35 -0700140 std::unique_ptr<SkStreamAsset> content() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000141
halcanary989da4a2016-03-21 14:33:17 -0700142 SkPDFCanon* getCanon() const;
halcanary26b5d152015-03-25 08:38:03 -0700143
halcanary2be7e012016-03-28 11:58:08 -0700144 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
145 // later being our representation of an object in the PDF file.
146 struct GraphicStateEntry {
147 GraphicStateEntry();
148
149 // Compare the fields we care about when setting up a new content entry.
150 bool compareInitialState(const GraphicStateEntry& b);
151
152 SkMatrix fMatrix;
153 // We can't do set operations on Paths, though PDF natively supports
154 // intersect. If the clip stack does anything other than intersect,
155 // we have to fall back to the region. Treat fClipStack as authoritative.
156 // See https://bugs.skia.org/221
157 SkClipStack fClipStack;
halcanary2be7e012016-03-28 11:58:08 -0700158
159 // When emitting the content entry, we will ensure the graphic state
160 // is set to these values first.
161 SkColor fColor;
162 SkScalar fTextScaleX; // Zero means we don't care what the value is.
163 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
164 int fShaderIndex;
165 int fGraphicStateIndex;
halcanary2be7e012016-03-28 11:58:08 -0700166 };
167
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000168protected:
reede8f30622016-03-23 18:59:25 -0700169 sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000170
Mike Reeda1361362017-03-07 09:37:29 -0500171 void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
reedf70b5312016-03-04 16:36:20 -0800172
Mike Reeda1361362017-03-07 09:37:29 -0500173 void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override;
reede51c3562016-07-19 14:33:20 -0700174 sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
175 sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
176 sk_sp<SkSpecialImage> snapSpecial() override;
brianosman04a44d02016-09-21 09:46:57 -0700177 SkImageFilterCache* getImageFilterCache() override;
reede51c3562016-07-19 14:33:20 -0700178
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000179private:
halcanary91fcb3e2016-03-04 13:53:22 -0800180 struct RectWithData {
181 SkRect rect;
halcanaryd7b28852016-03-07 12:39:14 -0800182 sk_sp<SkData> data;
halcanary91fcb3e2016-03-04 13:53:22 -0800183 RectWithData(const SkRect& rect, SkData* data)
184 : rect(rect), data(SkRef(data)) {}
halcanary9be37202016-08-08 07:21:42 -0700185 RectWithData(RectWithData&&) = default;
186 RectWithData& operator=(RectWithData&& other) = default;
halcanary91fcb3e2016-03-04 13:53:22 -0800187 };
188
189 struct NamedDestination {
halcanaryd7b28852016-03-07 12:39:14 -0800190 sk_sp<SkData> nameData;
halcanary91fcb3e2016-03-04 13:53:22 -0800191 SkPoint point;
192 NamedDestination(SkData* nameData, const SkPoint& point)
193 : nameData(SkRef(nameData)), point(point) {}
halcanary9be37202016-08-08 07:21:42 -0700194 NamedDestination(NamedDestination&&) = default;
195 NamedDestination& operator=(NamedDestination&&) = default;
halcanary91fcb3e2016-03-04 13:53:22 -0800196 };
197
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000198 // TODO(vandebo): push most of SkPDFDevice's state into a core object in
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000199 // order to get the right access levels without using friend.
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000200 friend class ScopedContentEntry;
vandebo@chromium.orga0c7edb2011-05-09 07:58:08 +0000201
ctguil@chromium.org15261292011-04-29 17:54:16 +0000202 SkISize fPageSize;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000203 SkMatrix fInitialTransform;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000204 SkClipStack fExistingClipStack;
wangxianzhuef6c50a2015-09-17 20:38:02 -0700205
halcanary91fcb3e2016-03-04 13:53:22 -0800206 SkTArray<RectWithData> fLinkToURLs;
207 SkTArray<RectWithData> fLinkToDestinations;
208 SkTArray<NamedDestination> fNamedDestinations;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000209
halcanarybe27a112015-04-01 13:31:19 -0700210 SkTDArray<SkPDFObject*> fGraphicStateResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000211 SkTDArray<SkPDFObject*> fXObjectResources;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000212 SkTDArray<SkPDFFont*> fFontResources;
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000213 SkTDArray<SkPDFObject*> fShaderResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000214
halcanary2be7e012016-03-28 11:58:08 -0700215 struct ContentEntry {
216 GraphicStateEntry fState;
217 SkDynamicMemoryWStream fContent;
218 };
219 SkSinglyLinkedList<ContentEntry> fContentEntries;
ctguil@chromium.org8dcf74f2011-07-12 21:56:27 +0000220
commit-bot@chromium.org8c294902013-10-21 17:14:37 +0000221 SkScalar fRasterDpi;
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000222
halcanary989da4a2016-03-21 14:33:17 -0700223 SkPDFDocument* fDocument;
halcanarya1f1ee92015-02-20 06:17:26 -0800224 ////////////////////////////////////////////////////////////////////////////
225
226 SkPDFDevice(SkISize pageSize,
227 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -0700228 SkPDFDocument* doc,
halcanarya1f1ee92015-02-20 06:17:26 -0800229 bool flip);
230
mtklein36352bf2015-03-25 18:17:31 -0700231 SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
bsalomon@google.come97f0852011-06-17 13:10:25 +0000232
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000233 void init();
halcanary3c35fb32016-06-30 11:55:07 -0700234 void cleanUp();
halcanary4b1e17e2016-07-27 14:49:46 -0700235 sk_sp<SkPDFObject> makeFormXObjectFromDevice();
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000236
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000237 void drawFormXObjectWithMask(int xObjectIndex,
halcanarydabd4f02016-08-03 11:16:56 -0700238 sk_sp<SkPDFObject> mask,
Mike Reeda1361362017-03-07 09:37:29 -0500239 const SkClipStack& clipStack,
reed374772b2016-10-05 17:33:02 -0700240 SkBlendMode,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000241 bool invertClip);
vandebo@chromium.org466f3d62011-05-18 23:06:29 +0000242
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000243 // If the paint or clip is such that we shouldn't draw anything, this
halcanary96fcdcc2015-08-27 07:41:13 -0700244 // returns nullptr and does not create a content entry.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000245 // setUpContentEntry and finishContentEntry can be used directly, but
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000246 // the preferred method is to use the ScopedContentEntry helper class.
Mike Reeda1361362017-03-07 09:37:29 -0500247 ContentEntry* setUpContentEntry(const SkClipStack& clipStack,
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000248 const SkMatrix& matrix,
249 const SkPaint& paint,
250 bool hasText,
halcanarydabd4f02016-08-03 11:16:56 -0700251 sk_sp<SkPDFObject>* dst);
reed374772b2016-10-05 17:33:02 -0700252 void finishContentEntry(SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000253 bool isContentEmpty();
254
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000255 void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
256 const SkClipStack& clipStack,
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000257 const SkPaint& paint,
258 bool hasText,
259 GraphicStateEntry* entry);
halcanarybe27a112015-04-01 13:31:19 -0700260 int addGraphicStateResource(SkPDFObject* gs);
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000261 int addXObjectResource(SkPDFObject* xObject);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000262
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000263 int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000264
halcanary4ed2f012016-08-15 18:40:07 -0700265
Mike Reeda1361362017-03-07 09:37:29 -0500266 void internalDrawText( const void*, size_t, const SkScalar pos[],
halcanarye06ca962016-09-09 05:34:55 -0700267 SkTextBlob::GlyphPositioning, SkPoint, const SkPaint&,
268 const uint32_t*, uint32_t, const char*);
halcanary4ed2f012016-08-15 18:40:07 -0700269
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000270 void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
halcanarya50151d2016-03-25 11:57:49 -0700271
272 void internalDrawImage(const SkMatrix& origMatrix,
Mike Reeda1361362017-03-07 09:37:29 -0500273 const SkClipStack& clipStack,
Hal Canaryf50ff392016-09-30 10:25:39 -0400274 SkImageSubset imageSubset,
halcanary7a14b312015-10-01 07:28:13 -0700275 const SkPaint& paint);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000276
Mike Reeda1361362017-03-07 09:37:29 -0500277 void internalDrawPath(const SkClipStack&,
278 const SkMatrix&,
279 const SkPath&,
280 const SkPaint&,
281 const SkMatrix* prePathMatrix,
282 bool pathIsMutable);
283
284 bool handleInversePath(const SkPath& origPath,
edisonn@google.coma9ebd162013-10-07 13:22:21 +0000285 const SkPaint& paint, bool pathIsMutable,
halcanary96fcdcc2015-08-27 07:41:13 -0700286 const SkMatrix* prePathMatrix = nullptr);
vandebo@chromium.org238be8c2012-07-13 20:06:02 +0000287
Mike Reedf0fb9292017-02-21 13:15:07 -0500288 typedef SkClipStackDevice INHERITED;
commit-bot@chromium.org5e009892013-10-14 13:42:12 +0000289
290 // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
291 // an SkPDFDevice
292 //friend class SkDocument_PDF;
293 //friend class SkPDFImageShader;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000294};
295
296#endif