xref: /aosp_15_r20/external/skia/src/pdf/SkPDFDevice.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkPDFDevice_DEFINED
9 #define SkPDFDevice_DEFINED
10 
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkSamplingOptions.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkStream.h"
17 #include "src/core/SkClipStack.h"
18 #include "src/core/SkClipStackDevice.h"
19 #include "src/core/SkTHash.h"
20 #include "src/pdf/SkKeyedImage.h"
21 #include "src/pdf/SkPDFGraphicStackState.h"
22 #include "src/pdf/SkPDFTag.h"
23 #include "src/pdf/SkPDFTypes.h"
24 
25 #include <cstddef>
26 #include <memory>
27 
28 class SkBitmap;
29 class SkBlender;
30 class SkData;
31 class SkDevice;
32 class SkImage;
33 class SkMesh;
34 class SkPDFDocument;
35 class SkPaint;
36 class SkPath;
37 class SkRRect;
38 class SkSpecialImage;
39 class SkSurface;
40 class SkSurfaceProps;
41 class SkVertices;
42 enum class SkBlendMode;
43 struct SkIRect;
44 struct SkISize;
45 struct SkImageInfo;
46 struct SkPoint;
47 struct SkRect;
48 
49 namespace sktext {
50 class GlyphRun;
51 class GlyphRunList;
52 }
53 
54 /**
55  *  \class SkPDFDevice
56  *
57  *  An SkPDFDevice is the drawing context for a page or layer of PDF
58  *  content.
59  */
60 class SkPDFDevice final : public SkClipStackDevice {
61 public:
62     /**
63      *  @param pageSize Page size in point units.
64      *         1 point == 127/360 mm == 1/72 inch
65      *  @param document  A non-null pointer back to the
66      *         PDFDocument object.  The document is responsible for
67      *         de-duplicating across pages (via the SkPDFDocument) and
68      *         for early serializing of large immutable objects, such
69      *         as images (via SkPDFDocument::serialize()).
70      *  @param initialTransform Transform to be applied to the entire page.
71      */
72     SkPDFDevice(SkISize pageSize, SkPDFDocument* document,
73                 const SkMatrix& initialTransform = SkMatrix::I());
74 
makeCongruentDevice()75     sk_sp<SkPDFDevice> makeCongruentDevice() {
76         return sk_make_sp<SkPDFDevice>(this->size(), fDocument);
77     }
78 
79     ~SkPDFDevice() override;
80 
81     /**
82      *  These are called inside the per-device-layer loop for each draw call.
83      *  When these are called, we have already applied any saveLayer
84      *  operations, and are handling any looping from the paint.
85      */
86     void drawPaint(const SkPaint& paint) override;
87     void drawPoints(SkCanvas::PointMode mode,
88                     size_t count, const SkPoint[],
89                     const SkPaint& paint) override;
90     void drawRect(const SkRect& r, const SkPaint& paint) override;
91     void drawOval(const SkRect& oval, const SkPaint& paint) override;
92     void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
93     void drawPath(const SkPath& origpath, const SkPaint& paint, bool pathIsMutable) override;
94 
95     void drawImageRect(const SkImage*,
96                        const SkRect* src,
97                        const SkRect& dst,
98                        const SkSamplingOptions&,
99                        const SkPaint&,
100                        SkCanvas::SrcRectConstraint) override;
101 
102     void drawVertices(const SkVertices*, sk_sp<SkBlender>, const SkPaint&, bool) override;
103     void drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) override;
104 
105     void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
106 
107     void drawDevice(SkDevice*, const SkSamplingOptions&, const SkPaint&) override;
108     void drawSpecial(SkSpecialImage*, const SkMatrix&, const SkSamplingOptions&,
109                      const SkPaint&, SkCanvas::SrcRectConstraint) override;
110 
111     sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
112     sk_sp<SkDevice> createDevice(const CreateInfo&, const SkPaint*) override;
113 
114     // PDF specific methods.
115     void drawSprite(const SkBitmap& bitmap, int x, int y,
116                     const SkPaint& paint);
117 
118     /** Create the resource dictionary for this device. Destructive. */
119     std::unique_ptr<SkPDFDict> makeResourceDict();
120 
121     /** Returns a SkStream with the page contents.
122      */
123     std::unique_ptr<SkStreamAsset> content();
124 
initialTransform()125     const SkMatrix& initialTransform() const { return fInitialTransform; }
126 
127 protected:
128     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
129     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
130 
131 private:
132     // TODO(vandebo): push most of SkPDFDevice's state into a core object in
133     // order to get the right access levels without using friend.
134     friend class ScopedContentEntry;
135 
136     SkMatrix fInitialTransform;
137 
138     skia_private::THashSet<SkPDFIndirectReference> fGraphicStateResources;
139     skia_private::THashSet<SkPDFIndirectReference> fXObjectResources;
140     skia_private::THashSet<SkPDFIndirectReference> fShaderResources;
141     skia_private::THashSet<SkPDFIndirectReference> fFontResources;
142 
143     class MarkedContentManager {
144     public:
145         MarkedContentManager(SkPDFDocument* document, SkDynamicMemoryWStream* out);
146         ~MarkedContentManager();
147 
148         // Sets the current element identifier. Associate future draws with the structure element
149         // with the given element identifier. Element identifier 0 is reserved to mean no structure
150         // element.
151         void setNextMarksElemId(int nextMarksElemId);
152 
153         // The current element identifier.
154         int elemId() const;
155 
156         // Starts a marked-content sequence for a content item for the structure element with the
157         // current element identifier. If there is an active marked-content sequence associated with
158         // a different element identifier the active marked-content sequence will first be closed.
159         // If there is no structure element with the current element identifier then the
160         // marked-content sequence will not be started.
161         void beginMark();
162 
163         // Tests if there is an active marked-content sequence.
164         bool hasActiveMark() const;
165 
166         // Accumulates an upper left location for the active mark. The point is in PDF page space
167         // and so is y-up. Only use if this.hasActiveMark()
168         void accumulate(const SkPoint& p);
169 
170         // Tests if this marked content manager made any marks.
madeMarks()171         bool madeMarks() const { return fMadeMarks; }
172 
173     private:
174         SkPDFDocument* fDoc;
175         SkDynamicMemoryWStream* fOut;
176         SkPDFStructTree::Mark fCurrentlyActiveMark;
177         int fNextMarksElemId;
178         bool fMadeMarks;
179     } fMarkManager;
180 
181     SkDynamicMemoryWStream fContent;
182     SkDynamicMemoryWStream fContentBuffer;
183     bool fNeedsExtraSave = false;
184     SkPDFGraphicStackState fActiveStackState;
185     SkPDFDocument* fDocument;
186 
187     ////////////////////////////////////////////////////////////////////////////
188 
189     void onDrawGlyphRunList(SkCanvas*, const sktext::GlyphRunList&, const SkPaint& paint) override;
190 
191     // Set alpha to true if making a transparency group form x-objects.
192     SkPDFIndirectReference makeFormXObjectFromDevice(bool alpha = false);
193     SkPDFIndirectReference makeFormXObjectFromDevice(SkIRect bbox, bool alpha = false);
194 
195     void drawFormXObjectWithMask(SkPDFIndirectReference xObject,
196                                  SkPDFIndirectReference sMask,
197                                  SkBlendMode,
198                                  bool invertClip);
199 
200     // If the paint or clip is such that we shouldn't draw anything, this
201     // returns nullptr and does not create a content entry.
202     // setUpContentEntry and finishContentEntry can be used directly, but
203     // the preferred method is to use the ScopedContentEntry helper class.
204     SkDynamicMemoryWStream* setUpContentEntry(const SkClipStack* clipStack,
205                                               const SkMatrix& matrix,
206                                               const SkPaint& paint,
207                                               SkScalar,
208                                               SkPDFIndirectReference* dst);
209     void finishContentEntry(const SkClipStack*, SkBlendMode, SkPDFIndirectReference, SkPath*);
210     bool isContentEmpty();
211 
212     void internalDrawGlyphRun(
213             const sktext::GlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
214     void drawGlyphRunAsPath(
215             const sktext::GlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
216 
217     void internalDrawImageRect(SkKeyedImage,
218                                const SkRect* src,
219                                const SkRect& dst,
220                                const SkSamplingOptions&,
221                                const SkPaint&,
222                                const SkMatrix& canvasTransformationMatrix);
223 
224     void internalDrawPath(const SkClipStack&,
225                           const SkMatrix&,
226                           const SkPath&,
227                           const SkPaint&,
228                           bool pathIsMutable);
229 
230     void internalDrawPathWithFilter(const SkClipStack& clipStack,
231                                     const SkMatrix& ctm,
232                                     const SkPath& origPath,
233                                     const SkPaint& paint);
234 
235     bool handleInversePath(const SkPath& origPath, const SkPaint& paint, bool pathIsMutable);
236 
237     void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
238     void setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream*);
239     void drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream*, SkPath* shape);
240 
hasEmptyClip()241     bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }
242 
243     void reset();
244 };
245 
246 #endif
247