liyujie
2025-08-28 b3810562527858a3b3d98ffa6e9c9c5b0f4a9a8e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
 
#ifndef SkPDFDevice_DEFINED
#define SkPDFDevice_DEFINED
 
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkClipStack.h"
#include "SkClipStackDevice.h"
#include "SkData.h"
#include "SkKeyedImage.h"
#include "SkPDFTypes.h"
#include "SkPaint.h"
#include "SkRect.h"
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTHash.h"
#include "SkTextBlobPriv.h"
 
#include <vector>
 
class SkGlyphRunList;
class SkKeyedImage;
class SkPDFArray;
class SkPDFDevice;
class SkPDFDict;
class SkPDFDocument;
class SkPDFFont;
class SkPDFObject;
class SkPath;
class SkRRect;
struct SkPDFIndirectReference;
 
/**
 *  \class SkPDFDevice
 *
 *  An SkPDFDevice is the drawing context for a page or layer of PDF
 *  content.
 */
class SkPDFDevice final : public SkClipStackDevice {
public:
    /**
     *  @param pageSize Page size in point units.
     *         1 point == 127/360 mm == 1/72 inch
     *  @param document  A non-null pointer back to the
     *         PDFDocument object.  The document is responsible for
     *         de-duplicating across pages (via the SkPDFDocument) and
     *         for early serializing of large immutable objects, such
     *         as images (via SkPDFDocument::serialize()).
     *  @param initialTransform Transform to be applied to the entire page.
     */
    SkPDFDevice(SkISize pageSize, SkPDFDocument* document,
                const SkMatrix& initialTransform = SkMatrix::I());
 
    sk_sp<SkPDFDevice> makeCongruentDevice() {
        return sk_make_sp<SkPDFDevice>(this->size(), fDocument);
    }
 
    ~SkPDFDevice() override;
 
    /**
     *  These are called inside the per-device-layer loop for each draw call.
     *  When these are called, we have already applied any saveLayer
     *  operations, and are handling any looping from the paint.
     */
    void drawPaint(const SkPaint& paint) override;
    void drawPoints(SkCanvas::PointMode mode,
                    size_t count, const SkPoint[],
                    const SkPaint& paint) override;
    void drawRect(const SkRect& r, const SkPaint& paint) override;
    void drawOval(const SkRect& oval, const SkPaint& paint) override;
    void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
    void drawPath(const SkPath& origpath, const SkPaint& paint, bool pathIsMutable) override;
    void drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
                        const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override;
    void drawSprite(const SkBitmap& bitmap, int x, int y,
                    const SkPaint& paint) override;
 
    void drawImageRect(const SkImage*,
                       const SkRect* src,
                       const SkRect& dst,
                       const SkPaint&,
                       SkCanvas::SrcRectConstraint) override;
    void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
    void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
                      const SkPaint&) override;
    void drawDevice(SkBaseDevice*, int x, int y,
                    const SkPaint&) override;
 
    // PDF specific methods.
 
    /** Create the resource dictionary for this device. Destructive. */
    std::unique_ptr<SkPDFDict> makeResourceDict();
 
    /** return annotations (link to urls and destinations) or nulltpr */
    std::unique_ptr<SkPDFArray> getAnnotations();
 
    /** Add our named destinations to the supplied dictionary.
     *  @param dict  Dictionary to add destinations to.
     *  @param page  The PDF object representing the page for this device.
     */
    void appendDestinations(SkPDFDict* dict, SkPDFIndirectReference page) const;
 
    /** Returns a SkStream with the page contents.
     */
    std::unique_ptr<SkStreamAsset> content();
 
    SkISize size() const { return this->imageInfo().dimensions(); }
    SkIRect bounds() const { return this->imageInfo().bounds(); }
 
    // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
    // later being our representation of an object in the PDF file.
    struct GraphicStateEntry {
        SkMatrix fMatrix = SkMatrix::I();
        uint32_t fClipStackGenID = SkClipStack::kWideOpenGenID;
        SkColor4f fColor = {0, 0, 0, 1};
        SkScalar fTextScaleX = 1;  // Zero means we don't care what the value is.
        SkPaint::Style fTextFill = SkPaint::kFill_Style;  // Only if TextScaleX is non-zero.
        int fShaderIndex = -1;
        int fGraphicStateIndex = -1;
    };
 
    void DrawGlyphRunAsPath(SkPDFDevice* dev, const SkGlyphRun& glyphRun, SkPoint offset);
 
