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