| /* |
| * 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 |