blob: ad44bcd12d65cf9bfe06054e80abff3f3d342b41 [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 */
Hal Canary51329c92017-06-27 14:28:37 -040062 static sk_sp<SkPDFDevice> Make(SkISize pageSize, SkScalar rasterDpi, SkPDFDocument* doc) {
63 return sk_sp<SkPDFDevice>(new SkPDFDevice(pageSize, rasterDpi, doc, true));
halcanarya1f1ee92015-02-20 06:17:26 -080064 }
65
66 /** Create a PDF drawing context without fipping the y-axis. */
Hal Canary51329c92017-06-27 14:28:37 -040067 static sk_sp<SkPDFDevice> MakeUnflipped(SkISize pageSize,
68 SkScalar rasterDpi,
69 SkPDFDocument* doc) {
70 return sk_sp<SkPDFDevice>(new SkPDFDevice(pageSize, rasterDpi, doc, false));
71 }
72
73 sk_sp<SkPDFDevice> makeCongruentDevice() {
74 return sk_sp<SkPDFDevice>(new SkPDFDevice(fPageSize, fRasterDpi, fDocument, false));
halcanarya1f1ee92015-02-20 06:17:26 -080075 }
76
Brian Salomond3b65972017-03-22 12:05:03 -040077 ~SkPDFDevice() override;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000078
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000079 /** These are called inside the per-device-layer loop for each draw call.
80 When these are called, we have already applied any saveLayer operations,
81 and are handling any looping from the paint, and any effects from the
82 DrawFilter.
83 */
Mike Reeda1361362017-03-07 09:37:29 -050084 void drawPaint(const SkPaint& paint) override;
85 void drawPoints(SkCanvas::PointMode mode,
tfarinafa4f6cb2014-12-21 10:27:07 -080086 size_t count, const SkPoint[],
mtklein36352bf2015-03-25 18:17:31 -070087 const SkPaint& paint) override;
Mike Reeda1361362017-03-07 09:37:29 -050088 void drawRect(const SkRect& r, const SkPaint& paint) override;
89 void drawOval(const SkRect& oval, const SkPaint& paint) override;
90 void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
91 void drawPath(const SkPath& origpath,
tfarinafa4f6cb2014-12-21 10:27:07 -080092 const SkPaint& paint, const SkMatrix* prePathMatrix,
mtklein36352bf2015-03-25 18:17:31 -070093 bool pathIsMutable) override;
Mike Reeda1361362017-03-07 09:37:29 -050094 void drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed562fe472015-07-28 07:35:14 -070095 const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override;
Hal Canaryb9642382017-06-27 09:58:56 -040096 void drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -050097 void drawSprite(const SkBitmap& bitmap, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -070098 const SkPaint& paint) override;
Mike Reeda1361362017-03-07 09:37:29 -050099 void drawImage(const SkImage*,
halcanary7a14b312015-10-01 07:28:13 -0700100 SkScalar x,
101 SkScalar y,
102 const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500103 void drawImageRect(const SkImage*,
halcanary7a14b312015-10-01 07:28:13 -0700104 const SkRect* src,
105 const SkRect& dst,
106 const SkPaint&,
107 SkCanvas::SrcRectConstraint) override;
Mike Reeda1361362017-03-07 09:37:29 -0500108 void drawText(const void* text, size_t len,
mtklein36352bf2015-03-25 18:17:31 -0700109 SkScalar x, SkScalar y, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500110 void drawPosText(const void* text, size_t len,
tfarinafa4f6cb2014-12-21 10:27:07 -0800111 const SkScalar pos[], int scalarsPerPos,
mtklein36352bf2015-03-25 18:17:31 -0700112 const SkPoint& offset, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500113 void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
halcanarye06ca962016-09-09 05:34:55 -0700114 const SkPaint &, SkDrawFilter*) override;
Mike Reed2f6b5a42017-03-19 15:04:17 -0400115 void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500116 void drawDevice(SkBaseDevice*, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -0700117 const SkPaint&) override;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000118
119 // PDF specific methods.
120
halcanary8103a342016-03-08 15:10:16 -0800121 /** Create the resource dictionary for this device. */
122 sk_sp<SkPDFDict> makeResourceDict() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000123
wangxianzhuef6c50a2015-09-17 20:38:02 -0700124 /** Add our annotations (link to urls and destinations) to the supplied
125 * array.
126 * @param array Array to add annotations to.
127 */
128 void appendAnnotations(SkPDFArray* array) const;
129
epoger@google.comb58772f2013-03-08 09:09:10 +0000130 /** Add our named destinations to the supplied dictionary.
131 * @param dict Dictionary to add destinations to.
132 * @param page The PDF object representing the page for this device.
133 */
halcanary6d622702015-03-25 08:45:42 -0700134 void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
epoger@google.comb58772f2013-03-08 09:09:10 +0000135
halcanary8103a342016-03-08 15:10:16 -0800136 /** Returns a copy of the media box for this device. */
137 sk_sp<SkPDFArray> copyMediaBox() const;
halcanary51d04d32016-03-08 13:03:55 -0800138
halcanary8103a342016-03-08 15:10:16 -0800139 /** Returns a SkStream with the page contents.
halcanary51d04d32016-03-08 13:03:55 -0800140 */
mtklein5f939ab2016-03-16 10:28:35 -0700141 std::unique_ptr<SkStreamAsset> content() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000142
halcanary989da4a2016-03-21 14:33:17 -0700143 SkPDFCanon* getCanon() const;
halcanary26b5d152015-03-25 08:38:03 -0700144
halcanary2be7e012016-03-28 11:58:08 -0700145 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
146 // later being our representation of an object in the PDF file.
147 struct GraphicStateEntry {
148 GraphicStateEntry();
149
150 // Compare the fields we care about when setting up a new content entry.
151 bool compareInitialState(const GraphicStateEntry& b);
152
153 SkMatrix fMatrix;
154 // We can't do set operations on Paths, though PDF natively supports
155 // intersect. If the clip stack does anything other than intersect,
156 // we have to fall back to the region. Treat fClipStack as authoritative.
157 // See https://bugs.skia.org/221
158 SkClipStack fClipStack;
halcanary2be7e012016-03-28 11:58:08 -0700159
160 // When emitting the content entry, we will ensure the graphic state
161 // is set to these values first.
162 SkColor fColor;
163 SkScalar fTextScaleX; // Zero means we don't care what the value is.
164 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
165 int fShaderIndex;
166 int fGraphicStateIndex;
halcanary2be7e012016-03-28 11:58:08 -0700167 };
168
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000169protected:
reede8f30622016-03-23 18:59:25 -0700170 sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000171
Mike Reeda1361362017-03-07 09:37:29 -0500172 void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
reedf70b5312016-03-04 16:36:20 -0800173
Florin Malita53f77bd2017-04-28 13:48:37 -0400174 void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
175 SkImage*, const SkMatrix&) override;
reede51c3562016-07-19 14:33:20 -0700176 sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
177 sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
178 sk_sp<SkSpecialImage> snapSpecial() override;
brianosman04a44d02016-09-21 09:46:57 -0700179 SkImageFilterCache* getImageFilterCache() override;
reede51c3562016-07-19 14:33:20 -0700180
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000181private:
halcanary91fcb3e2016-03-04 13:53:22 -0800182 struct RectWithData {
183 SkRect rect;
halcanaryd7b28852016-03-07 12:39:14 -0800184 sk_sp<SkData> data;
halcanary91fcb3e2016-03-04 13:53:22 -0800185 };
186
187 struct NamedDestination {
halcanaryd7b28852016-03-07 12:39:14 -0800188 sk_sp<SkData> nameData;
halcanary91fcb3e2016-03-04 13:53:22 -0800189 SkPoint point;
halcanary91fcb3e2016-03-04 13:53:22 -0800190 };
191
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000192 // TODO(vandebo): push most of SkPDFDevice's state into a core object in
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000193 // order to get the right access levels without using friend.
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000194 friend class ScopedContentEntry;
vandebo@chromium.orga0c7edb2011-05-09 07:58:08 +0000195
ctguil@chromium.org15261292011-04-29 17:54:16 +0000196 SkISize fPageSize;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000197 SkMatrix fInitialTransform;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000198 SkClipStack fExistingClipStack;
wangxianzhuef6c50a2015-09-17 20:38:02 -0700199
halcanary91fcb3e2016-03-04 13:53:22 -0800200 SkTArray<RectWithData> fLinkToURLs;
201 SkTArray<RectWithData> fLinkToDestinations;
202 SkTArray<NamedDestination> fNamedDestinations;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000203
halcanarybe27a112015-04-01 13:31:19 -0700204 SkTDArray<SkPDFObject*> fGraphicStateResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000205 SkTDArray<SkPDFObject*> fXObjectResources;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000206 SkTDArray<SkPDFFont*> fFontResources;
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000207 SkTDArray<SkPDFObject*> fShaderResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000208
halcanary2be7e012016-03-28 11:58:08 -0700209 struct ContentEntry {
210 GraphicStateEntry fState;
211 SkDynamicMemoryWStream fContent;
212 };
213 SkSinglyLinkedList<ContentEntry> fContentEntries;
ctguil@chromium.org8dcf74f2011-07-12 21:56:27 +0000214
commit-bot@chromium.org8c294902013-10-21 17:14:37 +0000215 SkScalar fRasterDpi;
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000216
halcanary989da4a2016-03-21 14:33:17 -0700217 SkPDFDocument* fDocument;
halcanarya1f1ee92015-02-20 06:17:26 -0800218 ////////////////////////////////////////////////////////////////////////////
219
220 SkPDFDevice(SkISize pageSize,
221 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -0700222 SkPDFDocument* doc,
halcanarya1f1ee92015-02-20 06:17:26 -0800223 bool flip);
224
mtklein36352bf2015-03-25 18:17:31 -0700225 SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
bsalomon@google.come97f0852011-06-17 13:10:25 +0000226
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000227 void init();
halcanary3c35fb32016-06-30 11:55:07 -0700228 void cleanUp();
halcanary4b1e17e2016-07-27 14:49:46 -0700229 sk_sp<SkPDFObject> makeFormXObjectFromDevice();
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000230
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000231 void drawFormXObjectWithMask(int xObjectIndex,
halcanarydabd4f02016-08-03 11:16:56 -0700232 sk_sp<SkPDFObject> mask,
Mike Reeda1361362017-03-07 09:37:29 -0500233 const SkClipStack& clipStack,
reed374772b2016-10-05 17:33:02 -0700234 SkBlendMode,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000235 bool invertClip);
vandebo@chromium.org466f3d62011-05-18 23:06:29 +0000236
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000237 // If the paint or clip is such that we shouldn't draw anything, this
halcanary96fcdcc2015-08-27 07:41:13 -0700238 // returns nullptr and does not create a content entry.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000239 // setUpContentEntry and finishContentEntry can be used directly, but
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000240 // the preferred method is to use the ScopedContentEntry helper class.
Mike Reeda1361362017-03-07 09:37:29 -0500241 ContentEntry* setUpContentEntry(const SkClipStack& clipStack,
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000242 const SkMatrix& matrix,
243 const SkPaint& paint,
244 bool hasText,
halcanarydabd4f02016-08-03 11:16:56 -0700245 sk_sp<SkPDFObject>* dst);
reed374772b2016-10-05 17:33:02 -0700246 void finishContentEntry(SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000247 bool isContentEmpty();
248
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000249 void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
250 const SkClipStack& clipStack,
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000251 const SkPaint& paint,
252 bool hasText,
253 GraphicStateEntry* entry);
halcanarybe27a112015-04-01 13:31:19 -0700254 int addGraphicStateResource(SkPDFObject* gs);
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000255 int addXObjectResource(SkPDFObject* xObject);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000256
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000257 int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000258
halcanary4ed2f012016-08-15 18:40:07 -0700259
Mike Reeda1361362017-03-07 09:37:29 -0500260 void internalDrawText( const void*, size_t, const SkScalar pos[],
halcanarye06ca962016-09-09 05:34:55 -0700261 SkTextBlob::GlyphPositioning, SkPoint, const SkPaint&,
262 const uint32_t*, uint32_t, const char*);
halcanary4ed2f012016-08-15 18:40:07 -0700263
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000264 void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
halcanarya50151d2016-03-25 11:57:49 -0700265
266 void internalDrawImage(const SkMatrix& origMatrix,
Mike Reeda1361362017-03-07 09:37:29 -0500267 const SkClipStack& clipStack,
Hal Canaryf50ff392016-09-30 10:25:39 -0400268 SkImageSubset imageSubset,
halcanary7a14b312015-10-01 07:28:13 -0700269 const SkPaint& paint);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000270
Mike Reeda1361362017-03-07 09:37:29 -0500271 void internalDrawPath(const SkClipStack&,
272 const SkMatrix&,
273 const SkPath&,
274 const SkPaint&,
275 const SkMatrix* prePathMatrix,
276 bool pathIsMutable);
277
Hal Canaryd12a6762017-05-26 17:01:16 -0400278 void internalDrawPathWithFilter(const SkClipStack& clipStack,
279 const SkMatrix& ctm,
280 const SkPath& origPath,
281 const SkPaint& paint,
282 const SkMatrix* prePathMatrix);
283
Mike Reeda1361362017-03-07 09:37:29 -0500284 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
Hal Canary51329c92017-06-27 14:28:37 -0400288 void addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice, SkDynamicMemoryWStream*);
289 void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
commit-bot@chromium.org5e009892013-10-14 13:42:12 +0000290
Hal Canary51329c92017-06-27 14:28:37 -0400291 typedef SkClipStackDevice INHERITED;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000292};
293
294#endif