Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 PDF Theory of Operation | |
| 2 ======================= | |
| 3 | |
| 4 <!-- | |
| 5 PRE-GIT DOCUMENT VERSION HISTORY | |
| 6 2012-06-25 Steve VanDeBogart | |
| 7 * Original version | |
| 8 2015-01-14 Hal Canary. | |
| 9 * Add section "Using the PDF backend" | |
| 10 * Markdown formatting | |
| 11 --> | |
| 12 | |
| 13 | |
| 14 Using the PDF backend | |
| 15 --------------------- | |
| 16 | |
| 17 Here is an example of a C++ function that makes use of Skia's PDF backend | |
| 18 in the recommended way: using the SkDocument API. | |
| 19 | |
| 20 #include "SkDocument.h" | |
| 21 | |
| 22 class DocumentSource { | |
| 23 public: | |
| 24 virtual int pageCount() const = 0; | |
| 25 virtual void draw(int page, SkCanvas* dst) const = 0; | |
| 26 virtual SkScalar width(int page) const = 0; | |
| 27 virtual SkScalar height(int page) const = 0; | |
| 28 }; | |
| 29 | |
| 30 bool WritePDF(const DocumentSource& src, | |
| 31 SkWStream* outputStream) { | |
| 32 SkAutoTUnref<SkDocument> pdfDocument( | |
| 33 SkDocument::CreatePDF(outputStream)); | |
| 34 | |
| 35 for (int page = 0; page < src.pageCount(); ++page) { | |
| 36 SkCanvas* canvas = pdfDocument->beginPage( | |
| 37 src.width(page), src.height(page)); | |
| 38 src.draw(page, canvas); | |
| 39 pdfDocument->endPage(); | |
| 40 } | |
| 41 | |
| 42 return pdfDocument->close(); | |
| 43 } | |
| 44 | |
| 45 Internally, Skia uses SkPDFDocument and SkPDFDevice to represent PDF | |
| 46 documents and pages. This document describes how the backend | |
| 47 operates, but **these interfaces are not part of the public API and | |
| 48 are subject to perpetual change.** | |
| 49 | |
| 50 * * * | |
| 51 | |
| 52 ### Contents ### | |
| 53 | |
| 54 * [Typical usage of the PDF backend](#Typical_usage_of_the_PDF_backend) | |
| 55 * [PDF Objects and Document Structure](#PDF_Objects_and_Document_Structure) | |
| 56 * [PDF drawing](#PDF_drawing) | |
| 57 * [Interned objects](#Interned_objects) | |
| 58 * [Graphic States](#Graphic_States) | |
| 59 * [Clip and Transform](#Clip_and_Transform) | |
| 60 * [Generating a content stream](#Generating_a_content_stream) | |
| 61 * [Margins and content area](#Margins_and_content_area) | |
| 62 * [Drawing details](#Drawing_details) | |
| 63 + [Layers](#Layers) | |
| 64 + [Fonts](#Fonts) | |
| 65 + [Shaders](#Shaders) | |
| 66 + [Xfer modes](#Xfer_modes) | |
| 67 * [Known issues](#Known_issues) | |
| 68 | |
| 69 <a name="Typical_usage_of_the_PDF_backend"></a> | |
| 70 Typical usage of the PDF backend | |
| 71 -------------------------------- | |
| 72 | |
| 73 SkPDFDevice is the main interface to the PDF backend. This child of | |
| 74 SkDevice can be set on an SkCanvas and drawn to. It requires no more | |
| 75 care and feeding than SkDevice. Once drawing is complete, the device | |
| 76 should be added to an SkPDFDocument as a page of the desired PDF. A | |
| 77 new SkPDFDevice should be created for each page desired in the | |
| 78 document. After all the pages have been added to the document, | |
| 79 `SkPDFDocument::emitPDF()` can be called to get a PDF file. One of the | |
| 80 special features of the PDF backend is that the same device can be | |
| 81 added to multiple documents. This for example, would let you generate | |
| 82 a PDF with the single page you just drew as well as adding it to a | |
| 83 longer document with a bunch of other pages. | |
| 84 | |
| 85 SkAutoUnref<SkPDFDevice> pdfDevice( | |
| 86 new SkPDFDevice(width, height, initial_transform)); | |
| 87 | |
| 88 SkCanvas canvas(pdfDevice); | |
| 89 draw_content(&canvas); | |
| 90 | |
| 91 SkPDFDocument doc; | |
| 92 doc.appendPage(dev); | |
| 93 doc.emitPDF(&pdf_stream); | |
| 94 | |
| 95 <a name="PDF_Objects_and_Document_Structure"></a> | |
| 96 PDF Objects and Document Structure | |
| 97 ---------------------------------- | |
| 98 | |
| 99 **Background**: The PDF file format has a header, a set of objects and | |
| 100 then a footer that contains a table of contents for all of the objects | |
| 101 in the document (the cross-reference table). The table of contents | |
| 102 lists the specific byte position for each object. The objects may have | |
| 103 references to other objects and the ASCII size of those references is | |
| 104 dependent on the object number assigned to the referenced object; | |
| 105 therefore we can’t calculate the table of contents until the size of | |
| 106 objects is known, which requires assignment of object | |
| 107 numbers. | |
| 108 | |
| 109 Furthermore, PDF files can support a *linearized* mode, where objects | |
| 110 are in a specific order so that pdf-viewers can more easily retrieve | |
| 111 just the objects they need to display a specific page, i.e. by | |
| 112 byte-range requests over the web. Linearization also requires that all | |
| 113 objects used or referenced on the first page of the PDF have object | |
| 114 numbers before the rest of the objects. Consequently, before | |
| 115 generating a linearized PDF, all objects, their sizes, and object | |
| 116 references must be known. Skia has no plans to implement linearized | |
| 117 PDFs. | |
| 118 | |
| 119 <!-- <del>At this point, linearized PDFs are not generated. The | |
| 120 framework to generate them is in place, but the final bits of code | |
| 121 have not been written.</del> --> | |
| 122 | |
| 123 %PDF-1.4 | |
| 124 …objects... | |
| 125 xref | |
| 126 0 31 % Total number of entries in the table of contents. | |
| 127 0000000000 65535 f | |
| 128 0000210343 00000 n | |
| 129 … | |
| 130 0000117055 00000 n | |
| 131 trailer | |
| 132 <</Size 31 /Root 1 0 R>> | |
| 133 startxref | |
| 134 210399 % Byte offset to the start of the table of contents. | |
| 135 %%EOF | |
| 136 | |
| 137 The class SkPDFCatalog and the virtual class SkPDFObject are used to | |
| 138 manage the needs of the file format. Any object that will represent a | |
| 139 PDF object must inherit from SkPDFObject and implement the methods to | |
| 140 generate the binary representation and report any other SkPDFObjects | |
| 141 used as resources. SkPDFTypes.h defines most of the basic PDF objects | |
| 142 types: bool, int, scalar, string, name, array, dictionary, and object | |
| 143 reference. The stream type is defined in SkPDFStream.h. A stream is a | |
| 144 dictionary containing at least a Length entry followed by the data of | |
| 145 the stream. All of these types except the stream type can be used in | |
| 146 both a direct and an indirect fashion, i.e. an array can have an int | |
| 147 or a dictionary as an inline entry, which does not require an object | |
| 148 number. The stream type, cannot be inlined and must be referred to | |
| 149 with an object reference. Most of the time, other objects types can be | |
| 150 referred to with an object reference, but there are specific rules in | |
| 151 the PDF specification that requires an inline reference in some place | |
| 152 or an indirect reference in other places. All indirect objects must | |
| 153 have an object number assigned. | |
| 154 | |
| 155 * **bools**: `true` `false` | |
| 156 * **ints**: `42` `0` `-1` | |
| 157 * **scalars**: `0.001` | |
| 158 * **strings**: `(strings are in parentheses or byte encoded)` `<74657374>` | |
| 159 * **name**: `/Name` `/Name#20with#20spaces` | |
| 160 * **array**: `[/Foo 42 (arrays can contain multiple types)]` | |
| 161 * **dictionary**: `<</Key1 (value1) /key2 42>>` | |
| 162 * **indirect object**: | |
| 163 `5 0 obj | |
| 164 (An indirect string. Indirect objects have an object number and a | |
| 165 generation number, Skia always uses generation 0 objects) | |
| 166 endobj` | |
| 167 * **object reference**: `5 0 R` | |
| 168 * **stream**: `<</Length 56>> | |
| 169 stream | |
| 170 ...stream contents can be arbitrary, including binary... | |
| 171 endstream` | |
| 172 | |
| 173 The PDF backend requires all indirect objects used in a PDF to be | |
| 174 added to the SkPDFCatalog of the SkPDFDocument. The catalog is | |
| 175 responsible for assigning object numbers and generating the table of | |
| 176 contents required at the end of PDF files. In some sense, generating a | |
| 177 PDF is a three step process. In the first step all the objects and | |
| 178 references among them are created (mostly done by SkPDFDevice). In the | |
| 179 second step, object numbers are assigned and SkPDFCatalog is informed | |
| 180 of the file offset of each indirect object. Finally, in the third | |
| 181 step, the header is printed, each object is printed, and then the | |
| 182 table of contents and trailer are printed. SkPDFDocument takes care of | |
| 183 collecting all the objects from the various SkPDFDevice instances, | |
| 184 adding them to an SkPDFCatalog, iterating through the objects once to | |
| 185 set their file positions, and iterating again to generate the final | |
| 186 PDF. | |
| 187 | |
| 188 %PDF-1.4 | |
| 189 2 0 obj << | |
| 190 /Type /Catalog | |
| 191 /Pages 1 0 R | |
| 192 >> | |
| 193 endobj | |
| 194 3 0 obj << | |
| 195 /Type /Page | |
| 196 /Parent 1 0 R | |
| 197 /Resources <> | |
| 198 /MediaBox [0 0 612 792] | |
| 199 /Contents 4 0 R | |
| 200 >> | |
| 201 endobj | |
| 202 4 0 obj <> stream | |
| 203 endstream | |
| 204 endobj | |
| 205 1 0 obj << | |
| 206 /Type /Pages | |
| 207 /Kids [3 0 R] | |
| 208 /Count 1 | |
| 209 >> | |
| 210 endobj | |
| 211 xref | |
| 212 0 5 | |
| 213 0000000000 65535 f | |
| 214 0000000236 00000 n | |
| 215 0000000009 00000 n | |
| 216 0000000062 00000 n | |
| 217 0000000190 00000 n | |
| 218 trailer | |
| 219 <</Size 5 /Root 2 0 R>> | |
| 220 startxref | |
| 221 299 | |
| 222 %%EOF | |
| 223 | |
| 224 <a name="PDF_drawing"></a> | |
| 225 PDF drawing | |
| 226 ----------- | |
| 227 | |
| 228 Most drawing in PDF is specified by the text of a stream, referred to | |
| 229 as a content stream. The syntax of the content stream is different | |
| 230 than the syntax of the file format described above and is much closer | |
| 231 to PostScript in nature. The commands in the content stream tell the | |
| 232 PDF interpreter to draw things, like a rectangle (`x y w h re`), an | |
| 233 image, or text, or to do meta operations like set the drawing color, | |
| 234 apply a transform to the drawing coordinates, or clip future drawing | |
| 235 operations. The page object that references a content stream has a | |
| 236 list of resources that can be used in the content stream using the | |
| 237 dictionary name to reference the resources. Resources are things like | |
| 238 font objects, images objects, graphic state objects (a set of meta | |
| 239 operations like miter limit, line width, etc). Because of a mismatch | |
| 240 between Skia and PDF’s support for transparency (which will be | |
| 241 explained later), SkPDFDevice records each drawing operation into an | |
| 242 internal structure (ContentEntry) and only when the content stream is | |
| 243 needed does it flatten that list of structures into the final content | |
| 244 stream. | |
| 245 | |
| 246 4 0 obj << | |
| 247 /Type /Page | |
| 248 /Resources << | |
| 249 /Font <</F1 9 0 R>> | |
| 250 /XObject <</Image1 22 0 R /Image2 73 0 R>> | |
| 251 >> | |
| 252 /Content 5 0 R | |
| 253 >> endobj | |
| 254 | |
| 255 5 0 obj <</Length 227>> stream | |
| 256 % In the font specified in object 9 and a height | |
| 257 % of 12 points, at (72, 96) draw ‘Hello World.’ | |
| 258 BT | |
| 259 /F1 12 Tf | |
| 260 72 96 Td | |
| 261 (Hello World) Tj | |
| 262 ET | |
| 263 % Draw a filled rectange. | |
| 264 200 96 72 72 re B | |
| 265 ... | |
| 266 endstream | |
| 267 endobj | |
| 268 | |
| 269 <a name="Interned_objects"></a> | |
| 270 Interned objects | |
| 271 ---------------- | |
| 272 | |
| 273 There are a number of high level PDF objects (like fonts, graphic | |
| 274 states, etc) that are likely to be referenced multiple times in a | |
| 275 single PDF. To ensure that there is only one copy of each object | |
| 276 instance these objects an implemented with an | |
| 277 [interning pattern](http://en.wikipedia.org/wiki/String_interning). | |
| 278 As such, the classes representing these objects (like | |
| 279 SkPDFGraphicState) have private constructors and static methods to | |
| 280 retrieve an instance of the class. Internally, the class has a list of | |
| 281 unique instances that it consults before returning a new instance of | |
| 282 the class. If the requested instance already exists, the existing one | |
| 283 is returned. For obvious reasons, the returned instance should not be | |
| 284 modified. A mechanism to ensure that interned classes are immutable is | |
| 285 needed. See [issue 2683](http://skbug.com/2683). | |
| 286 | |
| 287 <a name="Graphic_States"></a> | |
| 288 Graphic States | |
| 289 -------------- | |
| 290 | |
| 291 PDF has a number of parameters that affect how things are drawn. The | |
| 292 ones that correspond to drawing options in Skia are: color, alpha, | |
| 293 line cap, line join type, line width, miter limit, and xfer/blend mode | |
| 294 (see later section for xfer modes). With the exception of color, these | |
| 295 can all be specified in a single pdf object, represented by the | |
| 296 SkPDFGraphicState class. A simple command in the content stream can | |
| 297 then set the drawing parameters to the values specified in that | |
| 298 graphic state object. PDF does not allow specifying color in the | |
| 299 graphic state object, instead it must be specified directly in the | |
| 300 content stream. Similarly the current font and font size are set | |
| 301 directly in the content stream. | |
| 302 | |
| 303 6 0 obj << | |
| 304 /Type /ExtGState | |
| 305 /CA 1 % Opaque - alpha = 1 | |
| 306 /LC 0 % Butt linecap | |
| 307 /LJ 0 % Miter line-join | |
| 308 /LW 2 % Line width of 2 | |
| 309 /ML 6 % Miter limit of 6 | |
| 310 /BM /Normal % Blend mode is normal i.e. source over | |
| 311 >> | |
| 312 endobj | |
| 313 | |
| 314 <a name="Clip_and_Transform"></a> | |
| 315 Clip and Transform | |
| 316 ------------------ | |
| 317 | |
| 318 Similar to Skia, PDF allows drawing to be clipped or | |
| 319 transformed. However, there are a few caveats that affect the design | |
| 320 of the PDF backend. PDF does not support perspective transforms | |
| 321 (perspective transform are treated as identity transforms). Clips, | |
| 322 however, have more issues to cotend with. PDF clips cannot be directly | |
| 323 unapplied or expanded. i.e. once an area has been clipped off, there | |
| 324 is no way to draw to it. However, PDF provides a limited depth stack | |
| 325 for the PDF graphic state (which includes the drawing parameters | |
| 326 mentioned above in the Graphic States section as well as the clip and | |
| 327 transform). Therefore to undo a clip, the PDF graphic state must be | |
| 328 pushed before the clip is applied, then popped to revert to the state | |
| 329 of the graphic state before the clip was applied. Along these lines, | |
| 330 PDF only supports clip intersection, so Skia’s other modes | |
| 331 (Difference, Union, Xor, ReverseDifference, Replace) have to be | |
| 332 checked and simulated using SkRegion. A general purpose geometry | |
| 333 library ([issue 221](http://skbug.com/221)) would make the simulation | |
| 334 more accurate and produce smaller PDFs. | |
| 335 | |
| 336 <!-- TODO(halcanary): update this documentation. --> | |
| 337 | |
| 338 As the canvas makes drawing calls into SkPDFDevice, the active | |
| 339 transform, clip region, and clip stack are stored in a ContentEntry | |
| 340 structure. Later, when the ContentEntry structures are flattened into | |
| 341 a valid PDF content stream, the transforms and clips are compared to | |
| 342 decide on an efficient set of operations to transition between the | |
| 343 states needed. Currently, a local optimization is used, to figure out | |
| 344 the best transition from one state to the next. A global optimization | |
| 345 could improve things by more effectively using the graphics state | |
| 346 stack provided in the PDF format. | |
| 347 | |
| 348 <a name="Generating_a_content_stream"></a> | |
| 349 Generating a content stream | |
| 350 --------------------------- | |
| 351 | |
| 352 For each draw call on an SkPDFDevice, a new ContentEntry is created, | |
| 353 which stores the matrix, clip region, and clip stack as well as the | |
| 354 paint parameters. Most of the paint parameters are bundled into an | |
| 355 SkPDFGraphicState (interned) with the rest (color, font size, etc) | |
| 356 explicitly stored in the ContentEntry. After populating the | |
| 357 ContentEntry with all the relevant context, it is compared to the the | |
| 358 most recently used ContentEntry. If the context matches, then the | |
| 359 previous one is appended to instead of using the new one. In either | |
| 360 case, with the context populated into the ContentEntry, the | |
| 361 appropriate draw call is allowed to append to the content stream | |
| 362 snippet in the ContentEntry to affect the core of the drawing call, | |
| 363 i.e. drawing a shape, an image, text, etc. | |
| 364 | |
| 365 When all drawing is complete, SkPDFDocument::emitPDF() will call | |
| 366 SkPDFDevice::content() to request the complete content stream for the | |
| 367 page. The first thing done is to apply the initial transform specified | |
| 368 in part in the constructor, this transform takes care of changing the | |
| 369 coordinate space from an origin in the lower left (PDF default) to the | |
| 370 upper left (Skia default) as well as any translation or scaling | |
| 371 requested by the user (i.e. to achieve a margin or scale the | |
| 372 canvas). Next (well almost next, see the next section), a clip is | |
| 373 applied to restrict drawing to the content area (the part of the page | |
| 374 inside the margins) of the page. Then, each ContentEntry is applied to | |
| 375 the content stream with the help of a helper class, GraphicStackState, | |
| 376 which tracks the state of the PDF graphics stack and optimizes the | |
| 377 output. For each ContentEntry, commands are emitted to the final | |
| 378 content entry to update the clip from its current state to the state | |
| 379 specified in the ContentEntry, similarly the Matrix and drawing state | |
| 380 (color, line joins, etc) are updated, then the content entry fragment | |
| 381 (the actual drawing operation) is appended. | |
| 382 | |
| 383 <a name="Margins_and_content_area"></a> | |
| 384 Margins and content area | |
| 385 ------------------------ | |
| 386 | |
| 387 The above procedure does not permit drawing in the margins. This is | |
| 388 done in order to contain any rendering problems in WebKit. In order to | |
| 389 support headers and footers, which are drawn in the margin, a second | |
| 390 set of ContentEntry’s are maintained. The | |
| 391 methodSkPDFDevice::setDrawingArea() selects which set of | |
| 392 ContentEntry’s are drawn into. Then, in the SkPDFDevice::content() | |
| 393 method, just before the clip to the content area is applied the margin | |
| 394 ContentEntry's are played back. | |
| 395 | |
| 396 <!-- TODO(halcanary): update this documentation. --> | |
| 397 | |
| 398 <a name="Drawing_details"></a> | |
| 399 Drawing details | |
| 400 --------------- | |
| 401 | |
| 402 Certain objects have specific properties that need to be dealt | |
| 403 with. Images, layers (see below), and fonts assume the standard PDF | |
| 404 coordinate system, so we have to undo any flip to the Skia coordinate | |
| 405 system before drawing these entities. We don’t currently support | |
| 406 inverted paths, so filling an inverted path will give the wrong result | |
| 407 ([issue 221](http://skbug.com/221)). PDF doesn’t draw zero length | |
| 408 lines that have butt of square caps, so that is emulated. | |
| 409 | |
| 410 <a name="Layers"></a> | |
| 411 ### Layers ### | |
| 412 | |
| 413 PDF has a higher level object called a form x-object (form external | |
| 414 object) that is basically a PDF page, with resources and a content | |
| 415 stream, but can be transformed and drawn on an existing page. This is | |
| 416 used to implement layers. SkDevice has a method, | |
| 417 createFormXObjectFromDevice, which uses the SkPDFDevice::content() | |
| 418 method to construct a form x-object from the the | |
| 419 device. SkPDFDevice::drawDevice() works by creating a form x-object of | |
| 420 the passed device and then drawing that form x-object in the root | |
| 421 device. There are a couple things to be aware of in this process. As | |
| 422 noted previously, we have to be aware of any flip to the coordinate | |
| 423 system - flipping it an even number of times will lead to the wrong | |
| 424 result unless it is corrected for. The SkClipStack passed to drawing | |
| 425 commands includes the entire clip stack, including the clipping | |
| 426 operations done on the base layer. Since the form x-object will be | |
| 427 drawn as a single operation onto the base layer, we can assume that | |
| 428 all of those clips are in effect and need not apply them within the | |
| 429 layer. | |
| 430 | |
| 431 <a name="Fonts"></a> | |
| 432 ### Fonts ### | |
| 433 | |
| 434 There are many details for dealing with fonts, so this document will | |
| 435 only talk about some of the more important ones. A couple short | |
| 436 details: | |
| 437 | |
| 438 * We can’t assume that an arbitrary font will be available at PDF view | |
| 439 time, so we embed all fonts in accordance with modern PDF | |
| 440 guidelines. | |
| 441 * Most fonts these days are TrueType fonts, so this is where most of | |
| 442 the effort has been concentrated. | |
| 443 * Because Skia may only be given a glyph-id encoding of the text to | |
| 444 render and there is no perfect way to reverse the encoding, the | |
| 445 PDF backend always uses the glyph-id encoding of the text. | |
| 446 | |
| 447 #### *Type1/Type3 fonts* #### | |
| 448 | |
| 449 Linux supports Type1 fonts, but Windows and Mac seem to lack the | |
| 450 functionality required to extract the required information from the | |
| 451 font without parsing the font file. When a non TrueType font is used | |
| 452 any any platform (except for Type1 on Linux), it is encoded as a Type3 | |
| 453 font. In this context, a Type3 font is an array of form x-objects | |
| 454 (content streams) that draw each glyph of the font. No hinting or | |
| 455 kerning information is included in a Type3 font, just the shape of | |
| 456 each glyph. Any font that has the do-not embed copy protection bit set | |
| 457 will also get embedded as a Type3 font. From what I understand, shapes | |
| 458 are not copyrightable, but programs are, so by stripping all the | |
| 459 programmatic information and only embedding the shape of the glyphs we | |
| 460 are honoring the do-not embed bit as much as required by law. | |
| 461 | |
| 462 PDF only supports an 8-bit encoding for Type1 or Type3 fonts. However, | |
| 463 they can contain more than 256 glyphs. The PDF backend handles this by | |
| 464 segmenting the glyphs into groups of 255 (glyph id 0 is always the | |
| 465 unknown glyph) and presenting the font as multiple fonts, each with up | |
| 466 to 255 glyphs. | |
| 467 | |
| 468 #### *Font subsetting* #### | |
| 469 | |
| 470 Many fonts, especially fonts with CJK support are fairly large, so it | |
| 471 is desirable to subset them. Chrome uses the SFNTLY package to provide | |
| 472 subsetting support to Skia for TrueType fonts. However, there is a | |
| 473 conflict between font subsetting and interned objects. If the object | |
| 474 is immutable, how can it be subsetted? This conflict is resolved by | |
| 475 using a substitution mechanism in SkPDFCatalog. Font objects are still | |
| 476 interned, but the interned objects aren’t internally | |
| 477 populated. Subsetting starts while drawing text to an SkPDFDevice; a | |
| 478 bit set indicating which glyphs have been used is maintained. Later, | |
| 479 when SkPDFDocument::emitPDF() is rendering the PDF, it queries each | |
| 480 device (each page) for the set of fonts used and the glyphs used from | |
| 481 each font and combines the information. It then asks the interned | |
| 482 (unpopulated) font objects to create a populated instance with the | |
| 483 calculated subset of the font - this instance is not interned. The | |
| 484 subsetted instance is then set as a substitute for the interned font | |
| 485 object in the SkPDFCatalog. All future references to those fonts | |
| 486 within that document will refer to the subsetted instances, resulting | |
| 487 in a final PDF with exactly one instance of each used font that | |
| 488 includes only the glyphs used. | |
| 489 | |
| 490 The substitution mechanism is a little complicated, but is needed to | |
| 491 support the use case of an SkPDFDevice being added to multiple | |
| 492 documents. If fonts were subsetted in-situ, concurrent PDF generation | |
| 493 would have to be explicitly handled. Instead, by giving each document | |
| 494 its own subsetted instance, there is no need to worry about concurrent | |
| 495 PDF generation. The substitution method is also used to support | |
| 496 optional stream compression. A stream can used by different documents | |
| 497 in both a compressed and uncompressed form, leading to the same | |
| 498 potential difficulties faced by the concurrent font use case. | |
| 499 | |
| 500 <a name="Shaders"></a> | |
| 501 ### Shaders ### | |
| 502 | |
| 503 Skia has two types of predefined shaders, image shaders and gradient | |
| 504 shaders. In both cases, shaders are effectively positioned absolutely, | |
| 505 so the initial position and bounds of where they are visible is part | |
| 506 of the immutable state of the shader object. Each of the Skia’s tile | |
| 507 modes needs to be considered and handled explicitly. The image shader | |
| 508 we generate will be tiled, so tiling is handled by default. To support | |
| 509 mirroring, we draw the image, reversed, on the appropriate axis, or on | |
| 510 both axes plus a fourth in the vacant quadrant. For clamp mode, we | |
| 511 extract the pixels along the appropriate edge and stretch the single | |
| 512 pixel wide/long image to fill the bounds. For both x and y in clamp | |
| 513 mode, we fill the corners with a rectangle of the appropriate | |
| 514 color. The composed shader is then rotated or scaled as appropriate | |
| 515 for the request. | |
| 516 | |
| 517 Gradient shaders are handled purely mathematically. First, the matrix | |
| 518 is transformed so that specific points in the requested gradient are | |
| 519 at pre-defined locations, for example, the linear distance of the | |
| 520 gradient is always normalized to one. Then, a type 4 PDF function is | |
| 521 created that achieves the desired gradient. A type 4 function is a | |
| 522 function defined by a resticted postscript language. The generated | |
| 523 functions clamp at the edges so if the desired tiling mode is tile or | |
| 524 mirror, we hav to add a bit more postscript code to map any input | |
| 525 parameter into the 0-1 range appropriately. The code to generate the | |
| 526 postscript code is somewhat obtuse, since it is trying to generate | |
| 527 optimized (for space) postscript code, but there is a significant | |
| 528 number of comments to explain the intent. | |
| 529 | |
| 530 <a name="Xfer_modes"></a> | |
| 531 ### Xfer modes ### | |
| 532 | |
| 533 PDF supports some of the xfer modes used in Skia directly. For those, | |
| 534 it is simply a matter of setting the blend mode in the graphic state | |
| 535 to the appropriate value (Normal/SrcOver, Multiply, Screen, Overlay, | |
| 536 Darken, Lighten, !ColorDOdge, ColorBurn, HardLight, SoftLight, | |
| 537 Difference, Exclusion). Aside from the standard SrcOver mode, PDF does | |
| 538 not directly support the porter-duff xfer modes though. Most of them | |
| 539 (Clear, SrcMode, DstMode, DstOver, SrcIn, DstIn, SrcOut, DstOut) can | |
| 540 be emulated by various means, mostly by creating form x-objects out of | |
| 541 part of the content and drawing it with a another form x-object as a | |
| 542 mask. I have not figured out how to emulate the following modes: | |
| 543 SrcATop, DstATop, Xor, Plus. | |
| 544 | |
| 545 At the time of writing [2012-06-25], I have a [CL outstanding to fix a | |
| 546 misunderstanding I had about the meaning of some of the emulated | |
| 547 modes](https://codereview.appspot.com/4631078/). | |
| 548 I will describe the system with this change applied. | |
| 549 | |
| 550 First, a bit of terminology and definition. When drawing something | |
| 551 with an emulated xfer mode, what’s already drawn to the device is | |
| 552 called the destination or Dst, and what’s about to be drawn is the | |
| 553 source or Src. Src (and Dst) can have regions where it is transparent | |
| 554 (alpha equals zero), but it also has an inherent shape. For most kinds | |
| 555 of drawn objects, the shape is the same as where alpha is not | |
| 556 zero. However, for things like images and layers, the shape is the | |
| 557 bounds of the item, not where the alpha is non-zero. For example, a | |
| 558 10x10 image, that is transparent except for a 1x1 dot in the center | |
| 559 has a shape that is 10x10. The xfermodes gm test demonstrates the | |
| 560 interaction between shape and alpha in combination with the port-duff | |
| 561 xfer modes. | |
| 562 | |
| 563 The clear xfer mode removes any part of Dst that is within Src’s | |
| 564 shape. This is accomplished by bundling the current content of the | |
| 565 device (Dst) into a single entity and then drawing that with the | |
| 566 inverse of Src’s shape used as a mask (we want Dst where Src | |
| 567 isn’t). The implementation of that takes a couple more steps. You may | |
| 568 have to refer back to [the content stream section](#Generating_a_content_stream) . For any draw call, a | |
| 569 ContentEntry is created through a method called | |
| 570 SkPDFDevice::setUpContentEntry(). This method examines the xfer modes | |
| 571 in effect for that drawing operation and if it is an xfer mode that | |
| 572 needs emulation, it creates a form x-object from the device, | |
| 573 i.e. creates Dst, and stores it away for later use. This also clears | |
| 574 all of that existing ContentEntry's on that device. The drawing | |
| 575 operation is then allowed to proceed as normal (in most cases, see | |
| 576 note about shape below), but into the now empty device. Then, when the | |
| 577 drawing operation in done, a complementary method is | |
| 578 called,SkPDFDevice::finishContentEntry(), which takes action if the | |
| 579 current xfer mode is emulated. In the case of Clear, it packages what | |
| 580 was just drawn into another form x-object, and then uses the Src form | |
| 581 x-object, an invert function, and the Dst form x-object to draw Dst | |
| 582 with the inverse shape of Src as a mask. This works well when the | |
| 583 shape of Src is the same as the opaque part of the drawing, since PDF | |
| 584 uses the alpha channel of the mask form x-object to do masking. When | |
| 585 shape doesn’t match the alpha channel, additional action is | |
| 586 required. The drawing routines where shape and alpha don’t match, set | |
| 587 state to indicate the shape (always rectangular), which | |
| 588 finishContentEntry uses. The clear xfer mode is a special case; if | |
| 589 shape is needed, then Src isn’t used, so there is code to not bother | |
| 590 drawing Src if shape is required and the xfer mode is clear. | |
| 591 | |
| 592 SrcMode is clear plus Src being drawn afterward. DstMode simply omits | |
| 593 drawing Src. DstOver is the same as SrcOver with Src and Dst swapped - | |
| 594 this is accomplished by inserting the new ContentEntry at the | |
| 595 beginning of the list of ContentEntry’s in setUpContentEntry instead | |
| 596 of at the end. SrcIn, SrcOut, DstIn, DstOut are similar to each, the | |
| 597 difference being an inverted or non-inverted mask and swapping Src and | |
| 598 Dst (or not). SrcIn is SrcMode with Src drawn with Dst as a | |
| 599 mask. SrcOut is like SrcMode, but with Src drawn with an inverted Dst | |
| 600 as a mask. DstIn is SrcMode with Dst drawn with Src as a | |
| 601 mask. Finally, DstOut is SrcMode with Dst draw with an inverted Src as | |
| 602 a mask. | |
| 603 | |
| 604 <a name="Known_issues"></a> | |
| 605 Known issues | |
| 606 ------------ | |
| 607 | |
| 608 * [issue 221](http://skbug.com/221), | |
|
jcgregorio
2015/01/14 21:19:11
Have the bugs been reviewed? For example, 221 is m
hal.canary
2015/01/20 18:02:05
Updated.
| |
| 609 [issue 241](http://skbug.com/241) | |
| 610 As previously noted, a boolean geometry library | |
| 611 would improve clip fidelity in some places, add supported for | |
| 612 inverted fill types, as well as simplify code. | |
| 613 * [issue 237](http://skbug.com/237) | |
| 614 SkMaskFilter is not supported. | |
| 615 * [issue 238](http://skbug.com/238) | |
| 616 SkColorFilter is not supported. | |
| 617 * [issue 242](http://skbug.com/242) | |
| 618 perspective transforms are not supported. | |
| 619 * [issue 249](http://skbug.com/249) | |
| 620 SrcAtop DstAtop, Xor, and Plus xfer modes are not supported. | |
| 621 * [issue 240](http://skbug.com/240) | |
| 622 drawVerticies is not implemented. | |
| 623 * [issue 236](http://skbug.com/236) | |
| 624 SkPDFImage doesn’t unpremultiply colors. | |
| 625 * [issue 248](http://skbug.com/248) | |
| 626 Linearized PDFs are not generated, though support for them | |
| 627 is built in. | |
| 628 * [issue 244](http://skbug.com/244) | |
| 629 Mostly, only TTF fonts are directly supported. | |
| 630 * [issue 260](http://skbug.com/260) | |
| 631 Page rotation is accomplished by specifying a different | |
| 632 size page instead of including the appropriate rotation | |
| 633 annotation. | |
| 634 | |
| 635 * * * | |
| 636 | |
| OLD | NEW |