michael@0: michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #ifndef SkPDFDevice_DEFINED michael@0: #define SkPDFDevice_DEFINED michael@0: michael@0: #include "SkBitmapDevice.h" michael@0: #include "SkBitmap.h" michael@0: #include "SkCanvas.h" michael@0: #include "SkPaint.h" michael@0: #include "SkPath.h" michael@0: #include "SkPicture.h" michael@0: #include "SkRect.h" michael@0: #include "SkRefCnt.h" michael@0: #include "SkStream.h" michael@0: #include "SkTDArray.h" michael@0: #include "SkTemplates.h" michael@0: michael@0: class SkPDFArray; michael@0: class SkPDFDevice; michael@0: class SkPDFDict; michael@0: class SkPDFFont; michael@0: class SkPDFFormXObject; michael@0: class SkPDFGlyphSetMap; michael@0: class SkPDFGraphicState; michael@0: class SkPDFObject; michael@0: class SkPDFResourceDict; michael@0: class SkPDFShader; michael@0: class SkPDFStream; michael@0: class SkRRect; michael@0: template class SkTSet; michael@0: michael@0: // Private classes. michael@0: struct ContentEntry; michael@0: struct GraphicStateEntry; michael@0: struct NamedDestination; michael@0: michael@0: /** \class SkPDFDevice michael@0: michael@0: The drawing context for the PDF backend. michael@0: */ michael@0: class SkPDFDevice : public SkBitmapDevice { michael@0: public: michael@0: /** Create a PDF drawing context with the given width and height. michael@0: * 72 points/in means letter paper is 612x792. michael@0: * @param pageSize Page size in points. michael@0: * @param contentSize The content size of the page in points. This will be michael@0: * combined with the initial transform to determine the drawing area michael@0: * (as reported by the width and height methods). Anything outside michael@0: * of the drawing area will be clipped. michael@0: * @param initialTransform The initial transform to apply to the page. michael@0: * This may be useful to, for example, move the origin in and michael@0: * over a bit to account for a margin, scale the canvas, michael@0: * or apply a rotation. Note1: the SkPDFDevice also applies michael@0: * a scale+translate transform to move the origin from the michael@0: * bottom left (PDF default) to the top left. Note2: drawDevice michael@0: * (used by layer restore) draws the device after this initial michael@0: * transform is applied, so the PDF device does an michael@0: * inverse scale+translate to accommodate the one that SkPDFDevice michael@0: * always does. michael@0: */ michael@0: // Deprecated, please use SkDocument::CreatePdf() instead. michael@0: SK_API SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize, michael@0: const SkMatrix& initialTransform); michael@0: SK_API virtual ~SkPDFDevice(); michael@0: michael@0: virtual void clear(SkColor color) SK_OVERRIDE; michael@0: michael@0: /** These are called inside the per-device-layer loop for each draw call. michael@0: When these are called, we have already applied any saveLayer operations, michael@0: and are handling any looping from the paint, and any effects from the michael@0: DrawFilter. michael@0: */ michael@0: virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE; michael@0: virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, michael@0: size_t count, const SkPoint[], michael@0: const SkPaint& paint) SK_OVERRIDE; michael@0: virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint); michael@0: virtual void drawRRect(const SkDraw&, const SkRRect& rr, michael@0: const SkPaint& paint) SK_OVERRIDE; michael@0: virtual void drawPath(const SkDraw&, const SkPath& origpath, michael@0: const SkPaint& paint, const SkMatrix* prePathMatrix, michael@0: bool pathIsMutable) SK_OVERRIDE; michael@0: virtual void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, michael@0: const SkRect* src, const SkRect& dst, michael@0: const SkPaint& paint, michael@0: SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE; michael@0: virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, michael@0: const SkMatrix& matrix, const SkPaint&) SK_OVERRIDE; michael@0: virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y, michael@0: const SkPaint& paint) SK_OVERRIDE; michael@0: virtual void drawText(const SkDraw&, const void* text, size_t len, michael@0: SkScalar x, SkScalar y, const SkPaint&) SK_OVERRIDE; michael@0: virtual void drawPosText(const SkDraw&, const void* text, size_t len, michael@0: const SkScalar pos[], SkScalar constY, michael@0: int scalarsPerPos, const SkPaint&) SK_OVERRIDE; michael@0: virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len, michael@0: const SkPath& path, const SkMatrix* matrix, michael@0: const SkPaint& paint) SK_OVERRIDE; michael@0: virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, michael@0: int vertexCount, const SkPoint verts[], michael@0: const SkPoint texs[], const SkColor colors[], michael@0: SkXfermode* xmode, const uint16_t indices[], michael@0: int indexCount, const SkPaint& paint) SK_OVERRIDE; michael@0: virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, michael@0: const SkPaint&) SK_OVERRIDE; michael@0: michael@0: virtual void onAttachToCanvas(SkCanvas* canvas) SK_OVERRIDE; michael@0: virtual void onDetachFromCanvas() SK_OVERRIDE; michael@0: michael@0: enum DrawingArea { michael@0: kContent_DrawingArea, // Drawing area for the page content. michael@0: kMargin_DrawingArea, // Drawing area for the margin content. michael@0: }; michael@0: michael@0: /** Sets the drawing area for the device. Subsequent draw calls are directed michael@0: * to the specific drawing area (margin or content). The default drawing michael@0: * area is the content drawing area. michael@0: * michael@0: * Currently if margin content is drawn and then a complex (for PDF) xfer michael@0: * mode is used, like SrcIn, Clear, etc, the margin content will get michael@0: * clipped. A simple way to avoid the bug is to always draw the margin michael@0: * content last. michael@0: */ michael@0: SK_API void setDrawingArea(DrawingArea drawingArea); michael@0: michael@0: /** Sets the DCTEncoder for images. michael@0: * @param encoder The encoder to encode a bitmap as JPEG (DCT). michael@0: * Result of encodings are cached, if the encoder changes the michael@0: * behaivor dynamically and an image is added to a second catalog, michael@0: * we will likely use the result of the first encoding call. michael@0: * By returning false from the encoder function, the encoder result michael@0: * is not used. michael@0: * Callers might not want to encode small images, as the time spent michael@0: * encoding and decoding might not be worth the space savings, michael@0: * if any at all. michael@0: */ michael@0: void setDCTEncoder(SkPicture::EncodeBitmap encoder) { michael@0: fEncoder = encoder; michael@0: } michael@0: michael@0: // PDF specific methods. michael@0: michael@0: /** Returns the resource dictionary for this device. michael@0: */ michael@0: SK_API SkPDFResourceDict* getResourceDict(); michael@0: michael@0: /** Get the fonts used on this device. michael@0: */ michael@0: SK_API const SkTDArray& getFontResources() const; michael@0: michael@0: /** Add our named destinations to the supplied dictionary. michael@0: * @param dict Dictionary to add destinations to. michael@0: * @param page The PDF object representing the page for this device. michael@0: */ michael@0: void appendDestinations(SkPDFDict* dict, SkPDFObject* page); michael@0: michael@0: /** Returns a copy of the media box for this device. The caller is required michael@0: * to unref() this when it is finished. michael@0: */ michael@0: SK_API SkPDFArray* copyMediaBox() const; michael@0: michael@0: /** Get the annotations from this page, or NULL if there are none. michael@0: */ michael@0: SK_API SkPDFArray* getAnnotations() const { return fAnnotations; } michael@0: michael@0: /** Returns a SkStream with the page contents. The caller is responsible michael@0: for a reference to the returned value. michael@0: DEPRECATED: use copyContentToData() michael@0: */ michael@0: SK_API SkStream* content() const; michael@0: michael@0: /** Returns a SkStream with the page contents. The caller is responsible michael@0: * for calling data->unref() when it is finished. michael@0: */ michael@0: SK_API SkData* copyContentToData() const; michael@0: michael@0: SK_API const SkMatrix& initialTransform() const { michael@0: return fInitialTransform; michael@0: } michael@0: michael@0: /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font michael@0: * that shows on this device. michael@0: */ michael@0: const SkPDFGlyphSetMap& getFontGlyphUsage() const { michael@0: return *(fFontGlyphUsage.get()); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * rasterDpi - the DPI at which features without native PDF support michael@0: * will be rasterized (e.g. draw image with perspective, michael@0: * draw text with perspective, ...) michael@0: * A larger DPI would create a PDF that reflects the original michael@0: * intent with better fidelity, but it can make for larger michael@0: * PDF files too, which would use more memory while rendering, michael@0: * and it would be slower to be processed or sent online or michael@0: * to printer. michael@0: */ michael@0: void setRasterDpi(SkScalar rasterDpi) { michael@0: fRasterDpi = rasterDpi; michael@0: } michael@0: michael@0: protected: michael@0: virtual bool onReadPixels(const SkBitmap& bitmap, int x, int y, michael@0: SkCanvas::Config8888) SK_OVERRIDE; michael@0: michael@0: virtual bool allowImageFilter(const SkImageFilter*) SK_OVERRIDE; michael@0: michael@0: private: michael@0: // TODO(vandebo): push most of SkPDFDevice's state into a core object in michael@0: // order to get the right access levels without using friend. michael@0: friend class ScopedContentEntry; michael@0: michael@0: SkISize fPageSize; michael@0: SkISize fContentSize; michael@0: SkMatrix fInitialTransform; michael@0: SkClipStack fExistingClipStack; michael@0: SkRegion fExistingClipRegion; michael@0: SkPDFArray* fAnnotations; michael@0: SkPDFResourceDict* fResourceDict; michael@0: SkTDArray fNamedDestinations; michael@0: michael@0: SkTDArray fGraphicStateResources; michael@0: SkTDArray fXObjectResources; michael@0: SkTDArray fFontResources; michael@0: SkTDArray fShaderResources; michael@0: michael@0: SkAutoTDelete fContentEntries; michael@0: ContentEntry* fLastContentEntry; michael@0: SkAutoTDelete fMarginContentEntries; michael@0: ContentEntry* fLastMarginContentEntry; michael@0: DrawingArea fDrawingArea; michael@0: michael@0: const SkClipStack* fClipStack; michael@0: michael@0: // Accessor and setter functions based on the current DrawingArea. michael@0: SkAutoTDelete* getContentEntries(); michael@0: ContentEntry* getLastContentEntry(); michael@0: void setLastContentEntry(ContentEntry* contentEntry); michael@0: michael@0: // Glyph ids used for each font on this device. michael@0: SkAutoTDelete fFontGlyphUsage; michael@0: michael@0: SkPicture::EncodeBitmap fEncoder; michael@0: SkScalar fRasterDpi; michael@0: michael@0: SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack, michael@0: const SkRegion& existingClipRegion); michael@0: michael@0: // override from SkBaseDevice michael@0: virtual SkBaseDevice* onCreateDevice(const SkImageInfo&, Usage) SK_OVERRIDE; michael@0: michael@0: void init(); michael@0: void cleanUp(bool clearFontUsage); michael@0: SkPDFFormXObject* createFormXObjectFromDevice(); michael@0: michael@0: void drawFormXObjectWithMask(int xObjectIndex, michael@0: SkPDFFormXObject* mask, michael@0: const SkClipStack* clipStack, michael@0: const SkRegion& clipRegion, michael@0: SkXfermode::Mode mode, michael@0: bool invertClip); michael@0: michael@0: // If the paint or clip is such that we shouldn't draw anything, this michael@0: // returns NULL and does not create a content entry. michael@0: // setUpContentEntry and finishContentEntry can be used directly, but michael@0: // the preferred method is to use the ScopedContentEntry helper class. michael@0: ContentEntry* setUpContentEntry(const SkClipStack* clipStack, michael@0: const SkRegion& clipRegion, michael@0: const SkMatrix& matrix, michael@0: const SkPaint& paint, michael@0: bool hasText, michael@0: SkPDFFormXObject** dst); michael@0: void finishContentEntry(SkXfermode::Mode xfermode, michael@0: SkPDFFormXObject* dst, michael@0: SkPath* shape); michael@0: bool isContentEmpty(); michael@0: michael@0: void populateGraphicStateEntryFromPaint(const SkMatrix& matrix, michael@0: const SkClipStack& clipStack, michael@0: const SkRegion& clipRegion, michael@0: const SkPaint& paint, michael@0: bool hasText, michael@0: GraphicStateEntry* entry); michael@0: int addGraphicStateResource(SkPDFGraphicState* gs); michael@0: int addXObjectResource(SkPDFObject* xObject); michael@0: michael@0: void updateFont(const SkPaint& paint, uint16_t glyphID, michael@0: ContentEntry* contentEntry); michael@0: int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID); michael@0: michael@0: void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry); michael@0: void internalDrawBitmap(const SkMatrix& matrix, michael@0: const SkClipStack* clipStack, michael@0: const SkRegion& clipRegion, michael@0: const SkBitmap& bitmap, michael@0: const SkIRect* srcRect, michael@0: const SkPaint& paint); michael@0: michael@0: /** Helper method for copyContentToData. It is responsible for copying the michael@0: * list of content entries |entry| to |data|. michael@0: */ michael@0: void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const; michael@0: michael@0: #ifdef SK_PDF_USE_PATHOPS michael@0: bool handleInversePath(const SkDraw& d, const SkPath& origPath, michael@0: const SkPaint& paint, bool pathIsMutable, michael@0: const SkMatrix* prePathMatrix = NULL); michael@0: #endif michael@0: bool handleRectAnnotation(const SkRect& r, const SkMatrix& matrix, michael@0: const SkPaint& paint); michael@0: bool handlePointAnnotation(const SkPoint* points, size_t count, michael@0: const SkMatrix& matrix, const SkPaint& paint); michael@0: SkPDFDict* createLinkAnnotation(const SkRect& r, const SkMatrix& matrix); michael@0: void handleLinkToURL(SkData* urlData, const SkRect& r, michael@0: const SkMatrix& matrix); michael@0: void handleLinkToNamedDest(SkData* nameData, const SkRect& r, michael@0: const SkMatrix& matrix); michael@0: void defineNamedDestination(SkData* nameData, const SkPoint& point, michael@0: const SkMatrix& matrix); michael@0: michael@0: typedef SkBitmapDevice INHERITED; michael@0: michael@0: // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create michael@0: // an SkPDFDevice michael@0: //friend class SkDocument_PDF; michael@0: //friend class SkPDFImageShader; michael@0: }; michael@0: michael@0: #endif