coff

package module
v0.0.0-...-51d21d0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 12, 2024 License: Artistic-2.0 Imports: 6 Imported by: 1

README

Common Outline Font Format

Common Outline Font Format: coff for short.

coff grew out of glox by considering its relationship to tx from AFDKO:

The narrow view of glox is that it extracts glyph outlines and converts them to SVG, a reasonably widespread and viewable format.

The broader view of glox is that it is a glyph exchange that can convert from and to many formats, using a common outline font format. The exchange part of this name comes from the tx tool, short for type exchange. tx is in the AFDKO toolkit and does many to many format conversions using ABF (Abstract Font Format) as an intermediate.

There are some crude notes on the UFO format, and some thoughts about CFF.

coff is also a Go package.

sources and targets

A source is something that can be used to generate coff (by extracting it from a font format for example).

A target is something that coff be converted to.

  • SVG, a target (glox) and a source (limited, ttf8)

  • TTF, a source (glox) and a target (limited, ttf8)

  • OTF, a source (glox)

  • UFO, not implemented; priority source and target

  • PDF, not implemented; useful target (following tx)

SVG

SVG is a large spec, too large to be implemented in its entirety.

An SVG target should be sufficient to reasonably display all glyphs for the purposes of debugging and proofing. A secondary goal would be to support downstream tools (such as kerning previewers or hinters). One idea is to match the structure of the SVG output of hb-view --output-format=svg, so that hb-view can be used to prototype tests and downstream tools.

An SVG source is not required to be completely general. Currently there are 2 important SVG sources: one, the vec8 tool that produces exact traces of PNG files; two, the hand-made SVG documents that are the source art for the font Ranalina.

Adding the SVG output of hb-view as an SVG source would be useful; there are two immediates uses: one, a second pathway for interpreting TTF/OTF font files; two, realising a particular shaping/rendering of a font (optical adjustments, hinting, variation space instantiation).

Another extension for SVG would be to support conversion of SVG sources to multi-coloured glyphs, essentially adding support for the OpenType COLR and CPAL tables.

Also note that OpenType also has the SVG table for incorporating SVG as-is. It's not clear if it would be useful to do such a conversion using coff. Probably not as coff is used to represent a fairly restricted notion of outline and the purpose of the OpenType SVG table is to support SVG in all its richness.

SVG and ttf8

The current primary consumer of SVG is ttf8. Here i outline the steps that ttf8 currently uses.

This informs the coff glyph model, which currently doesn't exist. But ttf8 has one.

  • One SVG group is one glyph
  • every path has a class. This class may be empty. The class is either the SVG/XML class or derived from the SVG/XML id.
  • class is used to sort path elements into baseline, anchors (for both base-to-mark positioning and kerning), and contours (empty class).
  • baseline is used to define a translate transform that brings bottom left of baseline to [0 0]
  • baseline is used to set the sides
  • the glyph em (a global default that can be overridden on a per-glyph basis; and is for the builtin .notdef) and a upem (a single value for the target) define a scale transform.
  • paths are converted to coff contours then transformed
  • coff contours are grid fitted by coff.ContoursTTAdjust()
  • kerning pairs and mark/base anchors are computed.

SVG path elements are converted to COFF contours early on, then geometry data is extracted, contours are transformed, and glyph position data is compiled.

