shaping

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2024 License: BSD-3-Clause, Unlicense Imports: 12 Imported by: 18

README

shaping

This text shaping library is shared by multiple Go UI toolkits including Fyne, and GIO.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bounds

type Bounds struct {
	// Ascent is the maximum ascent away from the baseline. This value is typically
	// positive in coordiate systems that grow up.
	Ascent fixed.Int26_6
	// Descent is the maximum descent away from the baseline. This value is typically
	// negative in coordinate systems that grow up.
	Descent fixed.Int26_6
	// Gap is the height of empty pixels between lines. This value is typically positive
	// in coordinate systems that grow up.
	Gap fixed.Int26_6
}

Bounds describes the minor-axis bounds of a line of text. In a LTR or RTL layout, it describes the vertical axis. In a BTT or TTB layout, it describes the horizontal.

For horizontal text:

  • Ascent GLYPH | GLYPH | GLYPH | GLYPH | GLYPH
  • Baseline GLYPH | GLYPH | GLYPH | GLYPH
  • Descent GLYPH |
  • Gap

For vertical text:

Descent ------- Baseline --------------- Ascent --- Gap
	|				| 						|		|
	GLYPH		  GLYPH					GLYPH
		GLYPH GLYPH		GLYPH GLYPH GLYPH

func (Bounds) LineThickness

func (b Bounds) LineThickness() fixed.Int26_6

LineThickness returns the thickness of a line of text described by b, that is its height for horizontal text, its width for vertical text.

type FontFeature

type FontFeature struct {
	Tag   loader.Tag
	Value uint32
}

FontFeature sets one font feature.

A font feature is an optionnal behavior a font might expose, identified by a 4 bytes [Tag]. Most features are disabled by default; setting a non zero [Value] enables it.

An exemple of font feature is the replacement of fractions (like 1/2, 3/4) by specialized glyphs, which would be activated by using

FontFeature{Tag: loader.MustNewTag("frac"), Value: 1}

See also https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist and https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/OpenType_fonts_guide

type Fontmap

type Fontmap interface {
	// ResolveFace is called by `SplitByFace` for each input rune potentially
	// triggering a face change.
	// It must always return a valid (non nil) font.Face value.
	ResolveFace(r rune) font.Face
}

Fontmap provides a general mechanism to select a face to use when shaping text.

type Glyph

type Glyph struct {
	// Width is the width of the glyph content,
	// expressed as a distance from the [XBearing],
	// typically positive
	Width fixed.Int26_6
	// Height is the height of the glyph content,
	// expressed as a distance from the [YBearing],
	// typically negative
	Height fixed.Int26_6
	// XBearing is the distance between the dot (with offset applied) and
	// the glyph content, typically positive for horizontal text;
	// often negative for vertical text.
	XBearing fixed.Int26_6
	// YBearing is the distance between the dot (with offset applied) and
	// the top of the glyph content, typically positive
	YBearing fixed.Int26_6
	// XAdvance is the distance between the current dot (without offset applied) and the next dot.
	// It is typically positive for horizontal text, and always zero for vertical text.
	XAdvance fixed.Int26_6
	// YAdvance is the distance between the current dot (without offset applied) and the next dot.
	// It is typically negative for vertical text, and always zero for horizontal text.
	YAdvance fixed.Int26_6

	// Offsets to be applied to the dot before actually drawing
	// the glyph.
	// For vertical text, YOffset is typically used to position the glyph
	// below the horizontal line at the dot
	XOffset, YOffset fixed.Int26_6

	// ClusterIndex is the lowest rune index of all runes shaped into
	// this glyph cluster. All glyphs sharing the same cluster value
	// are part of the same cluster and will have identical RuneCount
	// and GlyphCount fields.
	ClusterIndex int
	// RuneCount is the number of input runes shaped into this output
	// glyph cluster.
	RuneCount int
	// GlyphCount is the number of glyphs in this output glyph cluster.
	GlyphCount int
	GlyphID    font.GID
	Mask       font.GlyphMask
}

