ToText takes the added text spans and fits them within a given box of certain width and height using Donald Knuth's line breaking algorithm.
(width, height float64, halign, valign TextAlign, opts *TextOptions)
| 645 | |
| 646 | // ToText takes the added text spans and fits them within a given box of certain width and height using Donald Knuth's line breaking algorithm. |
| 647 | func (rt *RichText) ToText(width, height float64, halign, valign TextAlign, opts *TextOptions) *Text { |
| 648 | if opts == nil { |
| 649 | opts = &TextOptions{ |
| 650 | Linebreaker: KnuthLinebreaker{}, |
| 651 | } |
| 652 | } else if opts.Linebreaker == nil { |
| 653 | opts.Linebreaker = KnuthLinebreaker{} |
| 654 | } |
| 655 | |
| 656 | log := rt.String() |
| 657 | logRunes := []rune(log) |
| 658 | embeddingLevels := text.EmbeddingLevels(logRunes) |
| 659 | |
| 660 | // itemize string by font face and script, this also splits on embedding level boundaries and |
| 661 | // unicode.ReplacementChar (replaced by object) |
| 662 | i := 0 // index into logRunes |
| 663 | curFace := 0 // index into rt.faces |
| 664 | runs := []textRun{} |
| 665 | for j := range append(logRunes, 0) { |
| 666 | nextFace := rt.locs.index(j) |
| 667 | if nextFace != curFace || j == len(logRunes) { |
| 668 | items := text.ScriptItemizer(logRunes[i:j], embeddingLevels[i:j]) |
| 669 | for _, item := range items { |
| 670 | direction, rotation := scriptDirection(rt.mode, rt.orient, item.Script, item.Level, rt.faces[curFace].Direction) |
| 671 | runs = append(runs, textRun{ |
| 672 | Text: item.Text, |
| 673 | Level: item.Level, |
| 674 | Face: rt.faces[curFace], |
| 675 | Script: item.Script, |
| 676 | Direction: direction, |
| 677 | Rotation: rotation, |
| 678 | }) |
| 679 | } |
| 680 | curFace = nextFace |
| 681 | i = j |
| 682 | } |
| 683 | } |
| 684 | |
| 685 | // shape text into glyphs and keep index into runs |
| 686 | clusterOffset := uint32(0) |
| 687 | glyphIndices := indexer{} // indexes glyphs into runs |
| 688 | glyphs := make([]text.Glyph, 0, len(logRunes)) |
| 689 | for _, run := range runs { |
| 690 | ppem := run.Face.PPEM(DefaultResolution) |
| 691 | glyphRun := run.Face.Font.shaper.Shape(run.Text, ppem, run.Direction, run.Script, run.Face.Language, run.Face.Font.features, run.Face.Font.variations) |
| 692 | for i := range glyphRun { |
| 693 | glyph := &glyphRun[i] |
| 694 | glyph.SFNT = run.Face.Font.SFNT |
| 695 | glyph.Size = run.Face.Size |
| 696 | glyph.Script = run.Script |
| 697 | glyph.Cluster += clusterOffset |
| 698 | if obj, ok := rt.objects[glyph.Cluster]; ok { |
| 699 | // path/image objects |
| 700 | ppem := float64(run.Face.Font.SFNT.Head.UnitsPerEm) |
| 701 | xadv, yadv := obj.Width, obj.Height |
| 702 | if rt.mode != HorizontalTB { |
| 703 | yadv = -yadv |
| 704 | } |