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