Glyph describes the attributes of a single glyph from a single font face in a shaped output.

func (Glyph) LeftSideBearing

func (g Glyph) LeftSideBearing() fixed.Int26_6

LeftSideBearing returns the distance from the glyph's X origin to its leftmost edge. This value can be negative if the glyph extends across the origin.

func (Glyph) RightSideBearing

func (g Glyph) RightSideBearing() fixed.Int26_6

RightSideBearing returns the distance from the glyph's right edge to the edge of the glyph's advance. This value can be negative if the glyph's right edge is after the end of its advance.

type HarfbuzzShaper

type HarfbuzzShaper struct {
	// contains filtered or unexported fields
}

HarfbuzzShaper implements the Shaper interface using harfbuzz. Reusing this shaper type across multiple shaping operations is faster and more memory-efficient than creating a new shaper for each operation.

func (*HarfbuzzShaper) SetFontCacheSize

func (h *HarfbuzzShaper) SetFontCacheSize(size int)

SetFontCacheSize adjusts the size of the font cache within the shaper. It is safe to adjust the size after using the shaper, though shrinking it may result in many evictions on the next shaping.

func (*HarfbuzzShaper) Shape

func (t *HarfbuzzShaper) Shape(input Input) Output

Shape turns an input into an output.

type Input

type Input struct {
	// Text is the body of text being shaped. Only the range Text[RunStart:RunEnd] is considered
	// for shaping, with the rest provided as context for the shaper. This helps with, for example,
	// cross-run Arabic shaping or handling combining marks at the start of a run.
	Text []rune
	// RunStart and RunEnd indicate the subslice of Text being shaped.
	RunStart, RunEnd int
	// Direction is the directionality of the text.
	Direction di.Direction
	// Face is the font face to render the text in.
	Face font.Face

	// FontFeatures activates or deactivates optional features
	// provided by the font.
	// The settings are applied to the whole [Text].
	FontFeatures []FontFeature

	// Size is the requested size of the font.
	// More generally, it is a scale factor applied to the resulting metrics.
	// For instance, given a device resolution (in dpi) and a point size (like 14), the `Size` to
	// get result in pixels is given by : pointSize * dpi / 72
	Size fixed.Int26_6

	// Script is an identifier for the writing system used in the text.
	Script language.Script

	// Language is an identifier for the language of the text.
	Language language.Language
}

func SplitByFace

func SplitByFace(input Input, availableFaces Fontmap) []Input

SplitByFace split the runes from 'input' to several items, sharing the same characteristics as 'input', expected for the `Face` which is set to the return value of the `Fontmap.ResolveFace` call. The 'Face' field of 'input' is ignored: only 'availableFaces' is used to select the face.

func SplitByFontGlyphs

func SplitByFontGlyphs(input Input, availableFaces []font.Face) []Input

SplitByFontGlyphs split the runes from 'input' to several items, sharing the same characteristics as 'input', expected for the `Face` which is set to the first font among 'availableFonts' providing support for all the runes in the item. Runes supported by no fonts are mapped to the first element of 'availableFonts', which must not be empty. The 'Face' field of 'input' is ignored: only 'availableFaces' are consulted. Rune coverage is obtained by calling the NominalGlyph() method of each font. See also SplitByFace for a more general approach of font selection.

type Line

type Line []Output

Line holds runs of shaped text wrapped onto a single line. All the contained Output should be displayed sequentially on one line.

func (Line) AdjustBaselines

func (l Line) AdjustBaselines()

AdjustBaselines aligns runs with different baselines.

For vertical text, it centralizes 'sideways' runs, so that text with mixed 'upright' and 'sideways' orientation is better aligned.

This is currently a no-op for horizontal text.

Note that this method only update cross-axis metrics, so that the advance is preserved. As such, it is valid to call this method after line wrapping, for instance.

type LineBreakPolicy

type LineBreakPolicy uint8

LineBreakPolicy specifies when considering a line break within a "word" or UAX#14 segment is allowed.

const (
	// WhenNecessary means that lines will only be broken within words when the word
	// cannot fit on the next line by itself or during truncation to preserve as much
	// of the final word as possible.
	WhenNecessary LineBreakPolicy = iota
	// Never means that words will never be broken internally, allowing them to exceed
	// the specified maxWidth.
	Never
	// Always means that lines will always choose to break within words if it means that
	// more text can fit on the line.
	Always
)

func (LineBreakPolicy) String

func (l LineBreakPolicy) String() string

type LineWrapper

type LineWrapper struct {
	// contains filtered or unexported fields
}

LineWrapper holds reusable state for a line wrapping operation. Reusing LineWrappers for multiple paragraphs should improve performance.

func (*LineWrapper) Prepare

func (l *LineWrapper) Prepare(config WrapConfig, paragraph []rune, runs RunIterator)

Prepare initializes the LineWrapper for the given paragraph and shaped text. It must be called prior to invoking WrapNextLine. Prepare invalidates any lines previously returned by this wrapper.

func (*LineWrapper) WrapNextLine

func (l *LineWrapper) WrapNextLine(maxWidth int) (out WrappedLine, done bool)

WrapNextLine wraps the shaped glyphs of a paragraph to a particular max width. It is meant to be called iteratively to wrap each line, allowing lines to be wrapped to different widths within the same paragraph. When done is true, subsequent calls to WrapNextLine (without calling Prepare) will return a nil line.

The returned line is only valid until the next call to *LineWrapper.Prepare or *LineWrapper.WrapParagraph.

func (*LineWrapper) WrapParagraph

func (l *LineWrapper) WrapParagraph(config WrapConfig, maxWidth int, paragraph []rune, runs RunIterator) (_ []Line, truncated int)

WrapParagraph wraps the paragraph's shaped glyphs to a constant maxWidth. It is equivalent to iteratively invoking WrapLine with a constant maxWidth. If the config has a non-zero TruncateAfterLines, WrapParagraph will return at most that many lines. The truncated return value is the count of runes truncated from the end of the text. The returned lines are only valid until the next call to *LineWrapper.WrapParagraph or *LineWrapper.Prepare.

type Output

type Output struct {
	// Advance is the distance the Dot has advanced.
	// It is typically positive for horizontal text, negative for vertical.
	Advance fixed.Int26_6
	// Size is copied from the shaping.Input.Size that produced this Output.
	Size fixed.Int26_6
	// Glyphs are the shaped output text.
	Glyphs []Glyph
	// LineBounds describes the font's suggested line bounding dimensions. The
	// dimensions described should contain any glyphs from the given font.
	LineBounds Bounds
	// GlyphBounds describes a tight bounding box on the specific glyphs contained
	// within this output. The dimensions may not be sufficient to contain all
	// glyphs within the chosen font.
	//
	// Its [Gap] field is always zero.
	GlyphBounds Bounds

	// Direction is the direction used to shape the text,
	// as provided in the Input.
	Direction di.Direction

	// Runes describes the runes this output represents from the input text.
	Runes Range

	// Face is the font face that this output is rendered in. This is needed in
	// the output in order to render each run in a multi-font sequence in the
	// correct font.
	Face font.Face
}

Output describes the dimensions and content of shaped text.

func (*Output) FromFontUnit

func (o *Output) FromFontUnit(v float32) fixed.Int26_6

FromFontUnit converts an unscaled font value to the current [Size]

func (*Output) RecalculateAll

func (o *Output) RecalculateAll()

RecalculateAll updates the all other fields of the Output to match the current contents of the Glyphs field. This method will fail with UnimplementedDirectionError if the Output direction is unimplemented.

func (*Output) RecomputeAdvance

func (o *Output) RecomputeAdvance()

RecomputeAdvance updates only the Advance field based on the current contents of the Glyphs field. It is faster than RecalculateAll(), and can be used to speed up line wrapping logic.

func (*Output) ToFontUnit

func (o *Output) ToFontUnit(v fixed.Int26_6) float32

ToFontUnit converts a metrics (typically found in Glyph fields) to unscaled font units.

type Range

type Range struct {
	Offset int
	Count  int
}

