Source File
output.go
Belonging Package
github.com/go-text/typesetting/shaping
// SPDX-License-Identifier: Unlicense OR BSD-3-Clausepackage shapingimport ()// Glyph describes the attributes of a single glyph from a single// font face in a shaped output.type Glyph struct {// Width is the width of the glyph content,// expressed as a distance from the [XBearing],// typically positiveWidth fixed.Int26_6// Height is the height of the glyph content,// expressed as a distance from the [YBearing],// typically negativeHeight 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 positiveYBearing fixed.Int26_6// Advance is the distance between the current dot (without offset applied) and the next dot.// It is typically positive for horizontal text, and negative for vertical text.Advance 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.//// Deprecated: Use Advance instead.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.//// Deprecated: Use Advance instead.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 dotXOffset, 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.//// Deprecated: Use TextIndex() instead.ClusterIndex int// RuneCount is the number of input runes shaped into this output// glyph cluster.//// Deprecated: Use RunesCount() instead.RuneCount int// GlyphCount is the number of glyphs in this output glyph cluster.//// Deprecated: Use GlyphsCount() instead.GlyphCount intGlyphID font.GIDMask uint32// startLetterSpacing and endLetterSpacing are set when letter spacing is applied,// measuring the whitespace added on one side (half of the user provided letter spacing)// The line wrapper will ignore [endLetterSpacing] when deciding where to break,// and will trim [startLetterSpacing] at the start of the linesstartLetterSpacing, endLetterSpacing fixed.Int26_6}// TextIndex 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 RunesCount// and GlyphsCount values.func ( Glyph) () int { return .ClusterIndex }// RuneCount is the number of input runes shaped into this output// glyph cluster.func ( Glyph) () int { return .RuneCount }// GlyphsCount is the number of glyphs in this output glyph cluster.func ( Glyph) () int { return .GlyphCount }// 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) () fixed.Int26_6 {return .XBearing}// 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.func ( Glyph) () fixed.Int26_6 {return .Advance - .Width - .XBearing}// 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 GLYPHtype 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}// LineThickness returns the thickness of a line of text described by b,// that is its height for horizontal text, its width for vertical text.func ( Bounds) () fixed.Int26_6 {return .Ascent - .Descent + .Gap}// Output describes the dimensions and content of shaped text.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// VisualIndex is the visual position of this run within its containing line where// 0 indicates the leftmost run and increasing values move to the right. This is// useful for sorting the runs for drawing purposes.VisualIndex int32}// ToFontUnit converts a metrics (typically found in [Glyph] fields)// to unscaled font units.func ( *Output) ( fixed.Int26_6) float32 {return float32() / float32(.Size) * float32(.Face.Upem())}// FromFontUnit converts an unscaled font value to the current [Size]func ( *Output) ( float32) fixed.Int26_6 {return fixed.Int26_6( * float32(.Size) / float32(.Face.Upem()))}// 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) () {:= fixed.Int26_6(0)for , := range .Glyphs {+= .Advance}.Advance =}// advanceSpaceAware adjust the value in [Advance]// if a white space character ends the run.// Any end letter spacing (on the last glyph) is also removed// The paragraphDir is the text direction of the overall paragraph containing o.// If the paragraphDir is different then o's Direction, this method has no effect// because the trailing space in this run will always be internal to the paragraph.//// TODO: should we take into account multiple spaces ?func ( *Output) ( di.Direction) fixed.Int26_6 {:= len(.Glyphs)if == 0 || != .Direction {return .Advance}// adjust the last to account for spacesvar Glyphif .Direction.Progression() == di.FromTopLeft {= .Glyphs[-1]} else {= .Glyphs[0]}if .Direction.IsVertical() {if .Height == 0 {return .Advance - .Advance}} else { // horizontalif .Width == 0 {return .Advance - .Advance}}return .Advance - .endLetterSpacing}// 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) () {var (fixed.Int26_6fixed.Int26_6fixed.Int26_6)if .Direction.IsVertical() {for := range .Glyphs {:= &.Glyphs[]+= .Advance:= .XOffset + .XBearing // start of the glyphif < {=}:= + .Width // end of the glyphif > {=}}} else { // horizontalfor := range .Glyphs {:= &.Glyphs[]+= .Advance:= .YBearing + .YOffsetif > {=}:= + .Heightif < {=}}}.Advance =.GlyphBounds = Bounds{Ascent: ,Descent: ,}}// Assuming [Glyphs] comes from an horizontal shaping,// applies a 90°, clockwise rotation to the whole slice of glyphs,// to create 'sideways' vertical text.//// The [Direction] field is updated by switching the axis to vertical// and the orientation to "sideways".//// [RecalculateAll] should be called afterwards to update [Avance] and [GlyphBounds].func ( *Output) () {for , := range .Glyphs {// switch height and width.Glyphs[].Width = -.Height // height is negative.Glyphs[].Height = -.Width// compute the bearings.Glyphs[].XBearing = .YBearing + .Height.Glyphs[].YBearing = .Width// switch advance direction.Glyphs[].Advance = -.Advance // Advance for vertical text is negative// apply a rotation around the dot, and position the glyph// below the dot.Glyphs[].XOffset = .YOffset.Glyphs[].YOffset = -(.XOffset + .XBearing + .Width)}// adjust direction.Direction.SetSideways(true)}// properly update [GlyphBounds]func ( *Output) ( fixed.Int26_6) {if .Direction.IsVertical() {for := range .Glyphs {.Glyphs[].XOffset +=}} else {for := range .Glyphs {.Glyphs[].YOffset +=}}.GlyphBounds.Ascent +=.GlyphBounds.Descent +=}// 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.func ( Line) () {if len() == 0 {return}:= [0]if .Direction.Axis() == di.Horizontal {return}// Centralize sideways runs, to better align// with upright ones, which are usually visually centered.// We want to shift all the runs by the same amount, to// avoid breaking alignment of similar runs (consider "A あ is a pretty char.")var Boundsfor , := range {if !.Direction.IsSideways() {continue}if := .GlyphBounds.Ascent; > .Ascent {.Ascent =}if := .GlyphBounds.Descent; < .Descent {.Descent =}}// Place the middle of sideways run at the baseline (the zero):= .Descent + .LineThickness()/2for := range {if ![].Direction.IsSideways() {continue}[].moveCrossAxis(-)}}
![]() |
The pages are generated with Golds v0.8.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |