diff --git a/setter.go b/setter.go deleted file mode 100644 index a8f360a..0000000 --- a/setter.go +++ /dev/null @@ -1,227 +0,0 @@ -package typeset - -import "fmt" -import "strconv" -import "golang.org/x/image/font" -import "golang.org/x/image/math/fixed" - -type validationLevel int; const ( - validationLevelNone validationLevel = iota - validationLevelTokens - validationLevelMeasurement - validationLevelFlow -) - -type tokenKind int; const ( - tokenKindWord tokenKind = iota // contains everything that isn't: - tokenKindSpace // only unicode space runes, except \r or \n - tokenKindTab // only \t runes - tokenKindLineBreak // either "\n", or "\r\n" -) - -func (kind tokenKind) String () string { - switch kind { - case tokenKindWord: return "Word" - case tokenKindSpace: return "Space" - case tokenKindTab: return "Tab" - case tokenKindLineBreak: return "LineBreak" - } - return fmt.Sprintf("typeset.tokenKind(%d)", kind) -} - -type token struct { - kind tokenKind - width fixed.Int26_6 - position fixed.Point26_6 - runes []runeLayout -} - -func (tok token) String () string { - str := "" - for _, runl := range tok.runes { - str += string(runl.run) - } - return fmt.Sprintf ( - "%v:%v{%v,%v-%v}", - tok.kind, strconv.Quote(str), - tok.position.X, tok.position.Y, tok.width) -} - -type runeLayout struct { - x fixed.Int26_6 - run rune -} - -func (run runeLayout) String () string { - return fmt.Sprintf("%s-{%v}", strconv.Quote(string([]rune { run.run })), run.x) -} - -// RuneIter is an iterator that iterates over positioned runes. -type RuneIter func (yield func(fixed.Point26_6, rune) bool) - -// Align specifies a text alignment method. -type Align int; const ( - // X | Y - AlignStart Align = iota // left | top - AlignMiddle // center | center - AlignEnd // right | bottom - AlignEven // justified | (unsupported) -) - -// TypeSetter manages text, and can perform layout operations on it. It -// automatically avoids performing redundant work. It has no constructor and its -// zero value can be used safely, but it must not be copied after first use. -type TypeSetter struct { - text string - runes []runeLayout - tokens []token - - validationLevel validationLevel - - xAlign, yAlign Align - face font.Face - size fixed.Point26_6 // width, height - wrap bool - - minimumSize fixed.Point26_6 - layoutBounds fixed.Rectangle26_6 - layoutBoundsSpace fixed.Rectangle26_6 -} - -// Runes returns an iterator for all runes in the TypeSetter, and thier positions. -func (this *TypeSetter) Runes () RuneIter { - this.needFlow() - return func (yield func (fixed.Point26_6, rune) bool) { - for _, token := range this.tokens { - for _, runl := range token.runes { - pos := token.position - pos.X += runl.x - if !yield(pos, runl.run) { return } - } - } - } -} - -// Em returns the width of one emspace according to the typesetter's typeface, -// which is the width of the capital letter 'M'. -func (this *TypeSetter) Em () fixed.Int26_6 { - if this.face == nil { return 0 } - width, _ := this.face.GlyphAdvance('M') - return width -} - -// MinimumSize returns the minimum width and height needed to display text. If -// wrapping is enabled, this method will return { X: Em(), Y: 0 }. -func (this *TypeSetter) MinimumSize () fixed.Point26_6 { - if this.wrap { return fixed.Point26_6{ X: this.Em(), Y: 0 } } - this.needFlow() - return this.minimumSize -} - -// LayoutBounds returns the semantic bounding box of the text. The origin point -// (0, 0) of the rectangle corresponds to the origin of the first line's -// baseline. -func (this *TypeSetter) LayoutBounds () fixed.Rectangle26_6 { - this.needFlow() - return this.layoutBounds -} - -// LayoutBoundsSpace is like LayoutBounds, but it also takes into account the -// trailing whitespace at the end of each line (if it exists). -func (this *TypeSetter) LayoutBoundsSpace () fixed.Rectangle26_6 { - this.needFlow() - return this.layoutBoundsSpace -} - -// PositionAt returns the position of the rune at the specified index. -func (this *TypeSetter) PositionAt (index int) fixed.Point26_6 { - idx := 0 - var position fixed.Point26_6 - this.Runes()(func (pos fixed.Point26_6, run rune) bool { - if index == idx { - position = pos - return false - } - idx ++ - return true - }) - return position -} - -// SetText sets the text of the TypeSetter. -func (this *TypeSetter) SetText (text string) { - if this.text == text { return } - this.text = text - this.invalidate(validationLevelTokens) -} - -// SetSize sets the width and height of the TypeSetter. -func (this *TypeSetter) SetSize (size fixed.Point26_6) { - if this.size == size { return } - this.size = size - this.invalidate(validationLevelFlow) -} - -// SetWrap sets whether the text will wrap to the width specified by SetSize. -func (this *TypeSetter) SetWrap (wrap bool) { - if this.wrap == wrap { return } - this.wrap = wrap - this.invalidate(validationLevelFlow) -} - -// SetAlign sets the horizontal and vertical alignment of the text. -func (this *TypeSetter) SetAlign (x, y Align) { - if this.xAlign == x && this.yAlign == y { return } - this.xAlign = x - this.yAlign = y - this.invalidate(validationLevelFlow) -} - -// Face returns the font face as set by SetFace. -func (this *TypeSetter) Face () font.Face { - return this.face -} - -// SetFace sets the font face the text will be laid out according to. -func (this *TypeSetter) SetFace (face font.Face) { - if this.face == face { return } - this.face = face - this.invalidate(validationLevelMeasurement) -} - -func (this *TypeSetter) needTokens () { - if this.valid(validationLevelTokens) { return } - this.runes, this.tokens = parseString(this.text) - this.validate(validationLevelTokens) -} - -func (this *TypeSetter) needMeasurement () { - if this.valid(validationLevelMeasurement) { return } - this.needTokens() - measure(this.tokens, this.face) - this.validate(validationLevelMeasurement) -} - -func (this *TypeSetter) needFlow () { - if this.valid(validationLevelFlow) { return } - this.needMeasurement() - this.layoutBounds, this.layoutBoundsSpace, this.minimumSize = reflow ( - this.tokens, - this.face, this.size, - this.wrap, this.xAlign, this.yAlign) - this.validate(validationLevelFlow) -} - -func (this *TypeSetter) validate (level validationLevel) { - this.validationLevel = level -} - -func (this *TypeSetter) invalidate (level validationLevel) { - if this.valid(level) { - this.validationLevel = level - 1 - } -} - -func (this *TypeSetter) valid (level validationLevel) bool { - return this.validationLevel >= level -}