Range indicates the location of a sequence of elements within a longer slice.

type RunIterator

type RunIterator interface {
	// Next returns the next run in the iterator, if any. If there is a next run,
	// its index, content, and true will be returned, and the iterator will advance
	// to the following element. Otherwise Next returns an undefined index, an empty
	// Output, and false.
	Next() (index int, run Output, isValid bool)
	// Peek returns the same thing Next() would, but does not advance the iterator (so the
	// next call to Next() will return the same thing).
	Peek() (index int, run Output, isValid bool)
	// Save marks the current iterator position such that the iterator can return to it later
	// when Restore() is called. Only one position may be saved at a time, with subsequent
	// calls to Save() overriding the current value.
	Save()
	// Restore resets the iteration state to the most recently Save()-ed position.
	Restore()
}

RunIterator defines a type that can incrementally provide shaped text.

func NewSliceIterator

func NewSliceIterator(outs []Output) RunIterator

NewSliceIterator returns a RunIterator backed by an already-shaped slice of [Output]s.

type Segmenter

type Segmenter struct {
	// contains filtered or unexported fields
}

Segmenter holds a state used to split input according to three caracteristics : text direction (bidi), script, and face.

func (*Segmenter) Split

func (seg *Segmenter) Split(text Input, faces Fontmap) []Input

Split segments the given pre-configured input according to:

  • text direction
  • script
  • (vertical text only) glyph orientation
  • face, as defined by [faces]

Only the input runes in the range [text.RunStart] to [text.RunEnd] will be split.

As a consequence, it sets the following fields of the returned runs:

  • Text, RunStart, RunEnd
  • Direction
  • Script
  • Face

[text.Direction] is used during bidi ordering, and should refer to the general context [text] is used in (typically the user system preference for GUI apps.)

For vertical text, if its orientation is set, is copied as it is; otherwise, the orientation is resolved using the Unicode recommendations (see https://www.unicode.org/reports/tr50/).

The returned sliced is owned by the Segmenter and is only valid until the next call to [Split].

type Shaper

type Shaper interface {
	// Shape takes an Input and shapes it into the Output.
	Shape(Input) Output
}

Shaper describes the signature of a font shaping operation.

type WrapConfig

type WrapConfig struct {
	// TruncateAfterLines is the number of lines of text to allow before truncating
	// the text. A value of zero means no limit.
	TruncateAfterLines int
	// Truncator, if provided, will be inserted at the end of a truncated line. This
	// feature is only active if TruncateAfterLines is nonzero.
	Truncator Output
	// TextContinues indicates that the paragraph wrapped by this config is not the
	// final paragraph in the text. This alters text truncation when filling the
	// final line permitted by TruncateAfterLines. If the text of this paragraph
	// does fit entirely on TruncateAfterLines, normally the truncator symbol would
	// not be inserted. However, if the overall body of text continues beyond this
	// paragraph (indicated by TextContinues), the truncator should still be inserted
	// to indicate that further paragraphs of text were truncated. This field has
	// no effect if TruncateAfterLines is zero.
	TextContinues bool
	// BreakPolicy determines under what circumstances the wrapper will consider
	// breaking in between UAX#14 line breaking candidates, or "within words" in
	// many scripts.
	BreakPolicy LineBreakPolicy
}

WrapConfig provides line-wrapper settings.

func (WrapConfig) WithTruncator

func (w WrapConfig) WithTruncator(shaper Shaper, input Input) WrapConfig

WithTruncator returns a copy of WrapConfig with the Truncator field set to the result of shaping input with shaper.

type WrappedLine

type WrappedLine struct {
	// Line is the content of the line, as a slice of shaped runs
	Line Line
	// Truncated is the count of runes truncated from the end of the line,
	// if this line was truncated.
	Truncated int
	// NextLine is the indice (in the input text slice) of the begining
	// of the next line. It will equal len(text) if all the text
	// fit in one line.
	NextLine int
}

WrappedLine is the result of wrapping one line of text.

Jump to

Keyboard shortcuts

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