Source File
harfbuzz.go
Belonging Package
github.com/go-text/typesetting/harfbuzz
// Package harfbuzz provides advanced text layout for various scripts and languages,// with font-aware substitutions and positionning.//// Given a font and an input specified as runes, the package shapes this input// and returns a slice of positionned glyphs, identified by their index in the font.// See `Buffer` and its methods for more details.//// This package is a direct port of the C/C++ library.package harfbuzzimport (ot)// based on upstream commit 5a31dd02f0b32de12336f72db9297bfe94cf0da1 (v12.3.0)// debugMode is only used in test: if true, it prints detailed information// about shapingconst debugMode = falsetype (GID = ot.GIDgID = tables.GlyphID)// Direction is the text direction.// The zero value is the initial, unset, invalid direction.type Direction uint8const (LeftToRight Direction = 4 + iota // Text is set horizontally from left to right.RightToLeft // Text is set horizontally from right to left.TopToBottom // Text is set vertically from top to bottom.BottomToTop // Text is set vertically from bottom to top.)// Fetches the `Direction` of a script when it is// set horizontally. All right-to-left scripts will return// `RightToLeft`. All left-to-right scripts will return// `LeftToRight`. Scripts that can be written either// horizontally or vertically will return `Invalid`.// Unknown scripts will return `LeftToRight`.func getHorizontalDirection( language.Script) Direction {/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */switch {case language.Arabic, language.Hebrew, language.Syriac, language.Thaana,language.Cypriot, language.Kharoshthi, language.Phoenician, language.Nko, language.Lydian,language.Avestan, language.Imperial_Aramaic, language.Inscriptional_Pahlavi, language.Inscriptional_Parthian, language.Old_South_Arabian, language.Old_Turkic,language.Samaritan, language.Mandaic, language.Meroitic_Cursive, language.Meroitic_Hieroglyphs, language.Manichaean, language.Mende_Kikakui,language.Nabataean, language.Old_North_Arabian, language.Palmyrene, language.Psalter_Pahlavi, language.Hatran, language.Adlam, language.Hanifi_Rohingya,language.Old_Sogdian, language.Sogdian, language.Elymaic, language.Chorasmian, language.Yezidi:return RightToLeft/* https://github.com/harfbuzz/harfbuzz/issues/1000 */case language.Old_Hungarian, language.Old_Italic, language.Runic, language.Tifinagh:return 0}return LeftToRight}// Tests whether a text direction is horizontal. Requires// that the direction be valid.func ( Direction) () bool { return & ^Direction(1) == 4 }// Tests whether a text direction is vertical. Requires// that the direction be valid.func ( Direction) () bool { return & ^Direction(1) == 6 }// Tests whether a text direction moves backward (from right to left, or from// bottom to top). Requires that the direction be valid.func ( Direction) () bool { return & ^Direction(2) == 5 }// Tests whether a text direction moves forward (from left to right, or from// top to bottom). Requires that the direction be valid.func ( Direction) () bool { return & ^Direction(2) == 4 }// Reverses a text direction. Requires that the direction// is valid.func ( Direction) () Direction {return ^ 1}// SegmentProperties holds various text properties of a `Buffer`.type SegmentProperties struct {// Languages are crucial for selecting which OpenType feature to apply to the// buffer which can result in applying language-specific behaviour. Languages// are orthogonal to the scripts, and though they are related, they are// different concepts and should not be confused with each other.Language language.Language// Script is crucial for choosing the proper shaping behaviour for scripts that// require it (e.g. Arabic) and the OpenType features defined in the font// to be applied.//// See the package language for predefined values.Script language.Script// Direction is the text flow direction of the buffer. No shaping can happen without// setting direction, and it controls the visual direction for the// output glyphs; for RTL direction the glyphs will be reversed. Many layout// features depend on the proper setting of the direction, for example,// reversing RTL text before shaping, then shaping with LTR direction is not// the same as keeping the text in logical order and shaping with RTL// direction.Direction Direction}// ShappingOptions controls some fine tunning of the shaping// (see the constants).type ShappingOptions uint16const (// Flag indicating that special handling of the beginning// of text paragraph can be applied to this buffer. Should usually// be set, unless you are passing to the buffer only part// of the text without the full context.Bot ShappingOptions = 1 << iota// Flag indicating that special handling of the end of text// paragraph can be applied to this buffer, similar to// `Bot`.Eot// Flag indication that character with Default_Ignorable// Unicode property should use the corresponding glyph// from the font, instead of hiding them (done by// replacing them with the space glyph and zeroing the// advance width.) This flag takes precedence over// `RemoveDefaultIgnorables`.PreserveDefaultIgnorables// Flag indication that character with Default_Ignorable// Unicode property should be removed from glyph string// instead of hiding them (done by replacing them with the// space glyph and zeroing the advance width.)// `PreserveDefaultIgnorables` takes// precedence over this flag.RemoveDefaultIgnorables// Flag indicating that a dotted circle should// not be inserted in the rendering of incorrect// character sequences (such at <0905 093E>).DoNotinsertDottedCircle// Flag indicating that the [GlyphUnsafeToConcat]// glyph-flag should be produced by the shaper. By default// it will not be produced since it incurs a cost.ProduceUnsafeToConcat// Flag indicating that the [GlyphSafeToInsertTatweel]// glyph-flag should be produced by the shaper. By default// it will not be produced.ProduceSafeToInsertTatweel)// ClusterLevel allows selecting more fine-grained Cluster handling.// It defaults to `MonotoneGraphemes`.type ClusterLevel uint8const (// Return cluster values grouped by graphemes into monotone order.// Non-base characters are merged into the cluster of the base character that precedes them.// There is also cluster merging every time the clusters will otherwise become non-monotone.MonotoneGraphemes ClusterLevel = iota// Return cluster values grouped into monotone order.// Non-base characters are initially// assigned their own cluster values, which are not merged into preceding base// clusters. This allows HarfBuzz to perform additional operations like reorder// sequences of adjacent marks. The output is still monotone, but the cluster// values are more granular.MonotoneCharacters// Don't group cluster values.// Non-base characters are assigned their// own cluster values, which are not merged into preceding base clusters. Moreover,// the cluster values are not merged into monotone order. This is the most granular// cluster level, and it is useful for clients that need to know the exact cluster// values of each character, but is harder to use for clients, since clusters// might appear in any order.Characters)func ( ClusterLevel) () string {switch {case MonotoneCharacters:return "MonotoneCharacters"case MonotoneGraphemes:return "MonotoneGraphemes"case Characters:return "Characters"default:return fmt.Sprintf("<unknown cluster level: %d>", )}}// Feature holds information about requested// feature application. The feature will be applied with the given value to all// glyphs which are in clusters between `start` (inclusive) and `end` (exclusive).// Setting start to `FeatureGlobalStart` and end to `FeatureGlobalEnd`// specifies that the feature always applies to the entire buffer.type Feature struct {Tag ot.Tag// Value of the feature: 0 disables the feature, non-zero (usually// 1) enables the feature. For features implemented as lookup type 3 (like// 'salt') `Value` is a one-based index into the alternates.Value uint32// The cluster to Start applying this feature setting (inclusive)Start int// The cluster to End applying this feature setting (exclusive)End int}const (// Special setting for `Feature.Start` to apply the feature from the start// of the buffer.FeatureGlobalStart = 0// Special setting for `Feature.End` to apply the feature from to the end// of the buffer.FeatureGlobalEnd = maxInt)// ParseVariation parse the string representation of a variation// of the form tag=valuefunc ( string) (font.Variation, error) {:= parser{data: []byte()}return .parseOneVariation()}type parser struct {data []bytepos int}func isSpace( byte) bool {return == ' ' || == '\f' || == '\n' || == '\r' || == '\t' || == '\v'}func ( *parser) () {for .pos < len(.data) && isSpace(.data[.pos]) {.pos++}}// return true if `c` was foundfunc ( *parser) ( byte) bool {.skipSpaces()if .pos == len(.data) || .data[.pos] != {return false}.pos++return true}func ( *parser) () (uint32, bool) {:= .pos// go to the next spacefor .pos < len(.data) && isAlnum(.data[.pos]) {.pos++}, := strconv.Atoi(string(.data[:.pos]))return uint32(), == nil}func ( *parser) () (uint32, bool) {.skipSpaces():= .posfor .pos < len(.data) && isAlpha(.data[.pos]) {.pos++}:= string(.data[:.pos])/* CSS allows on/off as aliases 1/0. */if == "on" {return 1, true} else if == "off" {return 0, true} else {return 0, false}}func ( *parser) () (ot.Tag, error) {.skipSpaces()var byteif .pos < len(.data) && (.data[.pos] == '\'' || .data[.pos] == '"') {= .data[.pos].pos++}:= .posfor .pos < len(.data) && (.data[.pos] != ' ' && .data[.pos] != '=' && .data[.pos] != '[' && .data[.pos] != ) {.pos++}if .pos == || .pos > +4 {return 0, errors.New("invalid tag length")}// padd with space if necessary, since MustNewTag requires 4 bytes:= [4]byte{' ', ' ', ' ', ' '}copy([:], .data[:.pos]):= ot.MustNewTag(string([:]))if != 0 {/* CSS expects exactly four bytes. And we only allow quotations for* CSS compatibility. So, enforce the length. */if .pos != +4 {return 0, errors.New("tag must have 4 bytes")}if .pos == len(.data) || .data[.pos] != {return 0, errors.New("tag is missing end quote")}.pos++}return , nil}func ( *parser) () (float32, error) {.parseChar('=') // Optional.:= .pos// go to the next spacefor .pos < len(.data) && !isSpace(.data[.pos]) {.pos++}, := strconv.ParseFloat(string(.data[:.pos]), 32)return float32(),}func ( *parser) () ( font.Variation, error) {.Tag, = .parseTag()if != nil {return}.Value, = .parseVariationValue()if != nil {return}.skipSpaces()return}func ( *parser) () (, int, error) {.skipSpaces(), = FeatureGlobalStart, FeatureGlobalEndif !.parseChar('[') {return , , nil}, := .parseUint32()= int()if .parseChar(':') || .parseChar(';') {if , := .parseUint32(); {= int()}} else {if {= + 1}}if !.parseChar(']') {return 0, 0, errors.New("expecting closing bracked after feature indices")}return , , nil}// return true if a value was specifiedfunc ( *parser) () (uint32, bool) {/* CSS doesn't use equal-sign between tag and value.* If there was an equal-sign, then there *must* be a value.* A value without an equal-sign is ok, but not required. */.parseChar('='):= .pos, := .parseUint32()if ! { // retry for a bool.pos =, = .parseBool()}return ,}func ( *parser) () uint32 {if .parseChar('-') {return 0} else {_ = .parseChar('+')return 1}}func ( *parser) () ( Feature, error) {.Value = .parseFeatureValuePrefix().Tag, = .parseTag()if != nil {return ,}.Start, .End, = .parseFeatureIndices()if != nil {return ,}if , := .parseFeatureValuePostfix(); {.Value =}.skipSpaces()return , nil}// ParseFeature parses one feature string (usually coming from a comma-separated list of font features).//// Features can be enabled or disabled, either globally or limited to// specific character ranges. The format for specifying feature settings// follows. All valid CSS font-feature-settings values other than 'normal'// and the global values are also accepted, though not documented below.// CSS string escapes are not supported.//// The range indices refer to the positions between Unicode characters,// unless the --utf8-clusters is provided, in which case range indices// refer to UTF-8 byte indices. The position before the first character// is always 0.//// The format is Python-esque. Here is how it all works://// Syntax: Value: Start: End://// Setting value:// "kern" 1 0 ∞ // Turn feature on// "+kern" 1 0 ∞ // Turn feature on// "-kern" 0 0 ∞ // Turn feature off// "kern=0" 0 0 ∞ // Turn feature off// "kern=1" 1 0 ∞ // Turn feature on// "aalt=2" 2 0 ∞ // Choose 2nd alternate//// Setting index:// "kern[]" 1 0 ∞ // Turn feature on// "kern[:]" 1 0 ∞ // Turn feature on// "kern[5:]" 1 5 ∞ // Turn feature on, partial// "kern[:5]" 1 0 5 // Turn feature on, partial// "kern[3:5]" 1 3 5 // Turn feature on, range// "kern[3]" 1 3 3+1 // Turn feature on, single char//// Mixing it all://// "aalt[3:5]=2" 2 3 5 // Turn 2nd alternate on for rangefunc ( string) (Feature, error) {:= parser{data: []byte()}return .parseOneFeature()}func min(, int) int {if < {return}return}func min8(, uint8) uint8 {if < {return}return}func max(, int) int {if > {return}return}func max32(, uint32) uint32 {if > {return}return}func minF(, float32) float32 {if < {return}return}func maxF(, float32) float32 {if > {return}return}func isAlpha( byte) bool { return ( >= 'a' && <= 'z') || ( >= 'A' && <= 'Z') }func isAlnum( byte) bool { return isAlpha() || ( >= '0' && <= '9') }func toUpper( byte) byte {if >= 'a' && <= 'z' {return - 'a' + 'A'}return}func toLower( byte) byte {if >= 'A' && <= 'Z' {return - 'A' + 'a'}return}const maxInt = int(^uint(0) >> 1)// bitStorage returns the number of bits needed to store the number.func bitStorage( uint32) int { return 32 - bits.LeadingZeros32() }func roundf( float32) Position { return Position(math.Round(float64())) }
![]() |
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. |