From 1cd9336ea26bb1bb7998009a477547349bee7985 Mon Sep 17 00:00:00 2001 From: Benoit KUGLER Date: Wed, 3 Apr 2024 11:36:48 +0200 Subject: [PATCH] [language] Use a constructor to force correct canonication --- fontscan/fontmap_test.go | 7 +- fontscan/langset.go | 9 +- fontscan/langset_gen.go | 4 +- fontscan/substitutions_table.go | 154 +++++++++++++-------------- harfbuzz/buffer.go | 2 +- harfbuzz/buffer_test.go | 4 +- harfbuzz/fonts_test.go | 2 +- harfbuzz/harbuzz_test.go | 10 +- harfbuzz/ot_aat_map.go | 2 +- harfbuzz/ot_language_table.go | 160 ++++++++++++++--------------- harfbuzz/ot_tag.go | 10 +- harfbuzz/ot_tag_test.go | 8 +- language/language.go | 42 ++++---- language/language_test.go | 99 +++++++++--------- opentype/tables/aat_common_test.go | 2 +- shaping/input_test.go | 4 +- shaping/wrapping_test.go | 106 +++++++++---------- 17 files changed, 316 insertions(+), 309 deletions(-) diff --git a/fontscan/fontmap_test.go b/fontscan/fontmap_test.go index a294ea6f..42902de5 100644 --- a/fontscan/fontmap_test.go +++ b/fontscan/fontmap_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/go-text/typesetting/font" + "github.com/go-text/typesetting/language" fontapi "github.com/go-text/typesetting/opentype/api/font" meta "github.com/go-text/typesetting/opentype/api/metadata" "github.com/go-text/typesetting/opentype/loader" @@ -63,7 +64,7 @@ func ExampleFontMap_AddFace() { var _ shaping.Fontmap = (*FontMap)(nil) func TestResolveFont(t *testing.T) { - en, _ := NewLangID("en") + en, _ := NewLangID(language.NewLanguage("en")) var logOutput bytes.Buffer logger := log.New(&logOutput, "", 0) @@ -115,7 +116,7 @@ func TestResolveForLang(t *testing.T) { fm.SetQuery(Query{Families: []string{"helvetica"}}) // all system fonts should have support for english - en, _ := NewLangID("en") + en, _ := NewLangID(language.NewLanguage("en")) face := fm.ResolveFaceForLang(en) tu.AssertC(t, face != nil, "expected EN to be supported by system fonts") } @@ -141,7 +142,7 @@ func TestResolveFallbackManual(t *testing.T) { face := fm.ResolveFace('c') tu.Assert(t, fm.FontLocation(face.Font).File == "user:Amiri") - en, _ := NewLangID("en") + en, _ := NewLangID(language.NewLanguage("en")) face = fm.ResolveFaceForLang(en) tu.Assert(t, face != nil && fm.FontLocation(face.Font).File == "user:Amiri") } diff --git a/fontscan/langset.go b/fontscan/langset.go index 2ad7fbb8..86d9698b 100644 --- a/fontscan/langset.go +++ b/fontscan/langset.go @@ -18,15 +18,16 @@ type LangID uint16 // Derived languages not exactly supported are mapped to their primary part : for instance, // 'fr-be' is mapped to 'fr' func NewLangID(l language.Language) (LangID, bool) { + la := l.String() const N = len(languagesRunes) // binary search i, j := 0, N for i < j { h := i + (j-i)/2 entry := languagesRunes[h] - if l < entry.lang { + if la < entry.lang { j = h - } else if entry.lang < l { + } else if entry.lang < la { i = h + 1 } else { // extact match @@ -35,7 +36,7 @@ func NewLangID(l language.Language) (LangID, bool) { } // i is the index where l should be : // try to match the primary part - root := l.Primary() + root := l.Primary().String() for ; i >= 0; i-- { entry := languagesRunes[i] if entry.lang > root { // keep going @@ -76,7 +77,7 @@ func (ls LangSet) String() string { for bit := 0; bit < 64; bit++ { if page&(1<--... -type Language string +type Language struct { + lang string +} // NewLanguage canonicalizes the language input (as a BCP 47 language tag), by converting it to // lowercase, mapping '_' to '-', and stripping all characters other @@ -36,14 +38,17 @@ func NewLanguage(language string) Language { out = append(out, can) } } - return Language(out) + return Language{string(out)} } +// String returns the canonicalized BCP 47 representation of the language. +func (l Language) String() string { return l.lang } + // Primary returns the root language of l, that is // the part before the first '-' separator func (l Language) Primary() Language { - if index := strings.IndexByte(string(l), '-'); index != -1 { - l = l[:index] + if index := strings.IndexByte(l.lang, '-'); index != -1 { + return Language{l.lang[:index]} } return l } @@ -52,10 +57,10 @@ func (l Language) Primary() Language { // The resulting slice starts with the given whole language. // See http://www.unicode.org/reports/tr35/#Locale_Inheritance for more information. func (l Language) SimpleInheritance() []Language { - tags := strings.Split(string(l), "-") + tags := strings.Split(l.lang, "-") out := make([]Language, 0, len(tags)) for len(tags) != 0 { - out = append(out, Language(strings.Join(tags, "-"))) + out = append(out, Language{strings.Join(tags, "-")}) tags = tags[:len(tags)-1] } return out @@ -67,7 +72,7 @@ func (l Language) IsDerivedFrom(root Language) bool { return l.Primary() == root // IsUndetermined returns `true` if its primary language is "und". // It is a shortcut for IsDerivedFrom("und"). -func (l Language) IsUndetermined() bool { return l.IsDerivedFrom("und") } +func (l Language) IsUndetermined() bool { return l.IsDerivedFrom(Language{"und"}) } // SplitExtensionTags splits the language at the extension and private-use subtags, which are // marked by a "--" pattern. @@ -75,28 +80,29 @@ func (l Language) IsUndetermined() bool { return l.IsDerivedFrom("und") } // // (l, "") is returned if the language has no extension or private-use tag. func (l Language) SplitExtensionTags() (prefix, private Language) { - if len(l) >= 2 && l[0] == 'x' && l[1] == '-' { // x-<....> 'fully' private - return "", l + la := l.lang + if len(la) >= 2 && la[0] == 'x' && la[1] == '-' { // x-<....> 'fully' private + return Language{""}, l } firstExtension := -1 - for i := 0; i+3 < len(l); i++ { - if l[i] == '-' && l[i+2] == '-' { + for i := 0; i+3 < len(la); i++ { + if la[i] == '-' && la[i+2] == '-' { if firstExtension == -1 { // mark the end of the prefix firstExtension = i } - if l[i+1] == 'x' { // private-use tag - return l[:firstExtension], l[i+1:] + if la[i+1] == 'x' { // private-use tag + return Language{la[:firstExtension]}, Language{la[i+1:]} } // else keep looking for private sub tags } } if firstExtension == -1 { - return l, "" + return l, Language{""} } - return l[:firstExtension], "" + return Language{la[:firstExtension]}, Language{""} } // LanguageComparison is a three state enum resulting from comparing two languages @@ -123,7 +129,7 @@ func (l Language) Compare(other Language) LanguageComparison { } // check for the undetermined special case - if primary1 == "und" { + if primary1.lang == "und" { return LanguagesDiffer } return LanguagePrimaryMatch @@ -154,5 +160,5 @@ func DefaultLanguage() Language { return languageFromLocale(p) } - return "" + return Language{""} } diff --git a/language/language_test.go b/language/language_test.go index cad69306..2c9c2641 100644 --- a/language/language_test.go +++ b/language/language_test.go @@ -19,7 +19,7 @@ func TestNonASCIILanguage(t *testing.T) { func TestSimpleInheritance(t *testing.T) { l := NewLanguage("en_US_someVariant") - if sh := l.SimpleInheritance(); !reflect.DeepEqual(sh, []Language{l, "en-us", "en"}) { + if sh := l.SimpleInheritance(); !reflect.DeepEqual(sh, []Language{l, {"en-us"}, {"en"}}) { t.Fatalf("unexpected inheritance %v", sh) } @@ -30,36 +30,33 @@ func TestSimpleInheritance(t *testing.T) { } func TestLanguage_IsDerivedFrom(t *testing.T) { - type args struct { - root Language - } tests := []struct { name string l Language - args args + root Language want bool }{ { name: "", - l: "fr-FR", - args: args{"fr"}, + l: Language{"fr-FR"}, + root: Language{"fr"}, want: true, }, { name: "", - l: "ca", - args: args{"cat"}, + l: Language{"ca"}, + root: Language{"cat"}, want: false, }, { name: "", - l: "ca", - args: args{"ca"}, + l: Language{"ca"}, + root: Language{"ca"}, want: true, }, } for _, tt := range tests { - if got := tt.l.IsDerivedFrom(tt.args.root); got != tt.want { + if got := tt.l.IsDerivedFrom(tt.root); got != tt.want { t.Errorf("Language.IsDerivedFrom() = %v, want %v", got, tt.want) } } @@ -93,14 +90,14 @@ func TestLanguage_Compare(t *testing.T) { other Language want LanguageComparison }{ - {"fr", "fr", LanguagesExactMatch}, - {"fr-be", "fr-be", LanguagesExactMatch}, - {"und-fr", "und-fr", LanguagesExactMatch}, - {"es-fr", "es-be", LanguagePrimaryMatch}, - {"es-fr-78", "es-be", LanguagePrimaryMatch}, - {"und-fr", "und-be", LanguagesDiffer}, - {"und-math", "fr-math", LanguagesDiffer}, - {"fr-math", "und-math", LanguagesDiffer}, + {Language{"fr"}, Language{"fr"}, LanguagesExactMatch}, + {Language{"fr-be"}, Language{"fr-be"}, LanguagesExactMatch}, + {Language{"und-fr"}, Language{"und-fr"}, LanguagesExactMatch}, + {Language{"es-fr"}, Language{"es-be"}, LanguagePrimaryMatch}, + {Language{"es-fr-78"}, Language{"es-be"}, LanguagePrimaryMatch}, + {Language{"und-fr"}, Language{"und-be"}, LanguagesDiffer}, + {Language{"und-math"}, Language{"fr-math"}, LanguagesDiffer}, + {Language{"fr-math"}, Language{"und-math"}, LanguagesDiffer}, } for _, tt := range tests { if got := tt.l.Compare(tt.other); got != tt.want { @@ -114,38 +111,38 @@ var extensionTagTags = []struct { wantPrefix Language wantPrivate Language }{ - {"und", "und", ""}, - {"und-syrn", "und-syrn", ""}, - {"rm-ch-fonipa-sursilv-x-foobar", "rm-ch-fonipa-sursilv", "x-foobar"}, - {"fa-x-hbotabc-hbot-41686121-zxc", "fa", "x-hbotabc-hbot-41686121-zxc"}, - {"zh-x-hbotabc-hbot-41686121-zxc", "zh", "x-hbotabc-hbot-41686121-zxc"}, - {"fa-x-hbot-41686121-hbotabc-zxc", "fa", "x-hbot-41686121-hbotabc-zxc"}, - {"zh-x-hbot-41686121-hbotabc-zxc", "zh", "x-hbot-41686121-hbotabc-zxc"}, - {"fa-ir-x-hbotabc-hbot-41686121-zxc", "fa-ir", "x-hbotabc-hbot-41686121-zxc"}, - {"zh-cn-x-hbotabc-hbot-41686121-zxc", "zh-cn", "x-hbotabc-hbot-41686121-zxc"}, - {"zh-xy-x-hbotabc-hbot-41686121-zxc", "zh-xy", "x-hbotabc-hbot-41686121-zxc"}, - {"fa-ir-x-hbot-41686121-hbotabc-zxc", "fa-ir", "x-hbot-41686121-hbotabc-zxc"}, - {"zh-cn-x-hbot-41686121-hbotabc-zxc", "zh-cn", "x-hbot-41686121-hbotabc-zxc"}, - {"zh-xy-x-hbot-41686121-hbotabc-zxc", "zh-xy", "x-hbot-41686121-hbotabc-zxc"}, - {"xyz-xy-x-hbotabc-hbot-41686121-zxc", "xyz-xy", "x-hbotabc-hbot-41686121-zxc"}, - {"xyz-xy-x-hbot-41686121-hbotabc-zxc", "xyz-xy", "x-hbot-41686121-hbotabc-zxc"}, - {"x-hbscabc", "", "x-hbscabc"}, - {"x-hbscdeva", "", "x-hbscdeva"}, - {"x-hbscdev2", "", "x-hbscdev2"}, - {"x-hbscdev3", "", "x-hbscdev3"}, - {"x-hbsc-64657633", "", "x-hbsc-64657633"}, - {"x-hbotpap0-hbsccopt", "", "x-hbotpap0-hbsccopt"}, - {"en-x-hbsc", "en", "x-hbsc"}, - {"en-x-hbsc", "en", "x-hbsc"}, - {"en-x-hbscabc", "en", "x-hbscabc"}, - {"en-x-hbscdeva", "en", "x-hbscdeva"}, - {"en-x-hbscdev2", "en", "x-hbscdev2"}, - {"en-x-hbscdev3", "en", "x-hbscdev3"}, - {"en-x-fonipa", "en", "x-fonipa"}, + {Language{"und"}, Language{"und"}, Language{""}}, + {Language{"und-syrn"}, Language{"und-syrn"}, Language{""}}, + {Language{"rm-ch-fonipa-sursilv-x-foobar"}, Language{"rm-ch-fonipa-sursilv"}, Language{"x-foobar"}}, + {Language{"fa-x-hbotabc-hbot-41686121-zxc"}, Language{"fa"}, Language{"x-hbotabc-hbot-41686121-zxc"}}, + {Language{"zh-x-hbotabc-hbot-41686121-zxc"}, Language{"zh"}, Language{"x-hbotabc-hbot-41686121-zxc"}}, + {Language{"fa-x-hbot-41686121-hbotabc-zxc"}, Language{"fa"}, Language{"x-hbot-41686121-hbotabc-zxc"}}, + {Language{"zh-x-hbot-41686121-hbotabc-zxc"}, Language{"zh"}, Language{"x-hbot-41686121-hbotabc-zxc"}}, + {Language{"fa-ir-x-hbotabc-hbot-41686121-zxc"}, Language{"fa-ir"}, Language{"x-hbotabc-hbot-41686121-zxc"}}, + {Language{"zh-cn-x-hbotabc-hbot-41686121-zxc"}, Language{"zh-cn"}, Language{"x-hbotabc-hbot-41686121-zxc"}}, + {Language{"zh-xy-x-hbotabc-hbot-41686121-zxc"}, Language{"zh-xy"}, Language{"x-hbotabc-hbot-41686121-zxc"}}, + {Language{"fa-ir-x-hbot-41686121-hbotabc-zxc"}, Language{"fa-ir"}, Language{"x-hbot-41686121-hbotabc-zxc"}}, + {Language{"zh-cn-x-hbot-41686121-hbotabc-zxc"}, Language{"zh-cn"}, Language{"x-hbot-41686121-hbotabc-zxc"}}, + {Language{"zh-xy-x-hbot-41686121-hbotabc-zxc"}, Language{"zh-xy"}, Language{"x-hbot-41686121-hbotabc-zxc"}}, + {Language{"xyz-xy-x-hbotabc-hbot-41686121-zxc"}, Language{"xyz-xy"}, Language{"x-hbotabc-hbot-41686121-zxc"}}, + {Language{"xyz-xy-x-hbot-41686121-hbotabc-zxc"}, Language{"xyz-xy"}, Language{"x-hbot-41686121-hbotabc-zxc"}}, + {Language{"x-hbscabc"}, Language{""}, Language{"x-hbscabc"}}, + {Language{"x-hbscdeva"}, Language{""}, Language{"x-hbscdeva"}}, + {Language{"x-hbscdev2"}, Language{""}, Language{"x-hbscdev2"}}, + {Language{"x-hbscdev3"}, Language{""}, Language{"x-hbscdev3"}}, + {Language{"x-hbsc-64657633"}, Language{""}, Language{"x-hbsc-64657633"}}, + {Language{"x-hbotpap0-hbsccopt"}, Language{""}, Language{"x-hbotpap0-hbsccopt"}}, + {Language{"en-x-hbsc"}, Language{"en"}, Language{"x-hbsc"}}, + {Language{"en-x-hbsc"}, Language{"en"}, Language{"x-hbsc"}}, + {Language{"en-x-hbscabc"}, Language{"en"}, Language{"x-hbscabc"}}, + {Language{"en-x-hbscdeva"}, Language{"en"}, Language{"x-hbscdeva"}}, + {Language{"en-x-hbscdev2"}, Language{"en"}, Language{"x-hbscdev2"}}, + {Language{"en-x-hbscdev3"}, Language{"en"}, Language{"x-hbscdev3"}}, + {Language{"en-x-fonipa"}, Language{"en"}, Language{"x-fonipa"}}, // extension tags - {"en-a-fonipa", "en", ""}, - {"en-a-qwe-b-fonipa", "en", ""}, - {"en-a-qwe-x-fonipa", "en", "x-fonipa"}, + {Language{"en-a-fonipa"}, Language{"en"}, Language{""}}, + {Language{"en-a-qwe-b-fonipa"}, Language{"en"}, Language{""}}, + {Language{"en-a-qwe-x-fonipa"}, Language{"en"}, Language{"x-fonipa"}}, } func TestLanguage_SplitExtensionTags(t *testing.T) { diff --git a/opentype/tables/aat_common_test.go b/opentype/tables/aat_common_test.go index 6d0c1a8f..103053f6 100644 --- a/opentype/tables/aat_common_test.go +++ b/opentype/tables/aat_common_test.go @@ -415,5 +415,5 @@ func TestParseLtag(t *testing.T) { tu.AssertNoErr(t, err) tu.Assert(t, len(ltag.tagRange) == 1) - tu.Assert(t, ltag.Language(0) == "pl") + tu.Assert(t, ltag.Language(0).String() == "pl") } diff --git a/shaping/input_test.go b/shaping/input_test.go index 61bcc811..0a4915cc 100644 --- a/shaping/input_test.go +++ b/shaping/input_test.go @@ -592,7 +592,7 @@ func TestSplit(t *testing.T) { Direction: test.dir, Size: 10, - Language: "fr", + Language: language.NewLanguage("fr"), }, fm) tu.Assert(t, len(inputs) == len(test.expectedRuns)) for i, run := range test.expectedRuns { @@ -604,7 +604,7 @@ func TestSplit(t *testing.T) { tu.Assert(t, got.Face == run.face) // check that input properties are properly copied tu.Assert(t, got.Size == 10) - tu.Assert(t, got.Language == "fr") + tu.Assert(t, got.Language.String() == "fr") } // check that spliting a "middle" text slice is supported diff --git a/shaping/wrapping_test.go b/shaping/wrapping_test.go index 753fa1bc..1ec95902 100644 --- a/shaping/wrapping_test.go +++ b/shaping/wrapping_test.go @@ -1645,6 +1645,8 @@ func TestWrappingLatinE2E(t *testing.T) { } } +var langEn = language.NewLanguage("en") + // TestWrappingBidiRegression checks a specific regression discovered within the Gio test suite. func TestWrappingBidiRegression(t *testing.T) { type testcase struct { @@ -1671,47 +1673,47 @@ func TestWrappingBidiRegression(t *testing.T) { } bidiText2 := []rune("د عرمثال dstي met لم aqل جدmوpمg lرe dرd لو عل ميrةsdiduntut lab renنيتذدagلaaiua.ئPocttأior رادرsاي mيrbلmnonaيdتد ماةعcلخ.") bidiTextRuns2 := []Input{ - {Text: bidiText2, RunStart: 0, RunEnd: 9, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 9, RunEnd: 12, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 12, RunEnd: 14, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 14, RunEnd: 17, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 17, RunEnd: 21, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 21, RunEnd: 23, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 23, RunEnd: 27, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 27, RunEnd: 28, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 28, RunEnd: 29, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 29, RunEnd: 30, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 30, RunEnd: 31, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 31, RunEnd: 34, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 34, RunEnd: 35, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 35, RunEnd: 38, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 38, RunEnd: 39, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 39, RunEnd: 40, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 40, RunEnd: 50, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 50, RunEnd: 51, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 51, RunEnd: 52, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 52, RunEnd: 69, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 69, RunEnd: 74, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 74, RunEnd: 76, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 76, RunEnd: 77, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 77, RunEnd: 82, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 82, RunEnd: 84, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 84, RunEnd: 89, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 89, RunEnd: 90, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 90, RunEnd: 93, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 93, RunEnd: 98, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 98, RunEnd: 99, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 99, RunEnd: 102, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 102, RunEnd: 103, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 103, RunEnd: 104, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 104, RunEnd: 106, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 106, RunEnd: 107, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 107, RunEnd: 112, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 112, RunEnd: 113, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 113, RunEnd: 114, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 114, RunEnd: 121, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 121, RunEnd: 122, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 122, RunEnd: 125, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, + {Text: bidiText2, RunStart: 0, RunEnd: 9, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 9, RunEnd: 12, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 12, RunEnd: 14, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 14, RunEnd: 17, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 17, RunEnd: 21, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 21, RunEnd: 23, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 23, RunEnd: 27, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 27, RunEnd: 28, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 28, RunEnd: 29, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 29, RunEnd: 30, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 30, RunEnd: 31, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 31, RunEnd: 34, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 34, RunEnd: 35, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 35, RunEnd: 38, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 38, RunEnd: 39, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 39, RunEnd: 40, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 40, RunEnd: 50, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 50, RunEnd: 51, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 51, RunEnd: 52, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 52, RunEnd: 69, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 69, RunEnd: 74, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 74, RunEnd: 76, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 76, RunEnd: 77, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 77, RunEnd: 82, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 82, RunEnd: 84, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 84, RunEnd: 89, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 89, RunEnd: 90, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 90, RunEnd: 93, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 93, RunEnd: 98, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 98, RunEnd: 99, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 99, RunEnd: 102, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 102, RunEnd: 103, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 103, RunEnd: 104, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 104, RunEnd: 106, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 106, RunEnd: 107, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 107, RunEnd: 112, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 112, RunEnd: 113, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 113, RunEnd: 114, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 114, RunEnd: 121, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 121, RunEnd: 122, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 122, RunEnd: 125, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, } truncator := (&HarfbuzzShaper{}).Shape(Input{ Text: []rune("…"), @@ -2208,17 +2210,17 @@ func TestTruncationWithBreaking(t *testing.T) { func TestGraphemeBreakingRegression(t *testing.T) { bidiText2 := []rune("renنيتذدagلaaiua.ئPocttأior رادرs") inputRuns := []Input{ - {Text: bidiText2, RunStart: 0, RunEnd: 3, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 3, RunEnd: 8, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 8, RunEnd: 10, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 10, RunEnd: 11, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 11, RunEnd: 16, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 16, RunEnd: 18, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 18, RunEnd: 23, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 23, RunEnd: 24, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 24, RunEnd: 27, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, - {Text: bidiText2, RunStart: 27, RunEnd: 32, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: "en"}, - {Text: bidiText2, RunStart: 32, RunEnd: 33, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: "en"}, + {Text: bidiText2, RunStart: 0, RunEnd: 3, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 3, RunEnd: 8, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 8, RunEnd: 10, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 10, RunEnd: 11, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 11, RunEnd: 16, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 16, RunEnd: 18, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 18, RunEnd: 23, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 23, RunEnd: 24, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 24, RunEnd: 27, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, + {Text: bidiText2, RunStart: 27, RunEnd: 32, Direction: 0x1, Face: benchEnFace, Size: 896, Script: language.Arabic, Language: langEn}, + {Text: bidiText2, RunStart: 32, RunEnd: 33, Direction: 0x0, Face: benchEnFace, Size: 896, Script: language.Latin, Language: langEn}, } var shaper HarfbuzzShaper shaped := []Output{}