func (fw *FontWidthsConfig) FormatText(text string, maxWidth int, fontID string) (string, error) {
	if !fw.isFontIDValid(fontID) && len(fontID) > 0 && fontID != testFontID {
		validFontIDs := make([]string, len(fw.Fonts))
		i := 0
		for k := range fw.Fonts {
			validFontIDs[i] = k
			i++
		}
		return "", fmt.Errorf("Unknown fontID '%s' used in format(). List of valid fontIDs are '%s'", fontID, validFontIDs)
	}
	text = strings.ReplaceAll(text, "\n", " ")
	var formattedSb strings.Builder
	var curLineSb strings.Builder
	curWidth := 0
	isFirstLine := true
	isFirstWord := true
	pos := 0
	for pos < len(text) {
		endPos, word, err := fw.getNextWord(text[pos:])
		if err != nil {
			return "", err
		}
		if len(word) == 0 {
			break
		}
		pos += endPos
		if fw.isLineBreak(word) {
			curWidth = 0
			formattedSb.WriteString(curLineSb.String())
			formattedSb.WriteString(word)
			formattedSb.WriteByte('\n')
			if fw.isParagraphBreak(word) {
				isFirstLine = true
			} else {
				isFirstLine = false
			}
			isFirstWord = true
			curLineSb.Reset()
		} else {
			wordWidth := 0
			if !isFirstWord {
				wordWidth += fw.getRunePixelWidth(' ', fontID)
			}
			wordWidth += fw.getWordPixelWidth(word, fontID)
			if curWidth+wordWidth > maxWidth && curLineSb.Len() > 0 {
				formattedSb.WriteString(curLineSb.String())
				if isFirstLine {
					formattedSb.WriteString(`\n`)
					isFirstLine = false
				} else {
					formattedSb.WriteString(`\l`)
				}
				formattedSb.WriteByte('\n')
				isFirstWord = false
				curLineSb.Reset()
				curLineSb.WriteString(word)
				curWidth = wordWidth
			} else {
				curWidth += wordWidth
				if !isFirstWord {
					curLineSb.WriteByte(' ')
				}
				curLineSb.WriteString(word)
				isFirstWord = false
			}
		}
	}
	if curLineSb.Len() > 0 {
		formattedSb.WriteString(curLineSb.String())
	}
	return formattedSb.String(), nil
}
FormatText automatically inserts line breaks into text
according to in-game text box widths.
package parser
import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
)
type FontWidthsConfig struct {
	Fonts         map[string]map[string]int `json:"fonts"`
	DefaultFontID string                    `json:"defaultFontId"`
}
func LoadFontWidths(filepath string) (FontWidthsConfig, error) {
	var config FontWidthsConfig
	bytes, err := ioutil.ReadFile(filepath)
	if err != nil {
		return config, err
	}
	if err := json.Unmarshal(bytes, &config); err != nil {
		return config, err
	}
	return config, err
}
const testFontID = "TEST"