• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 Google LLC.
2 // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3 #ifndef SkPDFDocument_DEFINED
4 #define SkPDFDocument_DEFINED
5 
6 #include "include/core/SkDocument.h"
7 #include "include/core/SkMilestone.h"
8 #include "include/core/SkRefCnt.h"
9 #include "include/core/SkScalar.h"
10 #include "include/core/SkString.h"
11 #include "include/private/base/SkAPI.h"
12 #include "include/private/base/SkNoncopyable.h"
13 
14 #include <cstdint>
15 #include <memory>
16 #include <vector>
17 
18 class SkCanvas;
19 class SkCodec;
20 class SkData;
21 class SkExecutor;
22 class SkPDFArray;
23 class SkPDFStructTree;
24 class SkPixmap;
25 class SkWStream;
26 
27 #define SKPDF_STRING(X) SKPDF_STRING_IMPL(X)
28 #define SKPDF_STRING_IMPL(X) #X
29 
30 namespace SkPDF {
31 
32 /** Attributes for nodes in the PDF tree. */
33 class SK_API AttributeList : SkNoncopyable {
34 public:
35     AttributeList();
36     ~AttributeList();
37 
38     // Each attribute must have an owner (e.g. "Layout", "List", "Table", etc)
39     // and an attribute name (e.g. "BBox", "RowSpan", etc.) from PDF32000_2008 14.8.5,
40     // and then a value of the proper type according to the spec.
41     void appendInt(const char* owner, const char* name, int value);
42     void appendFloat(const char* owner, const char* name, float value);
43     void appendName(const char* owner, const char* attrName, const char* value);
44     void appendFloatArray(const char* owner,
45                           const char* name,
46                           const std::vector<float>& value);
47     void appendNodeIdArray(const char* owner,
48                            const char* attrName,
49                            const std::vector<int>& nodeIds);
50 
51 private:
52     friend class ::SkPDFStructTree;
53 
54     std::unique_ptr<SkPDFArray> fAttrs;
55     std::vector<int> fElemIds; // element identifiers referenced by fAttrs
56 };
57 
58 /** A node in a PDF structure tree, giving a semantic representation
59     of the content.  Each node ID is associated with content
60     by passing the SkCanvas and node ID to SkPDF::SetNodeId() when drawing.
61     NodeIDs should be unique within each tree.
62 */
63 struct StructureElementNode {
64     SkString fTypeString;
65     std::vector<std::unique_ptr<StructureElementNode>> fChildVector;
66     int fNodeId = 0;
67     AttributeList fAttributes;
68     SkString fAlt;
69     SkString fLang;
70 };
71 
72 struct DateTime {
73     int16_t  fTimeZoneMinutes;  // The number of minutes that this
74                                 // is ahead of or behind UTC.
75     uint16_t fYear;          //!< e.g. 2005
76     uint8_t  fMonth;         //!< 1..12
77     uint8_t  fDayOfWeek;     //!< 0..6, 0==Sunday
78     uint8_t  fDay;           //!< 1..31
79     uint8_t  fHour;          //!< 0..23
80     uint8_t  fMinute;        //!< 0..59
81     uint8_t  fSecond;        //!< 0..59
82 
83     void toISO8601(SkString* dst) const;
84 };
85 
86 using DecodeJpegCallback = std::unique_ptr<SkCodec> (*)(sk_sp<SkData>);
87 using EncodeJpegCallback = bool (*)(SkWStream* dst, const SkPixmap& src, int quality);
88 
89 /** Optional metadata to be passed into the PDF factory function.
90 */
91 struct Metadata {
92     /** The document's title.
93     */
94     SkString fTitle;
95 
96     /** The name of the person who created the document.
97     */
98     SkString fAuthor;
99 
100     /** The subject of the document.
101     */
102     SkString fSubject;
103 
104     /** Keywords associated with the document.  Commas may be used to delineate
105         keywords within the string.
106     */
107     SkString fKeywords;
108 
109     /** If the document was converted to PDF from another format,
110         the name of the conforming product that created the
111         original document from which it was converted.
112     */
113     SkString fCreator;
114 
115     /** The product that is converting this document to PDF.
116     */
117     SkString fProducer = SkString("Skia/PDF m" SKPDF_STRING(SK_MILESTONE));
118 
119     /** The date and time the document was created.
120         The zero default value represents an unknown/unset time.
121     */
122     DateTime fCreation = {0, 0, 0, 0, 0, 0, 0, 0};
123 
124     /** The date and time the document was most recently modified.
125         The zero default value represents an unknown/unset time.
126     */
127     DateTime fModified = {0, 0, 0, 0, 0, 0, 0, 0};
128 
129     /** The natural language of the text in the PDF. If fLang is empty, the root
130         StructureElementNode::fLang will be used (if not empty). Text not in
131         this language should be marked with StructureElementNode::fLang.
132     */
133     SkString fLang;
134 
135     /** The DPI (pixels-per-inch) at which features without native PDF support
136         will be rasterized (e.g. draw image with perspective, draw text with
137         perspective, ...)  A larger DPI would create a PDF that reflects the
138         original intent with better fidelity, but it can make for larger PDF
139         files too, which would use more memory while rendering, and it would be
140         slower to be processed or sent online or to printer.
141     */
142     SkScalar fRasterDPI = SK_ScalarDefaultRasterDPI;
143 
144     /** If true, include XMP metadata, a document UUID, and sRGB output intent
145         information.  This adds length to the document and makes it
146         non-reproducable, but are necessary features for PDF/A-2b conformance
147     */
148     bool fPDFA = false;
149 
150     /** Encoding quality controls the trade-off between size and quality. By
151         default this is set to 101 percent, which corresponds to lossless
152         encoding. If this value is set to a value <= 100, and the image is
153         opaque, it will be encoded (using JPEG) with that quality setting.
154     */
155     int fEncodingQuality = 101;
156 
157     /** An optional tree of structured document tags that provide
158         a semantic representation of the content. The caller
159         should retain ownership.
160     */
161     StructureElementNode* fStructureElementTreeRoot = nullptr;
162 
163     enum class Outline : int {
164         None = 0,
165         StructureElementHeaders = 1,
166     } fOutline = Outline::None;
167 
168     /** Executor to handle threaded work within PDF Backend. If this is nullptr,
169         then all work will be done serially on the main thread. To have worker
170         threads assist with various tasks, set this to a valid SkExecutor
171         instance. Currently used for executing Deflate algorithm in parallel.
172 
173         If set, the PDF output will be non-reproducible in the order and
174         internal numbering of objects, but should render the same.
175 
176         Experimental.
177     */
178     SkExecutor* fExecutor = nullptr;
179 
180     /** PDF streams may be compressed to save space.
181         Use this to specify the desired compression vs time tradeoff.
182     */
183     enum class CompressionLevel : int {
184         Default = -1,
185         None = 0,
186         LowButFast = 1,
187         Average = 6,
188         HighButSlow = 9,
189     } fCompressionLevel = CompressionLevel::Default;
190 
191     /** Preferred Subsetter. */
192     enum Subsetter {
193         kHarfbuzz_Subsetter,
194     } fSubsetter = kHarfbuzz_Subsetter;
195 
196     /** Clients can provide a way to decode jpeg. To use Skia's JPEG decoder, pass in
197         SkJpegDecoder::Decode. If not supplied, all images will need to be re-encoded
198         as jpegs or deflated images before embedding. If supplied, Skia may be able to
199         skip the re-encoding step.
200         Skia's JPEG decoder can be used here.
201     */
202     SkPDF::DecodeJpegCallback jpegDecoder = nullptr;
203 
204     /** Clients can provide a way to encode jpeg. If not supplied, images will be embedded
205         as a deflated image, potentially making them much larger. If clients provide
206         their own implementation, JPEGs should be encoded to RGB (not YUV) otherwise they
207         will have the wrong surrounding metadata provided by Skia.
208         Skia's JPEG encoder can be used here.
209     */
210     SkPDF::EncodeJpegCallback jpegEncoder = nullptr;
211 
212     // Skia's PDF support depends on having both a jpeg encoder and decoder for writing
213     // compact PDFs. It will technically work, but produce larger than optimal PDFs
214     // if either the decoder or encoder are left as nullptr. If clients will be creating
215     // PDFs that don't use images or otherwise want to incur this cost (with the upside
216     // of not having a jpeg library), they should set this to true to avoid an internal
217     // assert from firing.
218     bool allowNoJpegs = false;
219 };
220 
221 /** Associate a node ID with subsequent drawing commands in an
222     SkCanvas.  The same node ID can appear in a StructureElementNode
223     in order to associate a document's structure element tree with
224     its content.
225 
226     A node ID of zero indicates no node ID.
227 
228     @param canvas  The canvas used to draw to the PDF.
229     @param nodeId  The node ID for subsequent drawing commands.
230 */
231 SK_API void SetNodeId(SkCanvas* dst, int nodeID);
232 
233 /** Create a PDF-backed document, writing the results into a SkWStream.
234 
235     PDF pages are sized in point units. 1 pt == 1/72 inch == 127/360 mm.
236 
237     @param stream A PDF document will be written to this stream.  The document may write
238            to the stream at anytime during its lifetime, until either close() is
239            called or the document is deleted.
240     @param metadata a PDFmetadata object. Some fields may be left empty.
241 
242     @returns NULL if there is an error, otherwise a newly created PDF-backed SkDocument.
243 */
244 SK_API sk_sp<SkDocument> MakeDocument(SkWStream* stream, const Metadata& metadata);
245 
246 #if !defined(SK_DISABLE_LEGACY_PDF_JPEG)
MakeDocument(SkWStream * stream)247 static inline sk_sp<SkDocument> MakeDocument(SkWStream* stream) {
248     return MakeDocument(stream, Metadata());
249 }
250 #endif
251 
252 }  // namespace SkPDF
253 
254 #undef SKPDF_STRING
255 #undef SKPDF_STRING_IMPL
256 #endif  // SkPDFDocument_DEFINED
257