gotext.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package text
  3. import (
  4. "bytes"
  5. "fmt"
  6. "image"
  7. "io"
  8. "log"
  9. "os"
  10. "github.com/go-text/typesetting/di"
  11. "github.com/go-text/typesetting/font"
  12. gotextot "github.com/go-text/typesetting/font/opentype"
  13. "github.com/go-text/typesetting/fontscan"
  14. "github.com/go-text/typesetting/language"
  15. "github.com/go-text/typesetting/shaping"
  16. "golang.org/x/exp/slices"
  17. "golang.org/x/image/math/fixed"
  18. "golang.org/x/text/unicode/bidi"
  19. "gioui.org/f32"
  20. giofont "gioui.org/font"
  21. "gioui.org/font/opentype"
  22. "gioui.org/internal/debug"
  23. "gioui.org/io/system"
  24. "gioui.org/op"
  25. "gioui.org/op/clip"
  26. "gioui.org/op/paint"
  27. )
  28. // document holds a collection of shaped lines and alignment information for
  29. // those lines.
  30. type document struct {
  31. lines []line
  32. alignment Alignment
  33. // alignWidth is the width used when aligning text.
  34. alignWidth int
  35. unreadRuneCount int
  36. }
  37. // append adds the lines of other to the end of l and ensures they
  38. // are aligned to the same width.
  39. func (l *document) append(other document) {
  40. l.lines = append(l.lines, other.lines...)
  41. l.alignWidth = max(l.alignWidth, other.alignWidth)
  42. calculateYOffsets(l.lines)
  43. }
  44. // reset empties the document in preparation to reuse its memory.
  45. func (l *document) reset() {
  46. l.lines = l.lines[:0]
  47. l.alignment = Start
  48. l.alignWidth = 0
  49. l.unreadRuneCount = 0
  50. }
  51. func max(a, b int) int {
  52. if a > b {
  53. return a
  54. }
  55. return b
  56. }
  57. // A line contains the measurements of a line of text.
  58. type line struct {
  59. // runs contains sequences of shaped glyphs with common attributes. The order
  60. // of runs is logical, meaning that the first run will contain the glyphs
  61. // corresponding to the first runes of data in the original text.
  62. runs []runLayout
  63. // visualOrder is a slice of indices into Runs that describes the visual positions
  64. // of each run of text. Iterating this slice and accessing Runs at each
  65. // of the values stored in this slice traverses the runs in proper visual
  66. // order from left to right.
  67. visualOrder []int
  68. // width is the width of the line.
  69. width fixed.Int26_6
  70. // ascent is the height above the baseline.
  71. ascent fixed.Int26_6
  72. // descent is the height below the baseline, including
  73. // the line gap.
  74. descent fixed.Int26_6
  75. // lineHeight captures the gap that should exist between the baseline of this
  76. // line and the previous (if any).
  77. lineHeight fixed.Int26_6
  78. // direction is the dominant direction of the line. This direction will be
  79. // used to align the text content of the line, but may not match the actual
  80. // direction of the runs of text within the line (such as an RTL sentence
  81. // within an LTR paragraph).
  82. direction system.TextDirection
  83. // runeCount is the number of text runes represented by this line's runs.
  84. runeCount int
  85. yOffset int
  86. }
  87. // insertTrailingSyntheticNewline adds a synthetic newline to the final logical run of the line
  88. // with the given shaping cluster index.
  89. func (l *line) insertTrailingSyntheticNewline(newLineClusterIdx int) {
  90. // If there was a newline at the end of this paragraph, insert a synthetic glyph representing it.
  91. finalContentRun := len(l.runs) - 1
  92. // If there was a trailing newline update the rune counts to include
  93. // it on the last line of the paragraph.
  94. l.runeCount += 1
  95. l.runs[finalContentRun].Runes.Count += 1
  96. syntheticGlyph := glyph{
  97. id: 0,
  98. clusterIndex: newLineClusterIdx,
  99. glyphCount: 0,
  100. runeCount: 1,
  101. xAdvance: 0,
  102. yAdvance: 0,
  103. xOffset: 0,
  104. yOffset: 0,
  105. }
  106. // Inset the synthetic newline glyph on the proper end of the run.
  107. if l.runs[finalContentRun].Direction.Progression() == system.FromOrigin {
  108. l.runs[finalContentRun].Glyphs = append(l.runs[finalContentRun].Glyphs, syntheticGlyph)
  109. } else {
  110. // Ensure capacity.
  111. l.runs[finalContentRun].Glyphs = append(l.runs[finalContentRun].Glyphs, glyph{})
  112. copy(l.runs[finalContentRun].Glyphs[1:], l.runs[finalContentRun].Glyphs)
  113. l.runs[finalContentRun].Glyphs[0] = syntheticGlyph
  114. }
  115. }
  116. func (l *line) setTruncatedCount(truncatedCount int) {
  117. // If we've truncated the text with a truncator, adjust the rune counts within the
  118. // truncator to make it represent the truncated text.
  119. finalRunIdx := len(l.runs) - 1
  120. l.runs[finalRunIdx].truncator = true
  121. finalGlyphIdx := len(l.runs[finalRunIdx].Glyphs) - 1
  122. // The run represents all of the truncated text.
  123. l.runs[finalRunIdx].Runes.Count = truncatedCount
  124. // Only the final glyph represents any runes, and it represents all truncated text.
  125. for i := range l.runs[finalRunIdx].Glyphs {
  126. if i == finalGlyphIdx {
  127. l.runs[finalRunIdx].Glyphs[finalGlyphIdx].runeCount = truncatedCount
  128. } else {
  129. l.runs[finalRunIdx].Glyphs[finalGlyphIdx].runeCount = 0
  130. }
  131. }
  132. }
  133. // Range describes the position and quantity of a range of text elements
  134. // within a larger slice. The unit is usually runes of unicode data or
  135. // glyphs of shaped font data.
  136. type Range struct {
  137. // Count describes the number of items represented by the Range.
  138. Count int
  139. // Offset describes the start position of the represented
  140. // items within a larger list.
  141. Offset int
  142. }
  143. // glyph contains the metadata needed to render a glyph.
  144. type glyph struct {
  145. // id is this glyph's identifier within the font it was shaped with.
  146. id GlyphID
  147. // clusterIndex is the identifier for the text shaping cluster that
  148. // this glyph is part of.
  149. clusterIndex int
  150. // glyphCount is the number of glyphs in the same cluster as this glyph.
  151. glyphCount int
  152. // runeCount is the quantity of runes in the source text that this glyph
  153. // corresponds to.
  154. runeCount int
  155. // xAdvance and yAdvance describe the distance the dot moves when
  156. // laying out the glyph on the X or Y axis.
  157. xAdvance, yAdvance fixed.Int26_6
  158. // xOffset and yOffset describe offsets from the dot that should be
  159. // applied when rendering the glyph.
  160. xOffset, yOffset fixed.Int26_6
  161. // bounds describes the visual bounding box of the glyph relative to
  162. // its dot.
  163. bounds fixed.Rectangle26_6
  164. }
  165. type runLayout struct {
  166. // VisualPosition describes the relative position of this run of text within
  167. // its line. It should be a valid index into the containing line's VisualOrder
  168. // slice.
  169. VisualPosition int
  170. // X is the visual offset of the dot for the first glyph in this run
  171. // relative to the beginning of the line.
  172. X fixed.Int26_6
  173. // Glyphs are the actual font characters for the text. They are ordered
  174. // from left to right regardless of the text direction of the underlying
  175. // text.
  176. Glyphs []glyph
  177. // Runes describes the position of the text data this layout represents
  178. // within the containing text.Line.
  179. Runes Range
  180. // Advance is the sum of the advances of all clusters in the Layout.
  181. Advance fixed.Int26_6
  182. // PPEM is the pixels-per-em scale used to shape this run.
  183. PPEM fixed.Int26_6
  184. // Direction is the layout direction of the glyphs.
  185. Direction system.TextDirection
  186. // face is the font face that the ID of each Glyph in the Layout refers to.
  187. face *font.Face
  188. // truncator indicates that this run is a text truncator standing in for remaining
  189. // text.
  190. truncator bool
  191. }
  192. // shaperImpl implements the shaping and line-wrapping of opentype fonts.
  193. type shaperImpl struct {
  194. // Fields for tracking fonts/faces.
  195. fontMap *fontscan.FontMap
  196. faces []*font.Face
  197. faceToIndex map[*font.Font]int
  198. faceMeta []giofont.Font
  199. defaultFaces []string
  200. logger interface {
  201. Printf(format string, args ...any)
  202. }
  203. parser parser
  204. // Shaping and wrapping state.
  205. shaper shaping.HarfbuzzShaper
  206. wrapper shaping.LineWrapper
  207. bidiParagraph bidi.Paragraph
  208. // Scratch buffers used to avoid re-allocating slices during routine internal
  209. // shaping operations.
  210. splitScratch1, splitScratch2 []shaping.Input
  211. outScratchBuf []shaping.Output
  212. scratchRunes []rune
  213. // bitmapGlyphCache caches extracted bitmap glyph images.
  214. bitmapGlyphCache bitmapCache
  215. }
  216. // debugLogger only logs messages if debug.Text is true.
  217. type debugLogger struct {
  218. *log.Logger
  219. }
  220. func newDebugLogger() debugLogger {
  221. return debugLogger{Logger: log.New(log.Writer(), "[text] ", log.Default().Flags())}
  222. }
  223. func (d debugLogger) Printf(format string, args ...any) {
  224. if debug.Text.Load() {
  225. d.Logger.Printf(format, args...)
  226. }
  227. }
  228. func newShaperImpl(systemFonts bool, collection []FontFace) *shaperImpl {
  229. var shaper shaperImpl
  230. shaper.logger = newDebugLogger()
  231. shaper.fontMap = fontscan.NewFontMap(shaper.logger)
  232. shaper.faceToIndex = make(map[*font.Font]int)
  233. if systemFonts {
  234. str, err := os.UserCacheDir()
  235. if err != nil {
  236. shaper.logger.Printf("failed resolving font cache dir: %v", err)
  237. shaper.logger.Printf("skipping system font load")
  238. }
  239. if err := shaper.fontMap.UseSystemFonts(str); err != nil {
  240. shaper.logger.Printf("failed loading system fonts: %v", err)
  241. }
  242. }
  243. for _, f := range collection {
  244. shaper.Load(f)
  245. shaper.defaultFaces = append(shaper.defaultFaces, string(f.Font.Typeface))
  246. }
  247. shaper.shaper.SetFontCacheSize(32)
  248. return &shaper
  249. }
  250. // Load registers the provided FontFace with the shaper, if it is compatible.
  251. // It returns whether the face is now available for use. FontFaces are prioritized
  252. // in the order in which they are loaded, with the first face being the default.
  253. func (s *shaperImpl) Load(f FontFace) {
  254. desc := opentype.FontToDescription(f.Font)
  255. s.fontMap.AddFace(f.Face.Face(), fontscan.Location{File: fmt.Sprint(desc)}, desc)
  256. s.addFace(f.Face.Face(), f.Font)
  257. }
  258. func (s *shaperImpl) addFace(f *font.Face, md giofont.Font) {
  259. if _, ok := s.faceToIndex[f.Font]; ok {
  260. return
  261. }
  262. s.logger.Printf("loaded face %s(style:%s, weight:%d)", md.Typeface, md.Style, md.Weight)
  263. idx := len(s.faces)
  264. s.faceToIndex[f.Font] = idx
  265. s.faces = append(s.faces, f)
  266. s.faceMeta = append(s.faceMeta, md)
  267. }
  268. // splitByScript divides the inputs into new, smaller inputs on script boundaries
  269. // and correctly sets the text direction per-script. It will
  270. // use buf as the backing memory for the returned slice if buf is non-nil.
  271. func splitByScript(inputs []shaping.Input, documentDir di.Direction, buf []shaping.Input) []shaping.Input {
  272. var splitInputs []shaping.Input
  273. if buf == nil {
  274. splitInputs = make([]shaping.Input, 0, len(inputs))
  275. } else {
  276. splitInputs = buf
  277. }
  278. for _, input := range inputs {
  279. currentInput := input
  280. if input.RunStart == input.RunEnd {
  281. return []shaping.Input{input}
  282. }
  283. firstNonCommonRune := input.RunStart
  284. for i := firstNonCommonRune; i < input.RunEnd; i++ {
  285. if language.LookupScript(input.Text[i]) != language.Common {
  286. firstNonCommonRune = i
  287. break
  288. }
  289. }
  290. currentInput.Script = language.LookupScript(input.Text[firstNonCommonRune])
  291. for i := firstNonCommonRune + 1; i < input.RunEnd; i++ {
  292. r := input.Text[i]
  293. runeScript := language.LookupScript(r)
  294. if runeScript == language.Common || runeScript == currentInput.Script {
  295. continue
  296. }
  297. if i != input.RunStart {
  298. currentInput.RunEnd = i
  299. splitInputs = append(splitInputs, currentInput)
  300. }
  301. currentInput = input
  302. currentInput.RunStart = i
  303. currentInput.Script = runeScript
  304. // In the future, it may make sense to try to guess the language of the text here as well,
  305. // but this is a complex process.
  306. }
  307. // close and add the last input
  308. currentInput.RunEnd = input.RunEnd
  309. splitInputs = append(splitInputs, currentInput)
  310. }
  311. return splitInputs
  312. }
  313. func (s *shaperImpl) splitBidi(input shaping.Input) []shaping.Input {
  314. var splitInputs []shaping.Input
  315. if input.Direction.Axis() != di.Horizontal || input.RunStart == input.RunEnd {
  316. return []shaping.Input{input}
  317. }
  318. def := bidi.LeftToRight
  319. if input.Direction.Progression() == di.TowardTopLeft {
  320. def = bidi.RightToLeft
  321. }
  322. s.bidiParagraph.SetString(string(input.Text), bidi.DefaultDirection(def))
  323. out, err := s.bidiParagraph.Order()
  324. if err != nil {
  325. return []shaping.Input{input}
  326. }
  327. for i := 0; i < out.NumRuns(); i++ {
  328. currentInput := input
  329. run := out.Run(i)
  330. dir := run.Direction()
  331. _, endRune := run.Pos()
  332. currentInput.RunEnd = endRune + 1
  333. if dir == bidi.RightToLeft {
  334. currentInput.Direction = di.DirectionRTL
  335. } else {
  336. currentInput.Direction = di.DirectionLTR
  337. }
  338. splitInputs = append(splitInputs, currentInput)
  339. input.RunStart = currentInput.RunEnd
  340. }
  341. return splitInputs
  342. }
  343. // ResolveFace allows shaperImpl to implement shaping.FontMap, wrapping its fontMap
  344. // field and ensuring that any faces loaded as part of the search are registered with
  345. // ids so that they can be referred to by a GlyphID.
  346. func (s *shaperImpl) ResolveFace(r rune) *font.Face {
  347. face := s.fontMap.ResolveFace(r)
  348. if face != nil {
  349. family, aspect := s.fontMap.FontMetadata(face.Font)
  350. md := opentype.DescriptionToFont(font.Description{
  351. Family: family,
  352. Aspect: aspect,
  353. })
  354. s.addFace(face, md)
  355. return face
  356. }
  357. return nil
  358. }
  359. // splitByFaces divides the inputs by font coverage in the provided faces. It will use the slice provided in buf
  360. // as the backing storage of the returned slice if buf is non-nil.
  361. func (s *shaperImpl) splitByFaces(inputs []shaping.Input, buf []shaping.Input) []shaping.Input {
  362. var split []shaping.Input
  363. if buf == nil {
  364. split = make([]shaping.Input, 0, len(inputs))
  365. } else {
  366. split = buf
  367. }
  368. for _, input := range inputs {
  369. split = append(split, shaping.SplitByFace(input, s)...)
  370. }
  371. return split
  372. }
  373. // shapeText invokes the text shaper and returns the raw text data in the shaper's native
  374. // format. It does not wrap lines.
  375. func (s *shaperImpl) shapeText(ppem fixed.Int26_6, lc system.Locale, txt []rune) []shaping.Output {
  376. lcfg := langConfig{
  377. Language: language.NewLanguage(lc.Language),
  378. Direction: mapDirection(lc.Direction),
  379. }
  380. // Create an initial input.
  381. input := toInput(nil, ppem, lcfg, txt)
  382. if input.RunStart == input.RunEnd && len(s.faces) > 0 {
  383. // Give the empty string a face. This is a necessary special case because
  384. // the face splitting process works by resolving faces for each rune, and
  385. // the empty string contains no runes.
  386. input.Face = s.faces[0]
  387. }
  388. // Break input on font glyph coverage.
  389. inputs := s.splitBidi(input)
  390. inputs = s.splitByFaces(inputs, s.splitScratch1[:0])
  391. inputs = splitByScript(inputs, lcfg.Direction, s.splitScratch2[:0])
  392. // Shape all inputs.
  393. if needed := len(inputs) - len(s.outScratchBuf); needed > 0 {
  394. s.outScratchBuf = slices.Grow(s.outScratchBuf, needed)
  395. }
  396. s.outScratchBuf = s.outScratchBuf[:0]
  397. for _, input := range inputs {
  398. if input.Face != nil {
  399. s.outScratchBuf = append(s.outScratchBuf, s.shaper.Shape(input))
  400. } else {
  401. s.outScratchBuf = append(s.outScratchBuf, shaping.Output{
  402. // Use the text size as the advance of the entire fake run so that
  403. // it doesn't occupy zero space.
  404. Advance: input.Size,
  405. Size: input.Size,
  406. Glyphs: []shaping.Glyph{
  407. {
  408. Width: input.Size,
  409. Height: input.Size,
  410. XBearing: 0,
  411. YBearing: 0,
  412. XAdvance: input.Size,
  413. YAdvance: input.Size,
  414. XOffset: 0,
  415. YOffset: 0,
  416. ClusterIndex: input.RunStart,
  417. RuneCount: input.RunEnd - input.RunStart,
  418. GlyphCount: 1,
  419. GlyphID: 0,
  420. Mask: 0,
  421. },
  422. },
  423. LineBounds: shaping.Bounds{
  424. Ascent: input.Size,
  425. Descent: 0,
  426. Gap: 0,
  427. },
  428. GlyphBounds: shaping.Bounds{
  429. Ascent: input.Size,
  430. Descent: 0,
  431. Gap: 0,
  432. },
  433. Direction: input.Direction,
  434. Runes: shaping.Range{
  435. Offset: input.RunStart,
  436. Count: input.RunEnd - input.RunStart,
  437. },
  438. })
  439. }
  440. }
  441. return s.outScratchBuf
  442. }
  443. func wrapPolicyToGoText(p WrapPolicy) shaping.LineBreakPolicy {
  444. switch p {
  445. case WrapGraphemes:
  446. return shaping.Always
  447. case WrapWords:
  448. return shaping.Never
  449. default:
  450. return shaping.WhenNecessary
  451. }
  452. }
  453. // shapeAndWrapText invokes the text shaper and returns wrapped lines in the shaper's native format.
  454. func (s *shaperImpl) shapeAndWrapText(params Parameters, txt []rune) (_ []shaping.Line, truncated int) {
  455. wc := shaping.WrapConfig{
  456. Direction: mapDirection(params.Locale.Direction),
  457. TruncateAfterLines: params.MaxLines,
  458. TextContinues: params.forceTruncate,
  459. BreakPolicy: wrapPolicyToGoText(params.WrapPolicy),
  460. DisableTrailingWhitespaceTrim: params.DisableSpaceTrim,
  461. }
  462. families := s.defaultFaces
  463. if params.Font.Typeface != "" {
  464. parsed, err := s.parser.parse(string(params.Font.Typeface))
  465. if err != nil {
  466. s.logger.Printf("Unable to parse typeface %q: %v", params.Font.Typeface, err)
  467. } else {
  468. families = parsed
  469. }
  470. }
  471. s.fontMap.SetQuery(fontscan.Query{
  472. Families: families,
  473. Aspect: opentype.FontToDescription(params.Font).Aspect,
  474. })
  475. if wc.TruncateAfterLines > 0 {
  476. if len(params.Truncator) == 0 {
  477. params.Truncator = "…"
  478. }
  479. // We only permit a single run as the truncator, regardless of whether more were generated.
  480. // Just use the first one.
  481. wc.Truncator = s.shapeText(params.PxPerEm, params.Locale, []rune(params.Truncator))[0]
  482. }
  483. // Wrap outputs into lines.
  484. return s.wrapper.WrapParagraph(wc, params.MaxWidth, txt, shaping.NewSliceIterator(s.shapeText(params.PxPerEm, params.Locale, txt)))
  485. }
  486. // replaceControlCharacters replaces problematic unicode
  487. // code points with spaces to ensure proper rune accounting.
  488. func replaceControlCharacters(in []rune) []rune {
  489. for i, r := range in {
  490. switch r {
  491. // ASCII File separator.
  492. case '\u001C':
  493. // ASCII Group separator.
  494. case '\u001D':
  495. // ASCII Record separator.
  496. case '\u001E':
  497. case '\r':
  498. case '\n':
  499. // Unicode "next line" character.
  500. case '\u0085':
  501. // Unicode "paragraph separator".
  502. case '\u2029':
  503. default:
  504. continue
  505. }
  506. in[i] = ' '
  507. }
  508. return in
  509. }
  510. // Layout shapes and wraps the text, and returns the result in Gio's shaped text format.
  511. func (s *shaperImpl) LayoutString(params Parameters, txt string) document {
  512. return s.LayoutRunes(params, []rune(txt))
  513. }
  514. // Layout shapes and wraps the text, and returns the result in Gio's shaped text format.
  515. func (s *shaperImpl) Layout(params Parameters, txt io.RuneReader) document {
  516. s.scratchRunes = s.scratchRunes[:0]
  517. for r, _, err := txt.ReadRune(); err != nil; r, _, err = txt.ReadRune() {
  518. s.scratchRunes = append(s.scratchRunes, r)
  519. }
  520. return s.LayoutRunes(params, s.scratchRunes)
  521. }
  522. func calculateYOffsets(lines []line) {
  523. if len(lines) < 1 {
  524. return
  525. }
  526. // Ceil the first value to ensure that we don't baseline it too close to the top of the
  527. // viewport and cut off the top pixel.
  528. currentY := lines[0].ascent.Ceil()
  529. for i := range lines {
  530. if i > 0 {
  531. currentY += lines[i].lineHeight.Round()
  532. }
  533. lines[i].yOffset = currentY
  534. }
  535. }
  536. // LayoutRunes shapes and wraps the text, and returns the result in Gio's shaped text format.
  537. func (s *shaperImpl) LayoutRunes(params Parameters, txt []rune) document {
  538. hasNewline := len(txt) > 0 && txt[len(txt)-1] == '\n'
  539. var ls []shaping.Line
  540. var truncated int
  541. if hasNewline {
  542. txt = txt[:len(txt)-1]
  543. }
  544. if params.MaxLines != 0 && hasNewline {
  545. // If we might end up truncating a trailing newline, we must insert the truncator symbol
  546. // on the final line (if we hit the limit).
  547. params.forceTruncate = true
  548. }
  549. ls, truncated = s.shapeAndWrapText(params, replaceControlCharacters(txt))
  550. hasTruncator := truncated > 0 || (params.forceTruncate && params.MaxLines == len(ls))
  551. if hasTruncator && hasNewline {
  552. // We have a truncator at the end of the line, so the newline is logically
  553. // truncated as well.
  554. truncated++
  555. hasNewline = false
  556. }
  557. // Convert to Lines.
  558. textLines := make([]line, len(ls))
  559. maxHeight := fixed.Int26_6(0)
  560. for i := range ls {
  561. otLine := toLine(s.faceToIndex, ls[i], params.Locale.Direction)
  562. if otLine.lineHeight > maxHeight {
  563. maxHeight = otLine.lineHeight
  564. }
  565. if isFinalLine := i == len(ls)-1; isFinalLine {
  566. if hasNewline {
  567. otLine.insertTrailingSyntheticNewline(len(txt))
  568. }
  569. if hasTruncator {
  570. otLine.setTruncatedCount(truncated)
  571. }
  572. }
  573. textLines[i] = otLine
  574. }
  575. if params.LineHeight != 0 {
  576. maxHeight = params.LineHeight
  577. }
  578. if params.LineHeightScale == 0 {
  579. params.LineHeightScale = 1.2
  580. }
  581. maxHeight = floatToFixed(fixedToFloat(maxHeight) * params.LineHeightScale)
  582. for i := range textLines {
  583. textLines[i].lineHeight = maxHeight
  584. }
  585. calculateYOffsets(textLines)
  586. return document{
  587. lines: textLines,
  588. alignment: params.Alignment,
  589. alignWidth: alignWidth(params.MinWidth, textLines),
  590. }
  591. }
  592. func alignWidth(minWidth int, lines []line) int {
  593. for _, l := range lines {
  594. minWidth = max(minWidth, l.width.Ceil())
  595. }
  596. return minWidth
  597. }
  598. // Shape converts the provided glyphs into a path. The path will enclose the forms
  599. // of all vector glyphs.
  600. func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec {
  601. var lastPos f32.Point
  602. var x fixed.Int26_6
  603. var builder clip.Path
  604. builder.Begin(pathOps)
  605. for i, g := range gs {
  606. if i == 0 {
  607. x = g.X
  608. }
  609. ppem, faceIdx, gid := splitGlyphID(g.ID)
  610. if faceIdx >= len(s.faces) {
  611. continue
  612. }
  613. face := s.faces[faceIdx]
  614. if face == nil {
  615. continue
  616. }
  617. scaleFactor := fixedToFloat(ppem) / float32(face.Upem())
  618. glyphData := face.GlyphData(gid)
  619. switch glyphData := glyphData.(type) {
  620. case font.GlyphOutline:
  621. outline := glyphData
  622. // Move to glyph position.
  623. pos := f32.Point{
  624. X: fixedToFloat((g.X - x) - g.Offset.X),
  625. Y: -fixedToFloat(g.Offset.Y),
  626. }
  627. builder.Move(pos.Sub(lastPos))
  628. lastPos = pos
  629. var lastArg f32.Point
  630. // Convert fonts.Segments to relative segments.
  631. for _, fseg := range outline.Segments {
  632. nargs := 1
  633. switch fseg.Op {
  634. case gotextot.SegmentOpQuadTo:
  635. nargs = 2
  636. case gotextot.SegmentOpCubeTo:
  637. nargs = 3
  638. }
  639. var args [3]f32.Point
  640. for i := 0; i < nargs; i++ {
  641. a := f32.Point{
  642. X: fseg.Args[i].X * scaleFactor,
  643. Y: -fseg.Args[i].Y * scaleFactor,
  644. }
  645. args[i] = a.Sub(lastArg)
  646. if i == nargs-1 {
  647. lastArg = a
  648. }
  649. }
  650. switch fseg.Op {
  651. case gotextot.SegmentOpMoveTo:
  652. builder.Move(args[0])
  653. case gotextot.SegmentOpLineTo:
  654. builder.Line(args[0])
  655. case gotextot.SegmentOpQuadTo:
  656. builder.Quad(args[0], args[1])
  657. case gotextot.SegmentOpCubeTo:
  658. builder.Cube(args[0], args[1], args[2])
  659. default:
  660. panic("unsupported segment op")
  661. }
  662. }
  663. lastPos = lastPos.Add(lastArg)
  664. }
  665. }
  666. return builder.End()
  667. }
  668. func fixedToFloat(i fixed.Int26_6) float32 {
  669. return float32(i) / 64.0
  670. }
  671. func floatToFixed(f float32) fixed.Int26_6 {
  672. return fixed.Int26_6(f * 64)
  673. }
  674. // Bitmaps returns an op.CallOp that will display all bitmap glyphs within gs.
  675. // The positioning of the bitmaps uses the same logic as Shape(), so the returned
  676. // CallOp can be added at the same offset as the path data returned by Shape()
  677. // and will align correctly.
  678. func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp {
  679. var x fixed.Int26_6
  680. bitmapMacro := op.Record(ops)
  681. for i, g := range gs {
  682. if i == 0 {
  683. x = g.X
  684. }
  685. _, faceIdx, gid := splitGlyphID(g.ID)
  686. if faceIdx >= len(s.faces) {
  687. continue
  688. }
  689. face := s.faces[faceIdx]
  690. if face == nil {
  691. continue
  692. }
  693. glyphData := face.GlyphData(gid)
  694. switch glyphData := glyphData.(type) {
  695. case font.GlyphBitmap:
  696. var imgOp paint.ImageOp
  697. var imgSize image.Point
  698. bitmapData, ok := s.bitmapGlyphCache.Get(g.ID)
  699. if !ok {
  700. var img image.Image
  701. switch glyphData.Format {
  702. case font.PNG, font.JPG, font.TIFF:
  703. img, _, _ = image.Decode(bytes.NewReader(glyphData.Data))
  704. case font.BlackAndWhite:
  705. // This is a complex family of uncompressed bitmaps that don't seem to be
  706. // very common in practice. We can try adding support later if needed.
  707. fallthrough
  708. default:
  709. // Unknown format.
  710. continue
  711. }
  712. imgOp = paint.NewImageOp(img)
  713. imgSize = img.Bounds().Size()
  714. s.bitmapGlyphCache.Put(g.ID, bitmap{img: imgOp, size: imgSize})
  715. } else {
  716. imgOp = bitmapData.img
  717. imgSize = bitmapData.size
  718. }
  719. off := op.Affine(f32.Affine2D{}.Offset(f32.Point{
  720. X: fixedToFloat((g.X - x) - g.Offset.X),
  721. Y: fixedToFloat(g.Offset.Y + g.Bounds.Min.Y),
  722. })).Push(ops)
  723. cl := clip.Rect{Max: imgSize}.Push(ops)
  724. glyphSize := image.Rectangle{
  725. Min: image.Point{
  726. X: g.Bounds.Min.X.Round(),
  727. Y: g.Bounds.Min.Y.Round(),
  728. },
  729. Max: image.Point{
  730. X: g.Bounds.Max.X.Round(),
  731. Y: g.Bounds.Max.Y.Round(),
  732. },
  733. }.Size()
  734. aff := op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Point{
  735. X: float32(glyphSize.X) / float32(imgSize.X),
  736. Y: float32(glyphSize.Y) / float32(imgSize.Y),
  737. })).Push(ops)
  738. imgOp.Add(ops)
  739. paint.PaintOp{}.Add(ops)
  740. aff.Pop()
  741. cl.Pop()
  742. off.Pop()
  743. }
  744. }
  745. return bitmapMacro.Stop()
  746. }
  747. // langConfig describes the language and writing system of a body of text.
  748. type langConfig struct {
  749. // Language the text is written in.
  750. language.Language
  751. // Writing system used to represent the text.
  752. language.Script
  753. // Direction of the text, usually driven by the writing system.
  754. di.Direction
  755. }
  756. // toInput converts its parameters into a shaping.Input.
  757. func toInput(face *font.Face, ppem fixed.Int26_6, lc langConfig, runes []rune) shaping.Input {
  758. var input shaping.Input
  759. input.Direction = lc.Direction
  760. input.Text = runes
  761. input.Size = ppem
  762. input.Face = face
  763. input.Language = lc.Language
  764. input.Script = lc.Script
  765. input.RunStart = 0
  766. input.RunEnd = len(runes)
  767. return input
  768. }
  769. func mapDirection(d system.TextDirection) di.Direction {
  770. switch d {
  771. case system.LTR:
  772. return di.DirectionLTR
  773. case system.RTL:
  774. return di.DirectionRTL
  775. }
  776. return di.DirectionLTR
  777. }
  778. func unmapDirection(d di.Direction) system.TextDirection {
  779. switch d {
  780. case di.DirectionLTR:
  781. return system.LTR
  782. case di.DirectionRTL:
  783. return system.RTL
  784. }
  785. return system.LTR
  786. }
  787. // toGioGlyphs converts text shaper glyphs into the minimal representation
  788. // that Gio needs.
  789. func toGioGlyphs(in []shaping.Glyph, ppem fixed.Int26_6, faceIdx int) []glyph {
  790. out := make([]glyph, 0, len(in))
  791. for _, g := range in {
  792. // To better understand how to calculate the bounding box, see here:
  793. // https://freetype.org/freetype2/docs/glyphs/glyph-metrics-3.svg
  794. var bounds fixed.Rectangle26_6
  795. bounds.Min.X = g.XBearing
  796. bounds.Min.Y = -g.YBearing
  797. bounds.Max = bounds.Min.Add(fixed.Point26_6{X: g.Width, Y: -g.Height})
  798. out = append(out, glyph{
  799. id: newGlyphID(ppem, faceIdx, g.GlyphID),
  800. clusterIndex: g.ClusterIndex,
  801. runeCount: g.RuneCount,
  802. glyphCount: g.GlyphCount,
  803. xAdvance: g.XAdvance,
  804. yAdvance: g.YAdvance,
  805. xOffset: g.XOffset,
  806. yOffset: g.YOffset,
  807. bounds: bounds,
  808. })
  809. }
  810. return out
  811. }
  812. // toLine converts the output into a Line with the provided dominant text direction.
  813. func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirection) line {
  814. if len(o) < 1 {
  815. return line{}
  816. }
  817. line := line{
  818. runs: make([]runLayout, len(o)),
  819. direction: dir,
  820. visualOrder: make([]int, len(o)),
  821. }
  822. maxSize := fixed.Int26_6(0)
  823. for i := range o {
  824. run := o[i]
  825. if run.Size > maxSize {
  826. maxSize = run.Size
  827. }
  828. var font *font.Font
  829. if run.Face != nil {
  830. font = run.Face.Font
  831. }
  832. line.runs[i] = runLayout{
  833. Glyphs: toGioGlyphs(run.Glyphs, run.Size, faceToIndex[font]),
  834. Runes: Range{
  835. Count: run.Runes.Count,
  836. Offset: line.runeCount,
  837. },
  838. Direction: unmapDirection(run.Direction),
  839. face: run.Face,
  840. Advance: run.Advance,
  841. PPEM: run.Size,
  842. VisualPosition: int(run.VisualIndex),
  843. }
  844. line.visualOrder[run.VisualIndex] = i
  845. line.runeCount += run.Runes.Count
  846. line.width += run.Advance
  847. if line.ascent < run.LineBounds.Ascent {
  848. line.ascent = run.LineBounds.Ascent
  849. }
  850. if line.descent < -run.LineBounds.Descent+run.LineBounds.Gap {
  851. line.descent = -run.LineBounds.Descent + run.LineBounds.Gap
  852. }
  853. }
  854. line.lineHeight = maxSize
  855. // Iterate and resolve the X of each run.
  856. x := fixed.Int26_6(0)
  857. for _, runIdx := range line.visualOrder {
  858. line.runs[runIdx].X = x
  859. x += line.runs[runIdx].Advance
  860. }
  861. return line
  862. }