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 "SkPaint.h" 17 #include "SkRect.h" 18 #include "SkRefCnt.h" 19 #include "SkSinglyLinkedList.h" 20 #include "SkStream.h" 21 #include "SkTDArray.h" 22 #include "SkTextBlob.h" 23 24 class SkImageSubset; 25 class SkPath; 26 class SkPDFArray; 27 class SkPDFCanon; 28 class SkPDFDevice; 29 class SkPDFDocument; 30 class SkPDFDict; 31 class SkPDFFont; 32 class SkPDFObject; 33 class SkPDFStream; 34 class SkRRect; 35 36 /** \class SkPDFDevice 37 38 The drawing context for the PDF backend. 39 */ 40 class SkPDFDevice final : public SkClipStackDevice { 41 public: 42 /** Create a PDF drawing context. SkPDFDevice applies a 43 * scale-and-translate transform to move the origin from the 44 * bottom left (PDF default) to the top left (Skia default). 45 * @param pageSize Page size in point units. 46 * 1 point == 127/360 mm == 1/72 inch 47 * @param rasterDpi the DPI at which features without native PDF 48 * support will be rasterized (e.g. draw image with 49 * perspective, draw text with perspective, ...). A 50 * larger DPI would create a PDF that reflects the 51 * original intent with better fidelity, but it can make 52 * for larger PDF files too, which would use more memory 53 * while rendering, and it would be slower to be processed 54 * or sent online or to printer. A good choice is 55 * SK_ScalarDefaultRasterDPI(72.0f). 56 * @param SkPDFDocument. A non-null pointer back to the 57 * document. The document is repsonsible for 58 * de-duplicating across pages (via the SkPDFCanon) and 59 * for early serializing of large immutable objects, such 60 * as images (via SkPDFDocument::serialize()). 61 */ Create(SkISize pageSize,SkScalar rasterDpi,SkPDFDocument * doc)62 static SkPDFDevice* Create(SkISize pageSize, 63 SkScalar rasterDpi, 64 SkPDFDocument* doc) { 65 return new SkPDFDevice(pageSize, rasterDpi, doc, true); 66 } 67 68 /** Create a PDF drawing context without fipping the y-axis. */ CreateUnflipped(SkISize pageSize,SkScalar rasterDpi,SkPDFDocument * doc)69 static SkPDFDevice* CreateUnflipped(SkISize pageSize, 70 SkScalar rasterDpi, 71 SkPDFDocument* doc) { 72 return new SkPDFDevice(pageSize, rasterDpi, doc, false); 73 } 74 75 ~SkPDFDevice() override; 76 77 /** These are called inside the per-device-layer loop for each draw call. 78 When these are called, we have already applied any saveLayer operations, 79 and are handling any looping from the paint, and any effects from the 80 DrawFilter. 81 */ 82 void drawPaint(const SkPaint& paint) override; 83 void drawPoints(SkCanvas::PointMode mode, 84 size_t count, const SkPoint[], 85 const SkPaint& paint) override; 86 void drawRect(const SkRect& r, const SkPaint& paint) override; 87 void drawOval(const SkRect& oval, const SkPaint& paint) override; 88 void drawRRect(const SkRRect& rr, const SkPaint& paint) override; 89 void drawPath(const SkPath& origpath, 90 const SkPaint& paint, const SkMatrix* prePathMatrix, 91 bool pathIsMutable) override; 92 void drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, 93 const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint) override; 94 void drawBitmap(const SkBitmap& bitmap, 95 const SkMatrix& matrix, const SkPaint&) override; 96 void drawSprite(const SkBitmap& bitmap, int x, int y, 97 const SkPaint& paint) override; 98 void drawImage(const SkImage*, 99 SkScalar x, 100 SkScalar y, 101 const SkPaint&) override; 102 void drawImageRect(const SkImage*, 103 const SkRect* src, 104 const SkRect& dst, 105 const SkPaint&, 106 SkCanvas::SrcRectConstraint) override; 107 void drawText(const void* text, size_t len, 108 SkScalar x, SkScalar y, const SkPaint&) override; 109 void drawPosText(const void* text, size_t len, 110 const SkScalar pos[], int scalarsPerPos, 111 const SkPoint& offset, const SkPaint&) override; 112 void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, 113 const SkPaint &, SkDrawFilter*) override; 114 void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override; 115 void drawDevice(SkBaseDevice*, int x, int y, 116 const SkPaint&) override; 117 118 // PDF specific methods. 119 120 /** Create the resource dictionary for this device. */ 121 sk_sp<SkPDFDict> makeResourceDict() const; 122 123 /** Add our annotations (link to urls and destinations) to the supplied 124 * array. 125 * @param array Array to add annotations to. 126 */ 127 void appendAnnotations(SkPDFArray* array) const; 128 129 /** Add our named destinations to the supplied dictionary. 130 * @param dict Dictionary to add destinations to. 131 * @param page The PDF object representing the page for this device. 132 */ 133 void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const; 134 135 /** Returns a copy of the media box for this device. */ 136 sk_sp<SkPDFArray> copyMediaBox() const; 137 138 /** Returns a SkStream with the page contents. 139 */ 140 std::unique_ptr<SkStreamAsset> content() const; 141 142 SkPDFCanon* getCanon() const; 143 144 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the 145 // later being our representation of an object in the PDF file. 146 struct GraphicStateEntry { 147 GraphicStateEntry(); 148 149 // Compare the fields we care about when setting up a new content entry. 150 bool compareInitialState(const GraphicStateEntry& b); 151 152 SkMatrix fMatrix; 153 // We can't do set operations on Paths, though PDF natively supports 154 // intersect. If the clip stack does anything other than intersect, 155 // we have to fall back to the region. Treat fClipStack as authoritative. 156 // See https://bugs.skia.org/221 157 SkClipStack fClipStack; 158 159 // When emitting the content entry, we will ensure the graphic state 160 // is set to these values first. 161 SkColor fColor; 162 SkScalar fTextScaleX; // Zero means we don't care what the value is. 163 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero. 164 int fShaderIndex; 165 int fGraphicStateIndex; 166 }; 167 168 protected: 169 sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override; 170 171 void drawAnnotation(const SkRect&, const char key[], SkData* value) override; 172 173 void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override; 174 sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override; 175 sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override; 176 sk_sp<SkSpecialImage> snapSpecial() override; 177 SkImageFilterCache* getImageFilterCache() override; 178 179 private: 180 struct RectWithData { 181 SkRect rect; 182 sk_sp<SkData> data; RectWithDataRectWithData183 RectWithData(const SkRect& rect, SkData* data) 184 : rect(rect), data(SkRef(data)) {} 185 RectWithData(RectWithData&&) = default; 186 RectWithData& operator=(RectWithData&& other) = default; 187 }; 188 189 struct NamedDestination { 190 sk_sp<SkData> nameData; 191 SkPoint point; NamedDestinationNamedDestination192 NamedDestination(SkData* nameData, const SkPoint& point) 193 : nameData(SkRef(nameData)), point(point) {} 194 NamedDestination(NamedDestination&&) = default; 195 NamedDestination& operator=(NamedDestination&&) = default; 196 }; 197 198 // TODO(vandebo): push most of SkPDFDevice's state into a core object in 199 // order to get the right access levels without using friend. 200 friend class ScopedContentEntry; 201 202 SkISize fPageSize; 203 SkMatrix fInitialTransform; 204 SkClipStack fExistingClipStack; 205 206 SkTArray<RectWithData> fLinkToURLs; 207 SkTArray<RectWithData> fLinkToDestinations; 208 SkTArray<NamedDestination> fNamedDestinations; 209 210 SkTDArray<SkPDFObject*> fGraphicStateResources; 211 SkTDArray<SkPDFObject*> fXObjectResources; 212 SkTDArray<SkPDFFont*> fFontResources; 213 SkTDArray<SkPDFObject*> fShaderResources; 214 215 struct ContentEntry { 216 GraphicStateEntry fState; 217 SkDynamicMemoryWStream fContent; 218 }; 219 SkSinglyLinkedList<ContentEntry> fContentEntries; 220 221 SkScalar fRasterDpi; 222 223 SkPDFDocument* fDocument; 224 //////////////////////////////////////////////////////////////////////////// 225 226 SkPDFDevice(SkISize pageSize, 227 SkScalar rasterDpi, 228 SkPDFDocument* doc, 229 bool flip); 230 231 SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override; 232 233 void init(); 234 void cleanUp(); 235 sk_sp<SkPDFObject> makeFormXObjectFromDevice(); 236 237 void drawFormXObjectWithMask(int xObjectIndex, 238 sk_sp<SkPDFObject> mask, 239 const SkClipStack& clipStack, 240 SkBlendMode, 241 bool invertClip); 242 243 // If the paint or clip is such that we shouldn't draw anything, this 244 // returns nullptr and does not create a content entry. 245 // setUpContentEntry and finishContentEntry can be used directly, but 246 // the preferred method is to use the ScopedContentEntry helper class. 247 ContentEntry* setUpContentEntry(const SkClipStack& clipStack, 248 const SkMatrix& matrix, 249 const SkPaint& paint, 250 bool hasText, 251 sk_sp<SkPDFObject>* dst); 252 void finishContentEntry(SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape); 253 bool isContentEmpty(); 254 255 void populateGraphicStateEntryFromPaint(const SkMatrix& matrix, 256 const SkClipStack& clipStack, 257 const SkPaint& paint, 258 bool hasText, 259 GraphicStateEntry* entry); 260 int addGraphicStateResource(SkPDFObject* gs); 261 int addXObjectResource(SkPDFObject* xObject); 262 263 int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID); 264 265 266 void internalDrawText( const void*, size_t, const SkScalar pos[], 267 SkTextBlob::GlyphPositioning, SkPoint, const SkPaint&, 268 const uint32_t*, uint32_t, const char*); 269 270 void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry); 271 272 void internalDrawImage(const SkMatrix& origMatrix, 273 const SkClipStack& clipStack, 274 SkImageSubset imageSubset, 275 const SkPaint& paint); 276 277 void internalDrawPath(const SkClipStack&, 278 const SkMatrix&, 279 const SkPath&, 280 const SkPaint&, 281 const SkMatrix* prePathMatrix, 282 bool pathIsMutable); 283 284 bool handleInversePath(const SkPath& origPath, 285 const SkPaint& paint, bool pathIsMutable, 286 const SkMatrix* prePathMatrix = nullptr); 287 288 typedef SkClipStackDevice INHERITED; 289 290 // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create 291 // an SkPDFDevice 292 //friend class SkDocument_PDF; 293 //friend class SkPDFImageShader; 294 }; 295 296 #endif 297