go-text / typesetting

High quality text shaping in pure Go.
Other
88 stars 11 forks source link

[shaping] Add an input segmenter #110

Closed benoitkugler closed 7 months ago

benoitkugler commented 7 months ago

This is prototype for #109

I propose the following API

// Split segments the given [text] according to :
//   - text direction
//   - script
//   - face, as defined by [faces]
//
// As a consequence, it sets the following fields of the returned runs :
//   - Text, RunStart, RunEnd
//   - Direction
//   - Script
//   - Face
//
// [defaultDirection] is used during bidi ordering, and should refer to the general
// context [text] is used in (typically the user system preference for GUI apps.)
//
// The returned sliced is owned by the [Segmenter] and is only valid until
// the next call to [Split].
func (seg *Segmenter) Split(text []rune, faces Fontmap, defaultDirection di.Direction) []Input

I'm happy to read your thoughts !

whereswaldon commented 7 months ago

The implementation looks good, and is definitely an improvement over what we have in Gio. However, if we're taking the time to overhaul this, there's an important feature gap: to implement richtext functionality, the user needs to already have the capability to segment the text on arbitrary internal boundaries to change things like the text size, the requested font family, etc... This API does not permit that because the construction of the shaping.Inputs is completely internalized.

What would you think about changing the API to:

// Split segments the given pre-configured input according to:
//   - text direction
//   - script
//   - face, as defined by [faces]
//
// Only the input runes in the range text.RunStart to text.RunEnd will be split.
//
// As a consequence, it sets the following fields of the returned runs:
//   - Text, RunStart, RunEnd
//   - Direction
//   - Script
//   - Face
//
// [defaultDirection] is used during bidi ordering, and should refer to the general
// context [text] is used in (typically the user system preference for GUI apps.)
//
// The returned sliced is owned by the [Segmenter] and is only valid until
// the next call to [Split].
func (seg *Segmenter) Split(text Input, faces Fontmap, defaultDirection di.Direction) []Input

We could additionally offer a:

// SplitText offers a simple segmentation API for unstyled text. It is identical to calling
// [Split] with a single run containing all text. See the documentation of [Split] for important
// memory ownership info.
func (seg *Segmenter) SplitText(text []rune, faces Fontmap, defaultDirection di.Direction) []Input {
    return seg.Split(Input{Text:text, RunEnd: len(text)}, faces, defaultDirection)
}
benoitkugler commented 7 months ago

What would you think about changing the API to:

Why not, it seems more versatile without too much complication ! Nice point.

benoitkugler commented 7 months ago

What would you think about changing the API to:

Done in 2f2ea8d. I've remove the defaultDirection parameter, since we can now use the Direction field of the Input.