protected:
    sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
 
    void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
 
    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
                     SkImage*, const SkMatrix&) override;
    sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
    sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
    sk_sp<SkSpecialImage> snapSpecial() override;
    SkImageFilterCache* getImageFilterCache() override;
 
private:
    struct RectWithData {
        SkRect rect;
        sk_sp<SkData> data;
    };
 
    struct NamedDestination {
        sk_sp<SkData> nameData;
        SkPoint point;
    };
 
    // TODO(vandebo): push most of SkPDFDevice's state into a core object in
    // order to get the right access levels without using friend.
    friend class ScopedContentEntry;
 
    SkMatrix fInitialTransform;
 
    std::vector<RectWithData> fLinkToURLs;
    std::vector<RectWithData> fLinkToDestinations;
    std::vector<NamedDestination> fNamedDestinations;
 
    SkTHashSet<SkPDFIndirectReference> fGraphicStateResources;
    SkTHashSet<SkPDFIndirectReference> fXObjectResources;
    SkTHashSet<SkPDFIndirectReference> fShaderResources;
    SkTHashSet<SkPDFIndirectReference> fFontResources;
    int fNodeId;
 
    SkDynamicMemoryWStream fContent;
    SkDynamicMemoryWStream fContentBuffer;
    bool fNeedsExtraSave = false;
    struct GraphicStackState {
        GraphicStackState(SkDynamicMemoryWStream* s = nullptr);
        void updateClip(const SkClipStack* clipStack, const SkIRect& bounds);
        void updateMatrix(const SkMatrix& matrix);
        void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state);
        void push();
        void pop();
        void drainStack();
        SkPDFDevice::GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
        // Must use stack for matrix, and for clip, plus one for no matrix or clip.
        static constexpr int kMaxStackDepth = 2;
        SkPDFDevice::GraphicStateEntry fEntries[kMaxStackDepth + 1];
        int fStackDepth = 0;
        SkDynamicMemoryWStream* fContentStream;
    };
    GraphicStackState fActiveStackState;
    SkPDFDocument* fDocument;
 
    ////////////////////////////////////////////////////////////////////////////
 
    SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
 
    // Set alpha to true if making a transparency group form x-objects.
    SkPDFIndirectReference makeFormXObjectFromDevice(bool alpha = false);
 
    void drawFormXObjectWithMask(SkPDFIndirectReference xObject,
                                 SkPDFIndirectReference sMask,
                                 SkBlendMode,
                                 bool invertClip);
 
    // If the paint or clip is such that we shouldn't draw anything, this
    // returns nullptr and does not create a content entry.
    // setUpContentEntry and finishContentEntry can be used directly, but
    // the preferred method is to use the ScopedContentEntry helper class.
    SkDynamicMemoryWStream* setUpContentEntry(const SkClipStack* clipStack,
                                              const SkMatrix& matrix,
                                              const SkPaint& paint,
                                              SkScalar,
                                              SkPDFIndirectReference* dst);
    void finishContentEntry(const SkClipStack*, SkBlendMode, SkPDFIndirectReference, SkPath*);
    bool isContentEmpty();
 
    void internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
    void drawGlyphRunAsPath(const SkGlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint);
 
    void internalDrawImageRect(SkKeyedImage,
                               const SkRect* src,
                               const SkRect& dst,
                               const SkPaint&,
                               const SkMatrix& canvasTransformationMatrix);
 
    void internalDrawPath(const SkClipStack&,
                          const SkMatrix&,
                          const SkPath&,
                          const SkPaint&,
                          bool pathIsMutable);
 
    void internalDrawPathWithFilter(const SkClipStack& clipStack,
                                    const SkMatrix& ctm,
                                    const SkPath& origPath,
                                    const SkPaint& paint);
 
    bool handleInversePath(const SkPath& origPath, const SkPaint& paint, bool pathIsMutable);
 
    void addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice, SkDynamicMemoryWStream*);
    void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
    void setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream*);
    void drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream*);
 
    bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }
 
    void reset();
 
    typedef SkClipStackDevice INHERITED;
};
 
#endif