blob: e409ed9bb213536e957cf0318329535956ad9c30 [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;
Hal Canaryb9642382017-06-27 09:58:56 -040094 void drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -050095 void drawSprite(const SkBitmap& bitmap, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -070096 const SkPaint& paint) override;
Mike Reeda1361362017-03-07 09:37:29 -050097 void drawImage(const SkImage*,
halcanary7a14b312015-10-01 07:28:13 -070098 SkScalar x,
99 SkScalar y,
100 const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500101 void drawImageRect(const SkImage*,
halcanary7a14b312015-10-01 07:28:13 -0700102 const SkRect* src,
103 const SkRect& dst,
104 const SkPaint&,
105 SkCanvas::SrcRectConstraint) override;
Mike Reeda1361362017-03-07 09:37:29 -0500106 void drawText(const void* text, size_t len,
mtklein36352bf2015-03-25 18:17:31 -0700107 SkScalar x, SkScalar y, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500108 void drawPosText(const void* text, size_t len,
tfarinafa4f6cb2014-12-21 10:27:07 -0800109 const SkScalar pos[], int scalarsPerPos,
mtklein36352bf2015-03-25 18:17:31 -0700110 const SkPoint& offset, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500111 void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
halcanarye06ca962016-09-09 05:34:55 -0700112 const SkPaint &, SkDrawFilter*) override;
Mike Reed2f6b5a42017-03-19 15:04:17 -0400113 void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
Mike Reeda1361362017-03-07 09:37:29 -0500114 void drawDevice(SkBaseDevice*, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -0700115 const SkPaint&) override;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000116
117 // PDF specific methods.
118
halcanary8103a342016-03-08 15:10:16 -0800119 /** Create the resource dictionary for this device. */
120 sk_sp<SkPDFDict> makeResourceDict() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000121
wangxianzhuef6c50a2015-09-17 20:38:02 -0700122 /** Add our annotations (link to urls and destinations) to the supplied
123 * array.
124 * @param array Array to add annotations to.
125 */
126 void appendAnnotations(SkPDFArray* array) const;
127
epoger@google.comb58772f2013-03-08 09:09:10 +0000128 /** Add our named destinations to the supplied dictionary.
129 * @param dict Dictionary to add destinations to.
130 * @param page The PDF object representing the page for this device.
131 */
halcanary6d622702015-03-25 08:45:42 -0700132 void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
epoger@google.comb58772f2013-03-08 09:09:10 +0000133
halcanary8103a342016-03-08 15:10:16 -0800134 /** Returns a copy of the media box for this device. */
135 sk_sp<SkPDFArray> copyMediaBox() const;
halcanary51d04d32016-03-08 13:03:55 -0800136
halcanary8103a342016-03-08 15:10:16 -0800137 /** Returns a SkStream with the page contents.
halcanary51d04d32016-03-08 13:03:55 -0800138 */
mtklein5f939ab2016-03-16 10:28:35 -0700139 std::unique_ptr<SkStreamAsset> content() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000140
halcanary989da4a2016-03-21 14:33:17 -0700141 SkPDFCanon* getCanon() const;
halcanary26b5d152015-03-25 08:38:03 -0700142
halcanary2be7e012016-03-28 11:58:08 -0700143 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
144 // later being our representation of an object in the PDF file.
145 struct GraphicStateEntry {
146 GraphicStateEntry();
147
148 // Compare the fields we care about when setting up a new content entry.
149 bool compareInitialState(const GraphicStateEntry& b);
150
151 SkMatrix fMatrix;
152 // We can't do set operations on Paths, though PDF natively supports
153 // intersect. If the clip stack does anything other than intersect,
154 // we have to fall back to the region. Treat fClipStack as authoritative.
155 // See https://bugs.skia.org/221
156 SkClipStack fClipStack;
halcanary2be7e012016-03-28 11:58:08 -0700157
158 // When emitting the content entry, we will ensure the graphic state
159 // is set to these values first.
160 SkColor fColor;
161 SkScalar fTextScaleX; // Zero means we don't care what the value is.
162 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
163 int fShaderIndex;
164 int fGraphicStateIndex;
halcanary2be7e012016-03-28 11:58:08 -0700165 };
166
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000167protected:
reede8f30622016-03-23 18:59:25 -0700168 sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000169
Mike Reeda1361362017-03-07 09:37:29 -0500170 void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
reedf70b5312016-03-04 16:36:20 -0800171
Florin Malita53f77bd2017-04-28 13:48:37 -0400172 void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
173 SkImage*, const SkMatrix&) 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 };
184
185 struct NamedDestination {
halcanaryd7b28852016-03-07 12:39:14 -0800186 sk_sp<SkData> nameData;
halcanary91fcb3e2016-03-04 13:53:22 -0800187 SkPoint point;
halcanary91fcb3e2016-03-04 13:53:22 -0800188 };
189
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000190 // TODO(vandebo): push most of SkPDFDevice's state into a core object in
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000191 // order to get the right access levels without using friend.
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000192 friend class ScopedContentEntry;
vandebo@chromium.orga0c7edb2011-05-09 07:58:08 +0000193
ctguil@chromium.org15261292011-04-29 17:54:16 +0000194 SkISize fPageSize;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000195 SkMatrix fInitialTransform;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000196 SkClipStack fExistingClipStack;
wangxianzhuef6c50a2015-09-17 20:38:02 -0700197
halcanary91fcb3e2016-03-04 13:53:22 -0800198 SkTArray<RectWithData> fLinkToURLs;
199 SkTArray<RectWithData> fLinkToDestinations;
200 SkTArray<NamedDestination> fNamedDestinations;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000201
halcanarybe27a112015-04-01 13:31:19 -0700202 SkTDArray<SkPDFObject*> fGraphicStateResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000203 SkTDArray<SkPDFObject*> fXObjectResources;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000204 SkTDArray<SkPDFFont*> fFontResources;
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000205 SkTDArray<SkPDFObject*> fShaderResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000206
halcanary2be7e012016-03-28 11:58:08 -0700207 struct ContentEntry {
208 GraphicStateEntry fState;
209 SkDynamicMemoryWStream fContent;
210 };
211 SkSinglyLinkedList<ContentEntry> fContentEntries;
ctguil@chromium.org8dcf74f2011-07-12 21:56:27 +0000212
commit-bot@chromium.org8c294902013-10-21 17:14:37 +0000213 SkScalar fRasterDpi;
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000214
halcanary989da4a2016-03-21 14:33:17 -0700215 SkPDFDocument* fDocument;
halcanarya1f1ee92015-02-20 06:17:26 -0800216 ////////////////////////////////////////////////////////////////////////////
217
218 SkPDFDevice(SkISize pageSize,
219 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -0700220 SkPDFDocument* doc,
halcanarya1f1ee92015-02-20 06:17:26 -0800221 bool flip);
222
mtklein36352bf2015-03-25 18:17:31 -0700223 SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
bsalomon@google.come97f0852011-06-17 13:10:25 +0000224
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000225 void init();
halcanary3c35fb32016-06-30 11:55:07 -0700226 void cleanUp();
halcanary4b1e17e2016-07-27 14:49:46 -0700227 sk_sp<SkPDFObject> makeFormXObjectFromDevice();
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000228
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000229 void drawFormXObjectWithMask(int xObjectIndex,
halcanarydabd4f02016-08-03 11:16:56 -0700230 sk_sp<SkPDFObject> mask,
Mike Reeda1361362017-03-07 09:37:29 -0500231 const SkClipStack& clipStack,
reed374772b2016-10-05 17:33:02 -0700232 SkBlendMode,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000233 bool invertClip);
vandebo@chromium.org466f3d62011-05-18 23:06:29 +0000234
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000235 // If the paint or clip is such that we shouldn't draw anything, this
halcanary96fcdcc2015-08-27 07:41:13 -0700236 // returns nullptr and does not create a content entry.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000237 // setUpContentEntry and finishContentEntry can be used directly, but
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000238 // the preferred method is to use the ScopedContentEntry helper class.
Mike Reeda1361362017-03-07 09:37:29 -0500239 ContentEntry* setUpContentEntry(const SkClipStack& clipStack,
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000240 const SkMatrix& matrix,
241 const SkPaint& paint,
242 bool hasText,
halcanarydabd4f02016-08-03 11:16:56 -0700243 sk_sp<SkPDFObject>* dst);
reed374772b2016-10-05 17:33:02 -0700244 void finishContentEntry(SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000245 bool isContentEmpty();
246
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000247 void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
248 const SkClipStack& clipStack,
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000249 const SkPaint& paint,
250 bool hasText,
251 GraphicStateEntry* entry);
halcanarybe27a112015-04-01 13:31:19 -0700252 int addGraphicStateResource(SkPDFObject* gs);
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000253 int addXObjectResource(SkPDFObject* xObject);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000254
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000255 int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000256
halcanary4ed2f012016-08-15 18:40:07 -0700257
Mike Reeda1361362017-03-07 09:37:29 -0500258 void internalDrawText( const void*, size_t, const SkScalar pos[],
halcanarye06ca962016-09-09 05:34:55 -0700259 SkTextBlob::GlyphPositioning, SkPoint, const SkPaint&,
260 const uint32_t*, uint32_t, const char*);
halcanary4ed2f012016-08-15 18:40:07 -0700261
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000262 void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
halcanarya50151d2016-03-25 11:57:49 -0700263
264 void internalDrawImage(const SkMatrix& origMatrix,
Mike Reeda1361362017-03-07 09:37:29 -0500265 const SkClipStack& clipStack,
Hal Canaryf50ff392016-09-30 10:25:39 -0400266 SkImageSubset imageSubset,
halcanary7a14b312015-10-01 07:28:13 -0700267 const SkPaint& paint);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000268
Mike Reeda1361362017-03-07 09:37:29 -0500269 void internalDrawPath(const SkClipStack&,
270 const SkMatrix&,
271 const SkPath&,
272 const SkPaint&,
273 const SkMatrix* prePathMatrix,
274 bool pathIsMutable);
275
Hal Canaryd12a6762017-05-26 17:01:16 -0400276 void internalDrawPathWithFilter(const SkClipStack& clipStack,
277 const SkMatrix& ctm,
278 const SkPath& origPath,
279 const SkPaint& paint,
280 const SkMatrix* prePathMatrix);
281
Mike Reeda1361362017-03-07 09:37:29 -0500282 bool handleInversePath(const SkPath& origPath,
edisonn@google.coma9ebd162013-10-07 13:22:21 +0000283 const SkPaint& paint, bool pathIsMutable,
halcanary96fcdcc2015-08-27 07:41:13 -0700284 const SkMatrix* prePathMatrix = nullptr);
vandebo@chromium.org238be8c2012-07-13 20:06:02 +0000285
Mike Reedf0fb9292017-02-21 13:15:07 -0500286 typedef SkClipStackDevice INHERITED;
commit-bot@chromium.org5e009892013-10-14 13:42:12 +0000287
288 // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
289 // an SkPDFDevice
290 //friend class SkDocument_PDF;
291 //friend class SkPDFImageShader;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000292};
293
294#endif