Notes:

  • the paths have an order in the SVG which is preserved, but is not relevant to the processing. This seems like a good thing, because the same is approximately true for OTF and TTF and is relevant to Variable Font processing. The order should not be whimsically changed.
  • path to contour correspondence is destroyed. In SVG one path can have multiple contours, and a group can have multiple path elements. When converting to coff, a simple linear list of contours is produced. (this affects the visual semantics. In SVG, paths are complete objects that are composed by overprinting, thus a contour in one path cannot make a hole in another path.
  • the path to contour (non-) correspondence means that a single contour is not (necessarily) associated with a single SVG id. One SVG path has one id but can have several contour.
  • notionally anchors are single points, but for convenience of drawing in SVG (in Inkscape) they appear in the SVG as lines. The bottom left position is used.

Realisation

outline.go in this repo is a partial realisation of this idea.

The primitive, Contour, consists of a sequence of points, with each point having a field, OnCurve, that specifies whether the point is an on-curve point or not.

The interpretation of Contour is variable.

A normalised contour has every point represented. Line segments will therefore consist of two adjacent on-curve points with no points in between. A quadratic curve will have exactly one off-curve points between two on-curve points. A cubic curve will have exactly two off-curve points between two on-curve points.

In principle higher-order Beziér curves can be represented by having more off-curve control points between on-curve points, but this is not supported (by the functions that manipulate Contour).

Sources such as TrueType may, as an intermediate, use Contour in non-normal form. For example implicit on-curve points may be omitted from the Contour. Using such a Contour with normal functions is ambiguous, so such contours should always be normalised first (which will be a source-specific operation).

END

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IntegerBounds

func IntegerBounds(min, max Point) image.Rectangle

Bound (float64) min and max Point to (integer) image.Rectangle

func MakeScale

func MakeScale(sx, sy float64) f64.Aff3

Return a matric for a scale (in x and y).

func MakeTranslate

func MakeTranslate(tx, ty float64) f64.Aff3

Return a matrix for a translation.

func PointToImageCeil

func PointToImageCeil(p Point) image.Point

Convert to an image.Point, rounding using ceil.

func PointToImageExact

func PointToImageExact(p Point) image.Point

Convert to an image.Point, fatal if the conversion cannot be made exactly. :todo: change to propagate error to caller.

func PointToImageFloor

func PointToImageFloor(p Point) image.Point

Convert to an image.Point, rounding using floor when an exact conversion cannot be made. This and the following PointToImageCeil are intended for min/max bounds conversions.

func TransformInverse

func TransformInverse(A f64.Aff3) f64.Aff3

Return the result U where U A == identify Mostly following https://math.stackexchange.com/questions/397111/how-to-find-3-x-3-matrix-inverses

func TransformMultiply

func TransformMultiply(L, R f64.Aff3) f64.Aff3

Return the result L×R. When the result product is applied to a column-vector point, the effect is first applying R then L.

Types

type Component

type Component struct {
	// Both GlyphIndex and Base are ways to refer to
	// the component, but only one should be used.
	// When building from SVG, Base is used first (glyphs
	// have no glyph index number), then compiled as a number.
	// When building from TTF or similar, only GlyphIndex is used.
	GlyphIndex int
	Base       string
	// The affine transformation.
	// By (obscure) convention Aff3 is a 3x3 matrix in
	// row-major order, where the bottom [0 0 1] row is implicit.
	M f64.Aff3
}

A component of a composite glyph. This is a model of a _reference_ to a component. The outlines of the component have to be fetched separately.

func MakeComponentAtXY

func MakeComponentAtXY(x, y float64, base string) Component

func (*Component) SetMatrix

func (component *Component) SetMatrix(flags uint16, arg1, arg2 int, scale []int16)

The arguments are TrueType specific.

func (*Component) Transform

func (component *Component) Transform(p Point) Point

Transform a single point

func (*Component) TransformBounds

func (component *Component) TransformBounds(b image.Rectangle) image.Rectangle

Transform a bounding rectangle (by the component's transform).

func (*Component) TransformLeftMultiply

func (c *Component) TransformLeftMultiply(factor f64.Aff3) *Component

type Contour

type Contour struct {
	P []ContourPoint
	// In UFO a contour can have a unique identifier;
	// in COFF a name.
	Name string
}

Models a single contour in a glyph.

func ContoursTTAdjust

func ContoursTTAdjust(cs []Contour) []Contour

When contours are intended for a TrueType target: Adjust the contours to the integer grid. The returned contours are completely fresh. :todo: a later version will cleverly compute implicit points that may lie on the half-grid.

func ContoursTransform

func ContoursTransform(cs []Contour, m f64.Aff3) []Contour

Transform all the points of all the contours in the slice by the specified transformation. The returned contours are completely fresh.

func ExpandImplicitTTContours

func ExpandImplicitTTContours(cs []Contour) []Contour

When contours have been sourced from TrueType: Expand the contours by adding the implicit on-curve points. In TrueType outline fonts, the low-level point representation allows a space-saving optimisation where an on-curve point can be eliminated when it is exactly between two off-curve points. The existence of these implicit points can be inferred from two-adjacent off-curve points. This function adds them back in.

type ContourPoint

type ContourPoint struct {
	X, Y    float64
	OnCurve bool
}

Although in TrueType all points are given in integer coordinates, implicit on-curve points are exactly halfway between their neighbours, introducing the possibility that those points can have fractional (.5) coordinates. Which is why we use float. Also, CFF support 16.16 fixed point (a slightly obscure fact).

type Outline

type Outline struct {
	Components []Component
	Contours   []Contour
}

Model a glyph in outline format. An Outline is _simple_ when components is empty. An Outline is _composite_ when components is non-empty. A composite Outline has no contours (at least when sourced from TrueType). A simple Outline _may_ have no contours, and this is usual for the space glyphs.

func (*Outline) Bounds

func (o *Outline) Bounds() image.Rectangle

Somewhat crude bounds. It includes the off-curve control points in the bounds, making it possibly conservative. Though for the conventional contour descriptions for fonts it will make no difference, because such descriptions routinely include the cardinal extremal points.

func (*Outline) Close

func (o *Outline) Close() *Outline

Close each contour by ensuring that the end point is the same as the start point. The outline is both mutated and returned.

func (*Outline) Flatten

func (o *Outline) Flatten(oer Outliner) *Outline

Flatten an outline by fully resolving it to one with only contours (so no components). Traverses into all the nested components and applies all the appropriate transforms. Outliner fetches an Outline given a component.

func (*Outline) IsSimple

func (o *Outline) IsSimple() bool

func (Outline) String

func (o Outline) String() string

type Outliner

type Outliner interface {
	GetOutline(i int) *Outline
}

type Point

type Point struct {
	X, Y float64
}

a 2D point in floating point coordinates.

func ContoursBounds

func ContoursBounds(contours []Contour) (min, max Point)

Return bounds as (floating point) min (bottom left) and max (top right) points.

func PointTransform

func PointTransform(p Point, m f64.Aff3) Point

Transform a single point

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL