blob: 4dded7316c785042384e1c15e9e612cdd751d5e5 [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.org98594282011-07-25 22:34:12 +000033class SkPDFGlyphSetMap;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000034class SkPDFObject;
scroggo@google.coma8e33a92013-11-08 18:02:53 +000035class SkRRect;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000036
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000037/** \class SkPDFDevice
38
39 The drawing context for the PDF backend.
40*/
halcanary70d15542015-11-22 12:55:04 -080041class SkPDFDevice final : public SkBaseDevice {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000042public:
halcanarya1f1ee92015-02-20 06:17:26 -080043 /** Create a PDF drawing context. SkPDFDevice applies a
44 * scale-and-translate transform to move the origin from the
45 * bottom left (PDF default) to the top left (Skia default).
46 * @param pageSize Page size in point units.
47 * 1 point == 127/360 mm == 1/72 inch
48 * @param rasterDpi the DPI at which features without native PDF
49 * support will be rasterized (e.g. draw image with
50 * perspective, draw text with perspective, ...). A
51 * larger DPI would create a PDF that reflects the
52 * original intent with better fidelity, but it can make
53 * for larger PDF files too, which would use more memory
54 * while rendering, and it would be slower to be processed
55 * or sent online or to printer. A good choice is
56 * SK_ScalarDefaultRasterDPI(72.0f).
halcanary989da4a2016-03-21 14:33:17 -070057 * @param SkPDFDocument. A non-null pointer back to the
58 * document. The document is repsonsible for
59 * de-duplicating across pages (via the SkPDFCanon) and
60 * for early serializing of large immutable objects, such
61 * as images (via SkPDFDocument::serialize()).
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000062 */
halcanarya1f1ee92015-02-20 06:17:26 -080063 static SkPDFDevice* Create(SkISize pageSize,
64 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -070065 SkPDFDocument* doc) {
66 return new SkPDFDevice(pageSize, rasterDpi, doc, true);
halcanarya1f1ee92015-02-20 06:17:26 -080067 }
68
69 /** Create a PDF drawing context without fipping the y-axis. */
70 static SkPDFDevice* CreateUnflipped(SkISize pageSize,
71 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -070072 SkPDFDocument* doc) {
73 return new SkPDFDevice(pageSize, rasterDpi, doc, false);
halcanarya1f1ee92015-02-20 06:17:26 -080074 }
75
76 virtual ~SkPDFDevice();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000077
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000078 /** These are called inside the per-device-layer loop for each draw call.
79 When these are called, we have already applied any saveLayer operations,
80 and are handling any looping from the paint, and any effects from the
81 DrawFilter.
82 */
mtklein36352bf2015-03-25 18:17:31 -070083 void drawPaint(const SkDraw&, const SkPaint& paint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -080084 void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
85 size_t count, const SkPoint[],
mtklein36352bf2015-03-25 18:17:31 -070086 const SkPaint& paint) override;
87 void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint) override;
88 void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) override;
89 void drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -080090 void drawPath(const SkDraw&, const SkPath& origpath,
91 const SkPaint& paint, const SkMatrix* prePathMatrix,
mtklein36352bf2015-03-25 18:17:31 -070092 bool pathIsMutable) override;
reed562fe472015-07-28 07:35:14 -070093 void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src,
94 const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -080095 void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
mtklein36352bf2015-03-25 18:17:31 -070096 const SkMatrix& matrix, const SkPaint&) override;
tfarinafa4f6cb2014-12-21 10:27:07 -080097 void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -070098 const SkPaint& paint) override;
halcanary7a14b312015-10-01 07:28:13 -070099 void drawImage(const SkDraw&,
100 const SkImage*,
101 SkScalar x,
102 SkScalar y,
103 const SkPaint&) override;
104 void drawImageRect(const SkDraw&,
105 const SkImage*,
106 const SkRect* src,
107 const SkRect& dst,
108 const SkPaint&,
109 SkCanvas::SrcRectConstraint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -0800110 void drawText(const SkDraw&, const void* text, size_t len,
mtklein36352bf2015-03-25 18:17:31 -0700111 SkScalar x, SkScalar y, const SkPaint&) override;
tfarinafa4f6cb2014-12-21 10:27:07 -0800112 void drawPosText(const SkDraw&, const void* text, size_t len,
113 const SkScalar pos[], int scalarsPerPos,
mtklein36352bf2015-03-25 18:17:31 -0700114 const SkPoint& offset, const SkPaint&) override;
tfarinafa4f6cb2014-12-21 10:27:07 -0800115 void drawVertices(const SkDraw&, SkCanvas::VertexMode,
116 int vertexCount, const SkPoint verts[],
117 const SkPoint texs[], const SkColor colors[],
118 SkXfermode* xmode, const uint16_t indices[],
mtklein36352bf2015-03-25 18:17:31 -0700119 int indexCount, const SkPaint& paint) override;
tfarinafa4f6cb2014-12-21 10:27:07 -0800120 void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
mtklein36352bf2015-03-25 18:17:31 -0700121 const SkPaint&) override;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000122
mtklein36352bf2015-03-25 18:17:31 -0700123 void onAttachToCanvas(SkCanvas* canvas) override;
124 void onDetachFromCanvas() override;
125 SkImageInfo imageInfo() const override;
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000126
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000127 // PDF specific methods.
128
halcanary8103a342016-03-08 15:10:16 -0800129 /** Create the resource dictionary for this device. */
130 sk_sp<SkPDFDict> makeResourceDict() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000131
vandebo@chromium.orgf0ec2662011-05-29 05:55:42 +0000132 /** Get the fonts used on this device.
133 */
halcanary4e4e8162015-02-25 08:59:48 -0800134 const SkTDArray<SkPDFFont*>& getFontResources() const;
vandebo@chromium.orgf0ec2662011-05-29 05:55:42 +0000135
wangxianzhuef6c50a2015-09-17 20:38:02 -0700136 /** Add our annotations (link to urls and destinations) to the supplied
137 * array.
138 * @param array Array to add annotations to.
139 */
140 void appendAnnotations(SkPDFArray* array) const;
141
epoger@google.comb58772f2013-03-08 09:09:10 +0000142 /** Add our named destinations to the supplied dictionary.
143 * @param dict Dictionary to add destinations to.
144 * @param page The PDF object representing the page for this device.
145 */
halcanary6d622702015-03-25 08:45:42 -0700146 void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
epoger@google.comb58772f2013-03-08 09:09:10 +0000147
halcanary8103a342016-03-08 15:10:16 -0800148 /** Returns a copy of the media box for this device. */
149 sk_sp<SkPDFArray> copyMediaBox() const;
halcanary51d04d32016-03-08 13:03:55 -0800150
halcanary8103a342016-03-08 15:10:16 -0800151 /** Returns a SkStream with the page contents.
halcanary51d04d32016-03-08 13:03:55 -0800152 */
mtklein5f939ab2016-03-16 10:28:35 -0700153 std::unique_ptr<SkStreamAsset> content() const;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000154
halcanary334fcbc2015-02-24 12:56:16 -0800155 /** Writes the page contents to the stream. */
halcanary4e4e8162015-02-25 08:59:48 -0800156 void writeContent(SkWStream*) const;
vandebo@chromium.org98594282011-07-25 22:34:12 +0000157
halcanary4e4e8162015-02-25 08:59:48 -0800158 const SkMatrix& initialTransform() const {
vandebo@chromium.org3509f052011-05-30 20:52:33 +0000159 return fInitialTransform;
160 }
vandebo@chromium.org61d26782011-05-24 23:02:07 +0000161
vandebo@chromium.org98594282011-07-25 22:34:12 +0000162 /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
163 * that shows on this device.
164 */
165 const SkPDFGlyphSetMap& getFontGlyphUsage() const {
166 return *(fFontGlyphUsage.get());
167 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000168
halcanary989da4a2016-03-21 14:33:17 -0700169 SkPDFCanon* getCanon() const;
halcanary26b5d152015-03-25 08:38:03 -0700170
halcanary2be7e012016-03-28 11:58:08 -0700171 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
172 // later being our representation of an object in the PDF file.
173 struct GraphicStateEntry {
174 GraphicStateEntry();
175
176 // Compare the fields we care about when setting up a new content entry.
177 bool compareInitialState(const GraphicStateEntry& b);
178
179 SkMatrix fMatrix;
180 // We can't do set operations on Paths, though PDF natively supports
181 // intersect. If the clip stack does anything other than intersect,
182 // we have to fall back to the region. Treat fClipStack as authoritative.
183 // See https://bugs.skia.org/221
184 SkClipStack fClipStack;
185 SkRegion fClipRegion;
186
187 // When emitting the content entry, we will ensure the graphic state
188 // is set to these values first.
189 SkColor fColor;
190 SkScalar fTextScaleX; // Zero means we don't care what the value is.
191 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
192 int fShaderIndex;
193 int fGraphicStateIndex;
194
195 // We may change the font (i.e. for Type1 support) within a
196 // ContentEntry. This is the one currently in effect, or nullptr if none.
197 SkPDFFont* fFont;
198 // In PDF, text size has no default value. It is only valid if fFont is
199 // not nullptr.
200 SkScalar fTextSize;
201 };
202
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000203protected:
mtklein36352bf2015-03-25 18:17:31 -0700204 const SkBitmap& onAccessBitmap() override {
reed89443ab2014-06-27 11:34:19 -0700205 return fLegacyBitmap;
206 }
207
reede8f30622016-03-23 18:59:25 -0700208 sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
edisonn@google.com73a7ea32013-11-11 20:55:15 +0000209
reedf70b5312016-03-04 16:36:20 -0800210 void drawAnnotation(const SkDraw&, const SkRect&, const char key[], SkData* value) override;
211
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000212private:
halcanary91fcb3e2016-03-04 13:53:22 -0800213 struct RectWithData {
214 SkRect rect;
halcanaryd7b28852016-03-07 12:39:14 -0800215 sk_sp<SkData> data;
halcanary91fcb3e2016-03-04 13:53:22 -0800216 RectWithData(const SkRect& rect, SkData* data)
217 : rect(rect), data(SkRef(data)) {}
halcanaryd7b28852016-03-07 12:39:14 -0800218 RectWithData(RectWithData&& other)
219 : rect(other.rect), data(std::move(other.data)) {}
220 RectWithData& operator=(RectWithData&& other) {
221 rect = other.rect;
222 data = std::move(other.data);
223 return *this;
224 }
halcanary91fcb3e2016-03-04 13:53:22 -0800225 };
226
227 struct NamedDestination {
halcanaryd7b28852016-03-07 12:39:14 -0800228 sk_sp<SkData> nameData;
halcanary91fcb3e2016-03-04 13:53:22 -0800229 SkPoint point;
230 NamedDestination(SkData* nameData, const SkPoint& point)
231 : nameData(SkRef(nameData)), point(point) {}
halcanaryd7b28852016-03-07 12:39:14 -0800232 NamedDestination(NamedDestination&& other)
233 : nameData(std::move(other.nameData)), point(other.point) {}
234 NamedDestination& operator=(NamedDestination&& other) {
235 nameData = std::move(other.nameData);
236 point = other.point;
237 return *this;
238 }
halcanary91fcb3e2016-03-04 13:53:22 -0800239 };
240
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000241 // TODO(vandebo): push most of SkPDFDevice's state into a core object in
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000242 // order to get the right access levels without using friend.
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000243 friend class ScopedContentEntry;
vandebo@chromium.orga0c7edb2011-05-09 07:58:08 +0000244
ctguil@chromium.org15261292011-04-29 17:54:16 +0000245 SkISize fPageSize;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000246 SkISize fContentSize;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000247 SkMatrix fInitialTransform;
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000248 SkClipStack fExistingClipStack;
249 SkRegion fExistingClipRegion;
wangxianzhuef6c50a2015-09-17 20:38:02 -0700250
halcanary91fcb3e2016-03-04 13:53:22 -0800251 SkTArray<RectWithData> fLinkToURLs;
252 SkTArray<RectWithData> fLinkToDestinations;
253 SkTArray<NamedDestination> fNamedDestinations;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000254
halcanarybe27a112015-04-01 13:31:19 -0700255 SkTDArray<SkPDFObject*> fGraphicStateResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000256 SkTDArray<SkPDFObject*> fXObjectResources;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000257 SkTDArray<SkPDFFont*> fFontResources;
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000258 SkTDArray<SkPDFObject*> fShaderResources;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000259
halcanary2be7e012016-03-28 11:58:08 -0700260 struct ContentEntry {
261 GraphicStateEntry fState;
262 SkDynamicMemoryWStream fContent;
263 };
264 SkSinglyLinkedList<ContentEntry> fContentEntries;
ctguil@chromium.org8dcf74f2011-07-12 21:56:27 +0000265
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000266 const SkClipStack* fClipStack;
267
vandebo@chromium.org98594282011-07-25 22:34:12 +0000268 // Glyph ids used for each font on this device.
halcanaryb8fb9932016-03-28 07:58:30 -0700269 std::unique_ptr<SkPDFGlyphSetMap> fFontGlyphUsage;
vandebo@chromium.org98594282011-07-25 22:34:12 +0000270
commit-bot@chromium.org8c294902013-10-21 17:14:37 +0000271 SkScalar fRasterDpi;
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000272
reed89443ab2014-06-27 11:34:19 -0700273 SkBitmap fLegacyBitmap;
274
halcanary989da4a2016-03-21 14:33:17 -0700275 SkPDFDocument* fDocument;
halcanarya1f1ee92015-02-20 06:17:26 -0800276 ////////////////////////////////////////////////////////////////////////////
277
278 SkPDFDevice(SkISize pageSize,
279 SkScalar rasterDpi,
halcanary989da4a2016-03-21 14:33:17 -0700280 SkPDFDocument* doc,
halcanarya1f1ee92015-02-20 06:17:26 -0800281 bool flip);
282
mtklein36352bf2015-03-25 18:17:31 -0700283 SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
bsalomon@google.come97f0852011-06-17 13:10:25 +0000284
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000285 void init();
vandebo@chromium.org98594282011-07-25 22:34:12 +0000286 void cleanUp(bool clearFontUsage);
reed@google.comfc641d02012-09-20 17:52:20 +0000287 SkPDFFormXObject* createFormXObjectFromDevice();
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000288
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000289 void drawFormXObjectWithMask(int xObjectIndex,
290 SkPDFFormXObject* mask,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000291 const SkClipStack* clipStack,
292 const SkRegion& clipRegion,
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000293 SkXfermode::Mode mode,
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000294 bool invertClip);
vandebo@chromium.org466f3d62011-05-18 23:06:29 +0000295
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000296 // If the paint or clip is such that we shouldn't draw anything, this
halcanary96fcdcc2015-08-27 07:41:13 -0700297 // returns nullptr and does not create a content entry.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000298 // setUpContentEntry and finishContentEntry can be used directly, but
vandebo@chromium.org13d14a92011-05-24 23:12:41 +0000299 // the preferred method is to use the ScopedContentEntry helper class.
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000300 ContentEntry* setUpContentEntry(const SkClipStack* clipStack,
301 const SkRegion& clipRegion,
302 const SkMatrix& matrix,
303 const SkPaint& paint,
304 bool hasText,
reed@google.comfc641d02012-09-20 17:52:20 +0000305 SkPDFFormXObject** dst);
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000306 void finishContentEntry(SkXfermode::Mode xfermode,
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000307 SkPDFFormXObject* dst,
308 SkPath* shape);
vandebo@chromium.org481aef62011-05-24 16:39:05 +0000309 bool isContentEmpty();
310
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000311 void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
312 const SkClipStack& clipStack,
313 const SkRegion& clipRegion,
314 const SkPaint& paint,
315 bool hasText,
316 GraphicStateEntry* entry);
halcanarybe27a112015-04-01 13:31:19 -0700317 int addGraphicStateResource(SkPDFObject* gs);
vandebo@chromium.org3b416212013-10-30 20:48:05 +0000318 int addXObjectResource(SkPDFObject* xObject);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000319
robertphillips8e0c1502015-07-07 10:28:43 -0700320 void updateFont(const SkPaint& paint, uint16_t glyphID, ContentEntry* contentEntry);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000321 int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000322
vandebo@chromium.orgb069c8c2011-05-24 17:19:38 +0000323 void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
halcanarya50151d2016-03-25 11:57:49 -0700324
325 void internalDrawImage(const SkMatrix& origMatrix,
halcanary7a14b312015-10-01 07:28:13 -0700326 const SkClipStack* clipStack,
halcanarya50151d2016-03-25 11:57:49 -0700327 const SkRegion& origClipRegion,
328 SkImageBitmap imageBitmap,
halcanary7a14b312015-10-01 07:28:13 -0700329 const SkPaint& paint);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000330
commit-bot@chromium.org92ffe7d2013-07-31 22:54:31 +0000331 bool handleInversePath(const SkDraw& d, const SkPath& origPath,
edisonn@google.coma9ebd162013-10-07 13:22:21 +0000332 const SkPaint& paint, bool pathIsMutable,
halcanary96fcdcc2015-08-27 07:41:13 -0700333 const SkMatrix* prePathMatrix = nullptr);
reedf70b5312016-03-04 16:36:20 -0800334 void handlePointAnnotation(const SkPoint&, const SkMatrix&, const char key[], SkData* value);
335 void handlePathAnnotation(const SkPath&, const SkDraw& d, const char key[], SkData* value);
vandebo@chromium.org238be8c2012-07-13 20:06:02 +0000336
reed89443ab2014-06-27 11:34:19 -0700337 typedef SkBaseDevice INHERITED;
commit-bot@chromium.org5e009892013-10-14 13:42:12 +0000338
339 // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
340 // an SkPDFDevice
341 //friend class SkDocument_PDF;
342 //friend class SkPDFImageShader;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000343};
344
345#endif