blob: 22711456f2c89acbb83f59edd8dcf3ebe144d5b6 [file] [log] [blame]
/*
* 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 "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkStream.h"
#include "include/private/SkTHash.h"
#include "src/core/SkClipStack.h"
#include "src/core/SkClipStackDevice.h"
#include "src/core/SkTextBlobPriv.h"
#include "src/pdf/SkKeyedImage.h"
#include "src/pdf/SkPDFGraphicStackState.h"
#include "src/pdf/SkPDFTypes.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 drawImageRect(const SkImage*,
const SkRect* src,
const SkRect& dst,
const SkSamplingOptions&,
const SkPaint&,
SkCanvas::SrcRectConstraint) override;
void onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint) override;
void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
// PDF specific methods.
void drawSprite(const SkBitmap& bitmap, int x, int y,
const SkPaint& paint);
/** Create the resource dictionary for this device. Destructive. */
std::unique_ptr<SkPDFDict> makeResourceDict();
/** 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(); }
void DrawGlyphRunAsPath(SkPDFDevice* dev, const SkGlyphRun& glyphRun, SkPoint offset);
const SkMatrix& initialTransform() const { return fInitialTransform; }
protected:
sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
void drawDevice(SkBaseDevice*, const SkSamplingOptions&, const SkPaint&) override;
void drawSpecial(SkSpecialImage*, const SkMatrix&, const SkSamplingOptions&,
const SkPaint&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
SkImageFilterCache* getImageFilterCache() override;
private:
// 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;
SkTHashSet<SkPDFIndirectReference> fGraphicStateResources;
SkTHashSet<SkPDFIndirectReference> fXObjectResources;
SkTHashSet<SkPDFIndirectReference> fShaderResources;
SkTHashSet<SkPDFIndirectReference> fFontResources;
int fNodeId;
SkDynamicMemoryWStream fContent;
SkDynamicMemoryWStream fContentBuffer;
bool fNeedsExtraSave = false;
SkPDFGraphicStackState 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);
SkPDFIndirectReference makeFormXObjectFromDevice(SkIRect bbox, 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 SkSamplingOptions&,
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 clearMaskOnGraphicState(SkDynamicMemoryWStream*);
void setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream*);
void drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream*);
bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }
void reset();
using INHERITED = SkClipStackDevice;
};
#endif