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