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