• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1---
2title: 'PDF Theory of Operation'
3linkTitle: 'PDF Theory of Operation'
4---
5
6<!--
7PRE-GIT DOCUMENT VERSION HISTORY
8    2012-06-25 Steve VanDeBogart
9               * Original version
10    2015-01-14 Hal Canary.
11               * Add section "Using the PDF backend"
12               * Markdown formatting
13-->
14
15Internally, SkPDFDocument and SkPDFDevice represents PDF documents and pages.
16This document describes how the backend operates, but **these interfaces are not
17part of the public API and are subject to perpetual change.**
18
19See [Using Skia's PDF Backend](/docs/user/sample/pdf/) to find out how to use
20SkPDF as a client calling Skia's public API.
21
22---
23
24### Contents
25
26- [Typical usage of the PDF backend](#Typical_usage_of_the_PDF_backend)
27- [PDF Objects and Document Structure](#PDF_Objects_and_Document_Structure)
28- [PDF drawing](#PDF_drawing)
29- [Interned objects](#Interned_objects)
30- [Graphic States](#Graphic_States)
31- [Clip and Transform](#Clip_and_Transform)
32- [Generating a content stream](#Generating_a_content_stream)
33- [Drawing details](#Drawing_details)
34  - [Layers](#Layers)
35  - [Fonts](#Fonts)
36  - [Shaders](#Shaders)
37  - [Xfer modes](#Xfer_modes)
38- [Known issues](#Known_issues)
39
40## <span id="Typical_usage_of_the_PDF_backend">Typical usage of the PDF backend</span>
41
42SkPDFDevice is the main interface to the PDF backend. This child of SkDevice can
43be set on an SkCanvas and drawn to. Once drawing to the canvas is complete
44(SkDocument::onEndPage() is called), the device's content and resources are
45added to the SkPDFDocument that owns the device. A new SkPDFDevice should be
46created for each page or layer desired in the document. After all the pages have
47been added to the document, `SkPDFDocument::onClose()` is called to finish
48serializing the PDF file.
49
50## <span id="PDF_Objects_and_Document_Structure">PDF Objects and Document Structure</span>
51
52![PDF Logical Document Structure](../PdfLogicalDocumentStructure.png)
53
54**Background**: The PDF file format has a header, a set of objects and then a
55footer that contains a table of contents for all of the objects in the document
56(the cross-reference table). The table of contents lists the specific byte
57position for each object. The objects may have references to other objects and
58the ASCII size of those references is dependent on the object number assigned to
59the referenced object; therefore we can't calculate the table of contents until
60the size of objects is known, which requires assignment of object numbers. The
61document uses SkWStream::bytesWritten() to query the offsets of each object and
62build the cross-reference table.
63
64Furthermore, PDF files can support a _linearized_ mode, where objects are in a
65specific order so that pdf-viewers can more easily retrieve just the objects
66they need to display a specific page, i.e. by byte-range requests over the web.
67Linearization also requires that all objects used or referenced on the first
68page of the PDF have object numbers before the rest of the objects.
69Consequently, before generating a linearized PDF, all objects, their sizes, and
70object references must be known. Skia has no plans to implement linearized PDFs.
71
72    %PDF-1.4
73    …objects...
74    xref
75    0 31  % Total number of entries in the table of contents.
76    0000000000 65535 f
77    0000210343 00000 n
7879    0000117055 00000 n
80    trailer
81    <</Size 31 /Root 1 0 R>>
82    startxref
83    210399  % Byte offset to the start of the table of contents.
84    %%EOF
85
86The the virtual class SkPDFObject are used to manage the needs of the file
87format. Any object that will represent a PDF object must inherit from
88SkPDFObject and implement the methods to generate the binary representation and
89report any other SkPDFObjects used as resources. SkPDFTypes.h defines most of
90the basic PDF object types: bool, int, scalar, string, name, array, dictionary,
91and stream. (A stream is a dictionary containing at least a Length entry
92followed by the data of the stream.)
93
94Streams are now handled in a slightly different way. The SkPDFStreamOut()
95function compresses and serializes the binary data immediately instead of
96creating a new object.
97
98All of these PDF object types except the stream type can be used in both a
99direct and an indirect fashion, i.e. an array can have an int or a dictionary as
100an inline entry, which does not require an object number. The stream type,
101cannot be inlined and must be referred to with an object reference. Most of the
102time, other objects types can be referred to with an object reference, but there
103are specific rules in the PDF specification that requires an inline reference in
104some place or an indirect reference in other places. All indirect objects must
105have an object number assigned.
106
107- **bools**: `true` `false`
108- **ints**: `42` `0` `-1`
109- **scalars**: `0.001`
110- **strings**: `(strings are in parentheses or byte encoded)` `<74657374>`
111- **name**: `/Name` `/Name#20with#20spaces`
112- **array**: `[/Foo 42 (arrays can contain multiple types)]`
113- **dictionary**: `<</Key1 (value1) /key2 42>>`
114- **indirect object**:
115  `5 0 obj (An indirect string. Indirect objects have an object number and a generation number, Skia always uses generation 0 objects) endobj`
116- **object reference**: `5 0 R`
117- **stream**:
118  `<</Length 56>> stream ...stream contents can be arbitrary, including binary... endstream`
119
120Indirect objects are either:
121
122- Serialized as soon as they are needed, and a new SkPDFIndirectReference is
123  returned, or
124
125- Serialized later, but reserve a document-unique SkPDFIndirectReference to
126  allow other objects to refer to it.
127
128Example document:
129
130    %PDF-1.4
131    2 0 obj <<
132      /Type /Catalog
133      /Pages 1 0 R
134    >>
135    endobj
136    3 0 obj <<
137      /Type /Page
138      /Parent 1 0 R
139      /Resources <>
140      /MediaBox [0 0 612 792]
141      /Contents 4 0 R
142    >>
143    endobj
144    4 0 obj <> stream
145    endstream
146    endobj
147    1 0 obj <<
148      /Type /Pages
149      /Kids [3 0 R]
150      /Count 1
151    >>
152    endobj
153    xref
154    0 5
155    0000000000 65535 f
156    0000000236 00000 n
157    0000000009 00000 n
158    0000000062 00000 n
159    0000000190 00000 n
160    trailer
161    <</Size 5 /Root 2 0 R>>
162    startxref
163    299
164    %%EOF
165
166## <span id="PDF_drawing">PDF drawing</span>
167
168Most drawing in PDF is specified by the text of a stream, referred to as a
169content stream. The syntax of the content stream is different than the syntax of
170the file format described above and is much closer to PostScript in nature. The
171commands in the content stream tell the PDF interpreter to draw things, like a
172rectangle (`x y w h re`), an image, or text, or to do meta operations like set
173the drawing color, apply a transform to the drawing coordinates, or clip future
174drawing operations. The page object that references a content stream has a list
175of resources that can be used in the content stream using the dictionary name to
176reference the resources. Resources are things like font objects, images objects,
177graphic state objects (a set of meta operations like miter limit, line width,
178etc). Because of a mismatch between Skia and PDF’s support for transparency
179(which will be explained later), SkPDFDevice records each drawing operation into
180an internal structure (ContentEntry) and only when the content stream is needed
181does it flatten that list of structures into the final content stream.
182
183    4 0 obj <<
184      /Type /Page
185      /Resources <<
186        /Font <</F1 9 0 R>>
187        /XObject <</Image1 22 0 R /Image2 73 0 R>>
188      >>
189      /Content 5 0 R
190    >> endobj
191
192    5 0 obj <</Length 227>> stream
193    % In the font specified in object 9 and a height
194    % of 12 points, at (72, 96) draw ‘Hello World.’
195    BT
196      /F1 12 Tf
197      72 96 Td
198      (Hello World) Tj
199    ET
200    % Draw a filled rectange.
201    200 96 72 72 re B
202    ...
203    endstream
204    endobj
205
206## <span id="Interned_objects">Interned objects</span>
207
208There are a number of high level PDF objects (like fonts, graphic states, etc)
209that are likely to be referenced multiple times in a single PDF. To ensure that
210there is only one copy of each object, the SkPDFDocument holds on to a mapping
211from type-specific keys onto the SkPDFIndirectReference for these objects.
212
213## <span id="Graphic_States">Graphic States</span>
214
215PDF has a number of parameters that affect how things are drawn. The ones that
216correspond to drawing options in Skia are: color, alpha, line cap, line join
217type, line width, miter limit, and xfer/blend mode (see later section for xfer
218modes). With the exception of color, these can all be specified in a single pdf
219object, represented by the SkPDFGraphicState class. A simple command in the
220content stream can then set the drawing parameters to the values specified in
221that graphic state object. PDF does not allow specifying color in the graphic
222state object, instead it must be specified directly in the content stream.
223Similarly the current font and font size are set directly in the content stream.
224
225    6 0 obj <<
226      /Type /ExtGState
227      /CA 1  % Opaque - alpha = 1
228      /LC 0  % Butt linecap
229      /LJ 0  % Miter line-join
230      /LW 2  % Line width of 2
231      /ML 6  % Miter limit of 6
232      /BM /Normal  % Blend mode is normal i.e. source over
233    >>
234    endobj
235
236## <span id="Clip_and_Transform">Clip and Transform</span>
237
238Similar to Skia, PDF allows drawing to be clipped or transformed. However, there
239are a few caveats that affect the design of the PDF backend. PDF does not
240support perspective transforms (perspective transform are treated as identity
241transforms). Clips, however, have more issues to cotend with. PDF clips cannot
242be directly unapplied or expanded. i.e. once an area has been clipped off, there
243is no way to draw to it. However, PDF provides a limited depth stack for the PDF
244graphic state (which includes the drawing parameters mentioned above in the
245Graphic States section as well as the clip and transform). Therefore to undo a
246clip, the PDF graphic state must be pushed before the clip is applied, then
247popped to revert to the state of the graphic state before the clip was applied.
248
249As the canvas makes drawing calls into SkPDFDevice, the active transform, clip
250region, and clip stack are stored in a ContentEntry structure. Later, when the
251ContentEntry structures are flattened into a valid PDF content stream, the
252transforms and clips are compared to decide on an efficient set of operations to
253transition between the states needed. Currently, a local optimization is used,
254to figure out the best transition from one state to the next. A global
255optimization could improve things by more effectively using the graphics state
256stack provided in the PDF format.
257
258## <span id="Generating_a_content_stream">Generating a content stream</span>
259
260For each draw call on an SkPDFDevice, a new ContentEntry is created, which
261stores the matrix, clip region, and clip stack as well as the paint parameters.
262Most of the paint parameters are bundled into an SkPDFGraphicState (interned)
263with the rest (color, font size, etc) explicitly stored in the ContentEntry.
264After populating the ContentEntry with all the relevant context, it is compared
265to the the most recently used ContentEntry. If the context matches, then the
266previous one is appended to instead of using the new one. In either case, with
267the context populated into the ContentEntry, the appropriate draw call is
268allowed to append to the content stream snippet in the ContentEntry to affect
269the core of the drawing call, i.e. drawing a shape, an image, text, etc.
270
271When all drawing is complete, SkPDFDocument::onEndPage() will call
272SkPDFDevice::content() to request the complete content stream for the page. The
273first thing done is to apply the initial transform specified in part in the
274constructor, this transform takes care of changing the coordinate space from an
275origin in the lower left (PDF default) to the upper left (Skia default) as well
276as any translation or scaling requested by the user (i.e. to achieve a margin or
277scale the canvas). Next (well almost next, see the next section), a clip is
278applied to restrict drawing to the content area (the part of the page inside the
279margins) of the page. Then, each ContentEntry is applied to the content stream
280with the help of a helper class, GraphicStackState, which tracks the state of
281the PDF graphics stack and optimizes the output. For each ContentEntry, commands
282are emitted to the final content entry to update the clip from its current state
283to the state specified in the ContentEntry, similarly the Matrix and drawing
284state (color, line joins, etc) are updated, then the content entry fragment (the
285actual drawing operation) is appended.
286
287## <span id="Drawing_details">Drawing details</span>
288
289Certain objects have specific properties that need to be dealt with. Images,
290layers (see below), and fonts assume the standard PDF coordinate system, so we
291have to undo any flip to the Skia coordinate system before drawing these
292entities. We don't currently support inverted paths, so filling an inverted path
293will give the wrong result ([issue 241](https://bug.skia.org/241)). PDF doesn't
294draw zero length lines that have butt of square caps, so that is emulated.
295
296### <span id="Layers">Layers</span>
297
298PDF has a higher level object called a form x-object (form external object) that
299is basically a PDF page, with resources and a content stream, but can be
300transformed and drawn on an existing page. This is used to implement layers.
301SkPDFDevice has a method, makeFormXObjectFromDevice(), which uses the
302SkPDFDevice::content() method to construct a form x-object from the the device.
303SkPDFDevice::drawDevice() works by creating a form x-object of the passed device
304and then drawing that form x-object in the root device. There are a couple
305things to be aware of in this process. As noted previously, we have to be aware
306of any flip to the coordinate system - flipping it an even number of times will
307lead to the wrong result unless it is corrected for. The SkClipStack passed to
308drawing commands includes the entire clip stack, including the clipping
309operations done on the base layer. Since the form x-object will be drawn as a
310single operation onto the base layer, we can assume that all of those clips are
311in effect and need not apply them within the layer.
312
313### <span id="Fonts">Fonts</span>
314
315There are many details for dealing with fonts, so this document will only talk
316about some of the more important ones. A couple short details:
317
318- We can't assume that an arbitrary font will be available at PDF view time, so
319  we embed all fonts in accordance with modern PDF guidelines.
320- Most fonts these days are TrueType fonts, so this is where most of the effort
321  has been concentrated.
322- Because Skia may only be given a glyph-id encoding of the text to render and
323  there is no perfect way to reverse the encoding, the PDF backend always uses
324  the glyph-id encoding of the text.
325
326#### _Type1/Type3 fonts_
327
328Linux supports Type1 fonts, but Windows and Mac seem to lack the functionality
329required to extract the required information from the font without parsing the
330font file. When a non TrueType font is used any any platform (except for Type1
331on Linux), it is encoded as a Type3 font. In this context, a Type3 font is an
332array of form x-objects (content streams) that draw each glyph of the font. No
333hinting or kerning information is included in a Type3 font, just the shape of
334each glyph. Any font that has the do-not embed copy protection bit set will also
335get embedded as a Type3 font. From what I understand, shapes are not
336copyrightable, but programs are, so by stripping all the programmatic
337information and only embedding the shape of the glyphs we are honoring the
338do-not embed bit as much as required by law.
339
340PDF only supports an 8-bit encoding for Type1 or Type3 fonts. However, they can
341contain more than 256 glyphs. The PDF backend handles this by segmenting the
342glyphs into groups of 255 (glyph id 0 is always the unknown glyph) and
343presenting the font as multiple fonts, each with up to 255 glyphs.
344
345#### _Font subsetting_
346
347Many fonts, especially fonts with CJK support are fairly large, so it is
348desirable to subset them. Chrome uses the SFNTLY package to provide subsetting
349support to Skia for TrueType fonts.
350
351### <span id="Shaders">Shaders</span>
352
353Skia has two types of predefined shaders, image shaders and gradient shaders. In
354both cases, shaders are effectively positioned absolutely, so the initial
355position and bounds of where they are visible is part of the immutable state of
356the shader object. Each of the Skia's tile modes needs to be considered and
357handled explicitly. The image shader we generate will be tiled, so tiling is
358handled by default. To support mirroring, we draw the image, reversed, on the
359appropriate axis, or on both axes plus a fourth in the vacant quadrant. For
360clamp mode, we extract the pixels along the appropriate edge and stretch the
361single pixel wide/long image to fill the bounds. For both x and y in clamp mode,
362we fill the corners with a rectangle of the appropriate color. The composed
363shader is then rotated or scaled as appropriate for the request.
364
365Gradient shaders are handled purely mathematically. First, the matrix is
366transformed so that specific points in the requested gradient are at pre-defined
367locations, for example, the linear distance of the gradient is always normalized
368to one. Then, a type 4 PDF function is created that achieves the desired
369gradient. A type 4 function is a function defined by a resticted postscript
370language. The generated functions clamp at the edges so if the desired tiling
371mode is tile or mirror, we hav to add a bit more postscript code to map any
372input parameter into the 0-1 range appropriately. The code to generate the
373postscript code is somewhat obtuse, since it is trying to generate optimized
374(for space) postscript code, but there is a significant number of comments to
375explain the intent.
376
377### <span id="Xfer_modes">Xfer modes</span>
378
379PDF supports some of the xfer modes used in Skia directly. For those, it is
380simply a matter of setting the blend mode in the graphic state to the
381appropriate value (Normal/SrcOver, Multiply, Screen, Overlay, Darken, Lighten,
382!ColorDOdge, ColorBurn, HardLight, SoftLight, Difference, Exclusion). Aside from
383the standard SrcOver mode, PDF does not directly support the porter-duff xfer
384modes though. Most of them (Clear, SrcMode, DstMode, DstOver, SrcIn, DstIn,
385SrcOut, DstOut) can be emulated by various means, mostly by creating form
386x-objects out of part of the content and drawing it with a another form x-object
387as a mask. I have not figured out how to emulate the following modes: SrcATop,
388DstATop, Xor, Plus.
389
390At the time of writing [2012-06-25], I have a
391[CL outstanding to fix a misunderstanding I had about the meaning of some of the emulated modes](https://codereview.appspot.com/4631078/).
392I will describe the system with this change applied.
393
394First, a bit of terminology and definition. When drawing something with an
395emulated xfer mode, what's already drawn to the device is called the destination
396or Dst, and what's about to be drawn is the source or Src. Src (and Dst) can
397have regions where it is transparent (alpha equals zero), but it also has an
398inherent shape. For most kinds of drawn objects, the shape is the same as where
399alpha is not zero. However, for things like images and layers, the shape is the
400bounds of the item, not where the alpha is non-zero. For example, a 10x10 image,
401that is transparent except for a 1x1 dot in the center has a shape that is
40210x10. The xfermodes gm test demonstrates the interaction between shape and
403alpha in combination with the port-duff xfer modes.
404
405The clear xfer mode removes any part of Dst that is within Src's shape. This is
406accomplished by bundling the current content of the device (Dst) into a single
407entity and then drawing that with the inverse of Src's shape used as a mask (we
408want Dst where Src isn't). The implementation of that takes a couple more steps.
409You may have to refer back to
410[the content stream section](#Generating_a_content_stream). For any draw call, a
411ContentEntry is created through a method called
412SkPDFDevice::setUpContentEntry(). This method examines the xfer modes in effect
413for that drawing operation and if it is an xfer mode that needs emulation, it
414creates a form x-object from the device, i.e. creates Dst, and stores it away
415for later use. This also clears all of that existing ContentEntry's on that
416device. The drawing operation is then allowed to proceed as normal (in most
417cases, see note about shape below), but into the now empty device. Then, when
418the drawing operation in done, a complementary method is
419called,SkPDFDevice::finishContentEntry(), which takes action if the current xfer
420mode is emulated. In the case of Clear, it packages what was just drawn into
421another form x-object, and then uses the Src form x-object, an invert function,
422and the Dst form x-object to draw Dst with the inverse shape of Src as a mask.
423This works well when the shape of Src is the same as the opaque part of the
424drawing, since PDF uses the alpha channel of the mask form x-object to do
425masking. When shape doesn't match the alpha channel, additional action is
426required. The drawing routines where shape and alpha don't match, set state to
427indicate the shape (always rectangular), which finishContentEntry uses. The
428clear xfer mode is a special case; if shape is needed, then Src isn't used, so
429there is code to not bother drawing Src if shape is required and the xfer mode
430is clear.
431
432SrcMode is clear plus Src being drawn afterward. DstMode simply omits drawing
433Src. DstOver is the same as SrcOver with Src and Dst swapped - this is
434accomplished by inserting the new ContentEntry at the beginning of the list of
435ContentEntry's in setUpContentEntry instead of at the end. SrcIn, SrcOut, DstIn,
436DstOut are similar to each, the difference being an inverted or non-inverted
437mask and swapping Src and Dst (or not). SrcIn is SrcMode with Src drawn with Dst
438as a mask. SrcOut is like SrcMode, but with Src drawn with an inverted Dst as a
439mask. DstIn is SrcMode with Dst drawn with Src as a mask. Finally, DstOut is
440SrcMode with Dst draw with an inverted Src as a mask.
441
442## <span id="Known_issues">Known issues</span>
443
444- [issue 249](https://bug.skia.org/249) SrcAtop Xor, and Plus xfer modes are not
445  supported.
446- [issue 240](https://bug.skia.org/240) drawVerticies is not implemented.
447- [issue 244](https://bug.skia.org/244) Mostly, only TTF fonts are _directly_
448  supported. (User metrics show that almost all fonts are truetype.)
449- [issue 260](https://bug.skia.org/260) Page rotation is accomplished by
450  specifying a different size page instead of including the appropriate rotation
451  annotation.
452
453---
454