blob: 0197b5a6d33b5fa15bcaf5f920dcfc7debbdda88 [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 "SkBitmap.h"
#include "SkCanvas.h"
#include "SkClipStack.h"
#include "SkClipStackDevice.h"
#include "SkData.h"
#include "SkPaint.h"
#include "SkRect.h"
#include "SkRefCnt.h"
#include "SkSinglyLinkedList.h"
#include "SkStream.h"
#include "SkTDArray.h"
#include "SkTextBlobPriv.h"
#include "SkKeyedImage.h"
class SkGlyphRunList;
class SkKeyedImage;
class SkPath;
class SkPDFArray;
class SkPDFCanon;
class SkPDFDevice;
class SkPDFDocument;
class SkPDFDict;
class SkPDFFont;
class SkPDFObject;
class SkPDFStream;
class SkRRect;
/**
* \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 repsonsible for
* de-duplicating across pages (via the SkPDFCanon) and
* for early serializing of large immutable objects, such
* as images (via SkPDFDocument::serialize()).
*/
SkPDFDevice(SkISize pageSize, SkPDFDocument* document);
/**
* Apply a scale-and-translate transform to move the origin from the
* bottom left (PDF default) to the top left (Skia default).
*/
void setFlip();
sk_sp<SkPDFDevice> makeCongruentDevice() {
return sk_make_sp<SkPDFDevice>(fPageSize, 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 drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint&) override;
void drawSprite(const SkBitmap& bitmap, int x, int y,
const SkPaint& paint) override;
void drawImage(const SkImage*,
SkScalar x,
SkScalar y,
const SkPaint&) override;
void drawImageRect(const SkImage*,
const SkRect* src,
const SkRect& dst,
const SkPaint&,
SkCanvas::SrcRectConstraint) override;
void drawPosText(const void* text, size_t len,
const SkScalar pos[], int scalarsPerPos,
const SkPoint& offset, const SkPaint&) override { SkASSERT(false); }
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. */
sk_sp<SkPDFDict> makeResourceDict() const;
/** Add our annotations (link to urls and destinations) to the supplied
* array.
* @param array Array to add annotations to.
*/
void appendAnnotations(SkPDFArray* array) const;
/** 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, SkPDFObject* page) const;
/** Returns a copy of the media box for this device. */
sk_sp<SkPDFArray> copyMediaBox() const;
/** Returns a SkStream with the page contents.
*/
std::unique_ptr<SkStreamAsset> content() const;
SkPDFCanon* getCanon() const;
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 {
GraphicStateEntry();
// Compare the fields we care about when setting up a new content entry.
bool compareInitialState(const GraphicStateEntry& b);
SkMatrix fMatrix;
// We can't do set operations on Paths, though PDF natively supports
// intersect. If the clip stack does anything other than intersect,
// we have to fall back to the region. Treat fClipStack as authoritative.
// See https://bugs.skia.org/221
SkClipStack fClipStack;
// When emitting the content entry, we will ensure the graphic state
// is set to these values first.
SkColor fColor;
SkScalar fTextScaleX; // Zero means we don't care what the value is.
SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
int fShaderIndex;
int fGraphicStateIndex;
};
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;
SkISize fPageSize;
SkMatrix fInitialTransform;
SkClipStack fExistingClipStack;
SkTArray<RectWithData> fLinkToURLs;
SkTArray<RectWithData> fLinkToDestinations;
SkTArray<NamedDestination> fNamedDestinations;
SkTDArray<SkPDFObject*> fGraphicStateResources;
SkTDArray<SkPDFObject*> fXObjectResources;
SkTDArray<SkPDFFont*> fFontResources;
SkTDArray<SkPDFObject*> fShaderResources;
struct ContentEntry {
GraphicStateEntry fState;
SkDynamicMemoryWStream fContent;
};
SkSinglyLinkedList<ContentEntry> fContentEntries;
SkPDFDocument* fDocument;
////////////////////////////////////////////////////////////////////////////
SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
void init();
void cleanUp();
// Set alpha to true if making a transparency group form x-objects.
sk_sp<SkPDFObject> makeFormXObjectFromDevice(bool alpha = false);
void drawFormXObjectWithMask(int xObjectIndex,
sk_sp<SkPDFObject> mask,
const SkClipStack& clipStack,
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.
ContentEntry* setUpContentEntry(const SkClipStack& clipStack,
const SkMatrix& matrix,
const SkPaint& paint,
bool hasText,
sk_sp<SkPDFObject>* dst);
void finishContentEntry(SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
bool isContentEmpty();
void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
const SkClipStack& clipStack,
const SkPaint& paint,
bool hasText,
GraphicStateEntry* entry);
int addGraphicStateResource(SkPDFObject* gs);
int addXObjectResource(SkPDFObject* xObject);
int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
void internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset);
void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
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*);
bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }
typedef SkClipStackDevice INHERITED;
};
#endif