SVG - The SVG (Scalable Vector Graphics) table (OpenType 1.8.1)
OpenType fonts with either TrueType or CFF outlines may also contain an optional 'SVG ' table, which allows some or all glyphs in the font to be defined with color, gradients, or animation.
This table contains SVG descriptions for some or all of the glyphs in the font. For every SVG glyph description, there must also exist a corresponding CFF or TT glyph description in the font.
SVG Main Header
Type | Name | Description |
---|---|---|
uint16 | version | Table version (starting at 0). Set to 0. |
Offset32 | offsetToSVGDocIndex | Offset (relative to the start of the SVG table) to the SVG Documents Index. Must be non-zero. |
uint32 | reserved | Set to 0. |
SVG Document Index
The SVG Document Index is a set of SVG documents, each of which defines one or more glyph descriptions.
Type | Name | Description |
---|---|---|
uint16 | numEntries | Number of SVG Document Index Entries. Must be non-zero. |
SVG Document Index Entry | entries[numEntries] | Array of SVG Document Index Entries. |
SVG Document Index Entry
Each SVG Document Index Entry specifies a range [startGlyphID, endGlyphID], inclusive, of glyph IDs and the location of its associated SVG document in the SVG table.
Type | Name | Description |
---|---|---|
uint16 | startGlyphID | The first glyph ID in the range described by this index entry. |
uint16 | endGlyphID | The last glyph ID in the range described by this index entry. Must be >= startGlyphID. |
Offset32 | svgDocOffset | Offset from the beginning of the SVG Document Index to an SVG document. Must be non-zero. |
uint32 | svgDocLength | Length of the SVG document. Must be non-zero. |
Index entries must be arranged in order of increasing startGlyphID. The startGlyphID of an index entry must be greater than the endGlyphID of the previous index entry, if any.
While SVG 1.1 requires in addition to plain text encoding that conforming SVG implementations must correctly support gzip-encoded [RFC1952] and deflate-encoded [RFC1951] data streams, this specification requires that the SVG documents be either plain-text or gzip-encoded [RFC1952]. The encoding of the (uncompressed) SVG document must be UTF-8. In both cases, svgDocLength encodes the length of the encoded data, not the decoded document.
For further details about the content of the SVG documents, see “Glyph Identifiers” and the following sections below.
Color Palettes
The SVG glyph descriptions may contain color variables whose values are obtained either from one of the various color palettes in the Color Palette (CPAL) table or by other means, for example values specified by the user. The first color palette shall be the default one. It is strongly recommended that the default values for the color variables in the SVG documents be set to the same values as in the first color palette table, for implementations that may not support color palettes.
Color variables are made available for use in the SVG glyph descriptions by the font engine setting CSS custom properties in a User Agent style sheet. The custom property names are of the form “--color<num>”, where <num> is a parameter index in the range [0, numPaletteEntries-1], inclusive, expressed as a non-zero- padded decimal number. numPaletteEntries is defined in the CPAL table. See the “Glyph rendering” section below for exactly how the values are to be passed in to the SVG document.
Font engines that support the SVG table and color palettes are strongly suggested to implement the CSS Custom Properties for Cascading Variables specification, as this is required for the palette entries to be passed into the SVG document.
Note that the SVG glyph descriptions are able to express their own explicit or “hard-coded” colors as well. These are not related to color variables and thus do not vary by palette selection. For example, a font designer may want the teardrop on a crying emoji always to be blue (this is “hard-coded”) but the rest of the emoji regulated by color variables, with the skin of the face having a default value of the classic “smiley face” yellow (default both in the SVG glyph description itself — see the var(--color0, yellow) example below — and in the default color palette).
Glyph Identifiers
For each glyph ID in an SVG Document Index Entry’s [startGlyphID, endGlyphID] range, inclusive, the associated SVG document must contain an element with id “glyph<glyphID>”, where <glyphID> is the glyph ID expressed as a non-zero-padded decimal value. This element functions as the SVG glyph description for the glyph ID.
For example, say a font with maxp.numGlyphs=100 has SVG glyph definitions only for its last 5 glyphs. The last SVG glyph definition has its own SVG document, but the rest share an SVG document (say, to take advantage of shared graphical elements). There will be two index entries, the first with glyph ID range [95, 98] and the second with glyph ID range [99, 99]. The SVG document referenced by the first index entry will contain elements with id “glyph95”, “glyph96”, “glyph97”, and “glyph98”. The SVG document referenced by the second index entry will contain an element with id “glyph99”.
Glyph identifiers may appear deep within the SVG hierarchy, but SVG itself does not define how partial SVG documents are to be rendered. Thus, font engines shall render glyph identifiers according to SVG’s <use> tag behavior, as though the body of the SVG document were wrapped in a <defs> tag. For example, consider the following SVG document, which defines two glyphs:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <defs>...</defs> <g id="glyph13">...</g> <g id="glyph14">...</g> </svg>
When a font engine renders glyph 14, the result shall be the same as rendering the following SVG document:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <defs> <defs>...</defs> <g id="glyph13">...</g> <g id="glyph14">...</g> </defs> <use xlink:href="#glyph14" /> </svg>
Glyph Semantics and Metrics
The glyph descriptions in the SVG documents are considered to be the SVG versions of the glyphs with the corresponding IDs in the CFF or glyf table. They are designed on an em specified in the head table’s unitsPerEm field, as with CFF and TrueType glyphs. SVG glyph definitions will be in SVG’s own y-down coordinate system, upright, with the default baseline at y=0. For example, the top of a capital letter may be at y=-800, and the bottom at y=0 (see Examples section below). It is the font engine’s responsibility to translate this to the coordinate system of the rest of the OT tables and the coordinate system of the graphics environment.
Glyph semantics are expressed in the usual way (cmap table followed by GSUB). Glyph metrics such as horizontal and vertical advances are specified in the OpenType metrics tables (hmtx and vmtx), and glyph positioning adjustments by the GPOS or kern table.
As with CFF glyphs, no explicit glyph bounding boxes are recorded. The “ink” bounding box of the rendered SVG glyph should be used if a bounding box is desired; this box may be different for animated vs static renderings of the glyph.
Note that the glyph advances are static and not able to be made variable or animated.
Glyph Rendering
The SVG glyph descriptions may be rendered statically or with animation enabled. Note that static rendering is done by not running any animations in the SVG document; this is different from running the document with animations running but at a snapshot time of zero seconds. Some clients may choose not to support — or may not be able to support — animation. Clients that support animation may still request, in certain cases, that the glyph be rendered statically, e.g. for printing to paper.
The font engine must apply the following user agent style sheet (or implement its functional equivalent) to the SVG documents processed from the SVG table:
@namespace svg url(http://www.w3.org/2000/svg); svg|text, svg|foreignObject { display: none !important; }
In addition, if the font engine supports color palettes, and color palette values are provided, the user agent style sheet must include CSS Custom Property declarations for the color variables. This is done by including ‘numPaletteEntries‘ (defined in the CPAL table) declarations in the :root rule of the form:
--color<num>: <color>
where <num> is each of the values from zero to numPaletteEntries-1, inclusive, expressed as a non-zero-padded decimal number; and <color> is the <num> index within the desired Color Palette, expressed in SVG’s <color> format. An example entry in the style sheet is:
--color0: rgba(255,0,0, 0.5);
and the corresponding usage in an SVG glyph description could be something like:
<path fill="var(--color0, yellow)" d="..."/>
where ‘yellow’ defines a default color to be used when the color0 variable is not defined.
Note that SVG’s context-fill value may be used in the glyph descriptions to denote the current text color.
The font engine must support at least version 1.1 of the SVG specification (exceptions are noted in the section on glyph rendering restrictions). The version attribute in the <svg> element is present in the SVG 1.1 and 1.2 specifications, but not in SVG 2. Thus, the SVG document may not always have a version field specified. Given this approach to versioning in SVG, and given that not all implementations may support all of SVG (whether 1.1 or 2), font designers should restrict their SVG, as a practical matter, to whatever is supported by SVG-in-OT implementations they care about. Targeting the capabilities of SVG 1.1 would be the approach most likely to result in cross-implementation consistency.
The following new values for any CSS property that takes an SVG paint value MUST be supported:
- context-fill
- context-stroke
These values mean the same paint as the computed value of the ‘fill’ or ‘stroke’ property, respectively, of the context element, which is the element in the outer document that is using the SVG glyphs. If the referenced paint is a gradient or a pattern, then the coordinate space to use and the object used for any ‘objectBoundingBox’-relative values are the same as those of the context element.
The following new values for the ‘fill-opacity’, ‘stroke-opacity’ and ‘opacity’ CSS properties MUST be supported:
- context-fill-opacity
- context-stroke-opacity
These values mean the same as the computed value of the ‘fill-opacity’ or ‘stroke-opacity’ property, respectively, of the context element.
The following new value for the ‘stroke-width’, ‘stroke-dasharray’ and ‘stroke-dashoffset’ CSS properties MUST be supported:
- context-value
This value means the same as the computed value of the corresponding property of the context element, but scaled so that it has the same size when used in the coordinate system of the root <svg> element of the SVG glyph document. For example, if the context element has ‘stroke-width’ set to 2px and the SVG glyph document is rendered with a coordinate system such that 2048 user units corresponds to 16px in the context element’s coordinate space, then using context-value for ‘stroke-width’ in the glyph definition will have the same visual effect as using 256 user units.
Font engines that support SVG glyphs are strongly suggested to implement the context-fill, context-fill-opacity, context-stroke, context-stroke-opacity and context-value property values according to the definitions found in SVG 2, as these definitions may be more precise than those described in this document above.
Security considerations and other glyph rendering restrictions
Processing of SVG glyph documents MUST be done with script execution, external references and interactivity disabled. If the font engine is rendering SVG glyphs with animation, then declarative animations MUST be enabled; if it is rendering glyphs statically, then declarative animations MUST be disabled.
These requirements correspond to the “secure animated” and “secure static” processing modes that the SVG Integration document requires font documents to be run in.
In addition, any SVG <text> and <foreignObject> elements within a glyph description must be ignored and not rendered (see the corresponding rules in the User Agent style sheet above).
Text Layout Process
An implementation that supports the SVG table first does layout in the usual manner, using the cmap, GSUB, hmtx, and other OpenType layout tables. This results in a list of glyph IDs arranged at particular x,y positions on the surface (along with the appropriate scale/rotation matrices). At this point, for each such glyph ID, if an SVG glyph description is available for it, it is rendered (in static or animated mode, as appropriate and if supported by the engine); otherwise, the CFF or TT glyph description must be rendered. Since the glyph advances are the same in either case, and not allowed to be animated, switching between SVG and CFF/TT rendering, or between animated and static SVG, should not require re-layout of lines (unless line layout depends on ink bounding boxes).
SVG Glyph Examples
SVG glyph descriptions must be defined in SVG’s own y-down coordinate system, upright, with the default baseline at y=0. It is always the font engine’s responsibility to translate this into the coordinate system of the rest of the OpenType font rendering environment.
The SVG code in these examples is presented exactly as could be used in the SVG documents of an OpenType font with SVG glyph outlines. The code is not optimized for brevity.
Example: Glyph specified directly in expected position
<svg id="glyph7" version="1.1" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" stop-color="darkblue" stop-opacity="1" /> <stop offset="100%" stop-color="#00aab3" stop-opacity="1" /> </linearGradient> </defs> <rect x="100" y="-430" width="200" height="430" fill="url(#grad)" /> <rect x="100" y="-635" width="200" height="135" fill="darkblue" /> </svg>
In this example, the letter “i” is drawn directly in the +x -y quadrant of the SVG coordinate system, upright, with its baseline on the x axis, exactly where the OpenType font engine expects it to be.
Example: Glyph shifted up with viewBox
<svg id="glyph7" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 1000 1000 1000"> <defs> <linearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" stop-color="darkblue" stop-opacity="1" /> <stop offset="100%" stop-color="#00aab3" stop-opacity="1" /> </linearGradient> </defs> <rect x="100" y="570" width="200" height="430" fill="url(#grad)" /> <rect x="100" y="365" width="200" height="135" fill="darkblue" /> </svg>
In this example, the glyph description of the letter “i” is first specified in the +x +y quadrant of the SVG coordinate system, upright, with its baseline along y=1000 in the SVG coordinate system. (This may be the natural way SVG illustrating software positioned it.) A viewBox in the <svg> element is then used to shift it upwards by 1000 units, to end up in the position where the OpenType font engine expects it to be.
The diagram is the same as in the above example.
Example: Common elements shared across glyphs in same SVG doc
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" stop-color="darkblue" stop-opacity="1" /> <stop offset="100%" stop-color="#00aab3" stop-opacity="1" /> </linearGradient> <g id="i-base"> <rect x="100" y="570" width="200" height="430" fill="url(#grad)" /> </g> </defs> <g id="glyph2" transform="translate(0,-1000)"> <use xlink:href="#i-base" /> </g> <g id="glyph13" transform="translate(0,-1000)"> <use xlink:href="#i-base" /> <rect x="100" y="365" width="200" height="135" fill="darkblue" /> </g> <g id="glyph14" transform="translate(0,-1000)"> <use xlink:href="#i-base" /> <polygon fill="darkblue" points="120,500 280,500 435,342 208,342"/> </g> </svg>
In this example, the base of the letter ‘i’ is shared across three glyphs, and has identifier “i-base” in the <defs> section. It represents the dotless ‘i’ in glyph ID 2. Glyph ID 13 adds a dot on top. Glyph ID 14 adds an acute accent on top. The diagram above shows glyph IDs 2, 13, and 14, from left to right.
Note that glyph IDs 3–12 can be defined in one or more separate SVG docs, and still allow glyph IDs 2, 13, and 14 to share the same SVG doc. For example:
SVG Document Index: numEntries=5 … entries[2]: { startGlyphID = 2, endGlyphID = 2, svgDocOffset/Length point to svgDoc0 } entries[3]: { startGlyphID = 3, endGlyphID = 12, svgDocOffset/Length point to svgDoc1 } entries[4]: { startGlyphID = 13, endGlyphID = 14, svgDocOffset/Length point to svgDoc0 }
Example: Specifying current text color in glyphs
<svg id="glyph7" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 1000 1000 1000"> <defs> <linearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" stop-color="darkblue" stop-opacity="1" /> <stop offset="100%" stop-color="#00aab3" stop-opacity="1" /> </linearGradient> </defs> <rect x="100" y="570" width="200" height="430" fill="url(#grad)" /> <rect x="100" y="365" width="200" height="135" fill="context-fill" /> </svg>
Here the “darkblue” color of the dot above the “i” in the “Glyph shifted up with viewBox” example is replaced by “context-fill”. The diagram above shows the glyph when the fill color of the context element (i.e. the text color) is set to black (left) and red (right).
Example: Specifying color palette variables in glyphs
<svg id="glyph7" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 1000 1000 1000"> <defs> <linearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" stop-color="var(--color0,darkblue)" stop-opacity="1" /> <stop offset="100%" stop-color="var(--color1,#00aab3)" stop-opacity="1" /> </linearGradient> </defs> <rect x="100" y="570" width="200" height="430" fill="url(#grad)" /> <rect x="100" y="365" width="200" height="135" fill="darkblue" /> </svg>
This example is the duplicate of the “Glyph shifted up with viewBox” example, but with the stop colors of the linear gradient controlled by color variables --color0 and --color1, which are provided by the font engine to the SVG renderer via a user agent style sheet (or its functional equivalent).
The color palettes (CPAL) table in this font specifies two palettes, each with two color entries. Here is a description of the CPAL palettes, with alpha assumed to be 0xFF for all colors:
palette[0]: { darkblue, #00aab3 } palette[1]: { purple, orchid }
The first item in the diagram above shows the first color palette applied to the glyph, which is done by the font engine passing the following user agent style sheet to the SVG renderer:
:root { --color0: darkblue; --color1: #00aab3; }
The second item in the diagram shows the second color palette applied to the glyph, using the style sheet:
:root { --color0: purple; --color1: orchid; }
Note that the dot is still dark blue, since this is hard coded in the glyph description and not controlled by a color variable.
The last item in the diagram shows the following user-selected colors applied to the glyph via the color variables:
:root { --color0: red; --color1: orange; }
If --color0 and --color1 aren’t defined by the font engine, however, then the default values provided in the stop-colors (darkblue and #00aab3, respectively) are used. Note that these are in fact the same colors as in the first (default) CPAL color palette, which means the glyph will render as in the first item in the diagram. This way, the glyph renders with the same colors by default, whether or not the font engine supports the CPAL.
Example: Embedding a PNG in an SVG glyph
<svg id="glyph2" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 1000 1000 1000"> <image x="100" y="365" width="200" height="635" xlink:href="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAMgAAAJ7CAYAAACmmd5sAAAFZklEQVR42u3XsQ3D MBAEQUpw9ypahrMPGGwiwcFMCQQW9zzWuu4FbJ2eAAQCAgGBgEBAICAQEAgIBAQC CAQEAgIBgYBAQCAgEBAICAQEAggEBAICAYGAQEAgIBAQCAgEEAgIBAQCAgGBgEBA ICAQEAgIBBAICAQEAgIBgYBAQCAgEBAIIBAQCAgEBAICAYGAQEAgIBAQCCAQEAgI BAQCAgGBgEBAICAQQCAgEBAICAQEAgIBgYBAQCAgEEAgIBAQCAgEBAICAYGAQEAg IBBPAAIBgYBAQCAgEBAICAQEAgIBBAICAYGAQEAgIBAQCAgEBAICAQQCAgGBgEBA ICAQEAgIBAQCCAQEAgIBgYBAQCAgEBAICAQEAggEBAICAYGAQEAgIBAQCAgEAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA4DHHWtftGWDv80sE2Ds9AQgEBAL+IPBuIAoBJxYIBAQCPukgEHBigUBAIOAP AlgQiAtiQsCCgEDAJx0sCFgQsCAgEHBigQUB5oKYELAgIBDwSQcLAhYELAgIBJxY YEEACwItEIWAEwucWGBBwIKABQGBgBMLLAhYEMCCQFwQEwJOLHBigQUBCwICAScW WBCwIGBBAIFAPbHcWGBBwCcdLAgIBJxYYEHAgoAFAYEA88RyY4EFAZ90sCAgEBAI +IOAQMCJBQIBBALxD+ITAj7p4MQCgYBAwB8EBAJOLBAICATwB4EYiELAiQUCAYGA TzoIBJxYIBAQCPiDABYE4oKYELAgIBDwSQcLAhYELAgIBJxYYEGAuSAmBCwICAR8 0sGCgAUBCwICAScWWBDAgkALRCHgxAInFlgQsCBgQUAg4MQCCwIWBLAgEBfEhIAT C5xYYEHAgoBAwIkFFgQsCFgQQCBQTyw3FlgQ8EkHCwICAScWWBCwIGBBQCDAPLHc WGBBwCcdLAgIBAQC/iAgEHBigUAAgUD8g/iEgE86OLFAICAQ8AcBgYATCwQCAgH8 QSAGohBwYoFAQCDgkw4CAScWCAQEAv4ggAWBuCAmBCwICAR80sGCgAUBCwICAScW WBBgLogJAQsCAgGfdLAgYEHAgoBAwIkFFgSwINACUQg4scCJBRYELAhYEBAIOLHA goAFASwIxAUxIeDEAicWWBCwICAQcGKBBQELAhYEEAjUE8uNBRYEfNLBgoBAwIkF FgQsCFgQEAgwTyw3FlgQ8EkHCwICAYGAPwgIBJxYIBBAIBD/ID4h4JMOTiwQCAgE /EFAIODEAoGAQAB/EIiBKAScWCAQEAj4pINAwIkFAgGBgD8IYEEgLogJAQsCAgGf dLAgYEHAgoBAwIkFFgSYC2JCwIKAQMAnHSwIWBCwICAQcGKBBQEsCLRAFAJOLHBi gQUBCwIWBAQCTiywIGBBAAsCcUFMCDixwIkFFgQsCAgEnFhgQcCCgAUBBAL1xHJj gQUBn3SwICAQcGKBBQELAhYEBALME8uNBRYEfNLBgoBAQCDgDwICAScWCAQQCMQ/ iE8I+KSDEwsEAgIBfxAQCDixQCAgEMAfBGIgCgEnFggEBAI+6SAQcGKBQEAg4A8C WBCIC2JCwIKAQMAnHSwIWBCwICAQcGKBBQHmgpgQsCAgEPBJBwsCFgQsCAgEnFhg QQALAi0QhYATC5xYYEHAgoAFAYGAEwssCFgQwIJAXBATAk4scGKBBQELAgIBJxZY ELAgYEEAgUA9sdxYYEHAJx0sCAgEnFhgQcCCgAUBgQDzxHJjgQUBn3SwICAQEAj4 g4BAwIkFAgEEAvEP4hMCPungxAKBgEDgH3wBrUwJtCBGuc0AAAAASUVORK5CYII= "/> </svg>
In this example, the PNG is embedded using SVG’s <image> element. The use case for this is bitmap lettering artwork that needs to be packaged into an OT-SVG font.