diff --git a/layout.go b/layout.go index 2d5c547..c763508 100644 --- a/layout.go +++ b/layout.go @@ -8,12 +8,11 @@ import "golang.org/x/image/math/fixed" type Align int const ( - // AlignLeft aligns the start of each line to the beginning point - // of each dot. - AlignLeft Align = iota - AlignRight - AlignCenter - AlignJustify + // X | Y + AlignStart Align = iota // left | top + AlignMiddle // center | center + AlignEnd // right | bottom + AlignEven // justified | evenly spaced ) // RuneLayout contains layout information for a single rune relative to its @@ -150,11 +149,7 @@ func DoLine (text []rune, face font.Face, wrap bool, width fixed.Int26_6) (line // set the width of the line's content. line.ContentWidth = lastWord.X + lastWord.Width - if wrap { - line.Width = width - } else { - line.Width = line.ContentWidth - } + line.Width = width line.SpaceAfter = lastWord.SpaceAfter return } @@ -174,17 +169,17 @@ func (line *LineLayout) Length () int { // method. func (line *LineLayout) Align (align Align) { if len(line.Words) == 0 { return } - - if align == AlignJustify { + + if align == AlignEven { line.justify() return } leftOffset := -line.Words[0].X - if align == AlignCenter { + if align == AlignMiddle { leftOffset += (line.Width - line.ContentWidth) / 2 - } else if align == AlignRight { + } else if align == AlignEnd { leftOffset += line.Width - line.ContentWidth } @@ -195,7 +190,7 @@ func (line *LineLayout) Align (align Align) { func (line *LineLayout) justify () { if len(line.Words) < 2 { - line.Align(AlignLeft) + line.Align(AlignStart) return } diff --git a/setter.go b/setter.go index d588567..a501543 100644 --- a/setter.go +++ b/setter.go @@ -14,11 +14,10 @@ type TypeSetter struct { layoutClean bool alignClean bool - align Align - face font.Face - maxWidth int - maxHeight int - wrap bool + hAlign, vAlign Align + face font.Face + width, height int + wrap bool minWidth fixed.Int26_6 layoutBounds image.Rectangle @@ -34,14 +33,15 @@ func (setter *TypeSetter) needLayout () { setter.layoutBounds = image.Rectangle { } setter.layoutBoundsSpace = image.Rectangle { } setter.minWidth = 0 - if setter.face == nil { return } + if setter.face == nil { return } horizontalExtent := fixed.Int26_6(0) horizontalExtentSpace := fixed.Int26_6(0) metrics := setter.face.Metrics() remaining := setter.text y := fixed.Int26_6(0) - + + // function to add line and update bounds statistics addLine := func (line LineLayout) { line.Y = y y += metrics.Height @@ -59,7 +59,7 @@ func (setter *TypeSetter) needLayout () { for len(remaining) > 0 { line, remainingFromLine := DoLine ( remaining, setter.face, setter.wrap, - fixed.I(setter.maxWidth)) + fixed.I(setter.width)) remaining = remainingFromLine addLine(line) } @@ -71,13 +71,19 @@ func (setter *TypeSetter) needLayout () { setter.lines[len(setter.lines) - 1].BreakAfter if needBlankLine { addLine(LineLayout { }) } + // if we are wrapping text, the width must be the user-set width + if setter.wrap { + horizontalExtent = fixed.I(setter.width) + horizontalExtentSpace = fixed.I(setter.width) + } + // calculate layout boundaries setter.minWidth = horizontalExtentSpace setter.layoutBounds.Max.X = horizontalExtent.Round() setter.layoutBoundsSpace.Max.X = horizontalExtentSpace.Round() y -= metrics.Height - if setter.maxHeight == 0 { + if setter.height == 0 { setter.layoutBounds.Min.Y = -metrics.Ascent.Round() setter.layoutBounds.Max.Y = y.Round() + @@ -85,7 +91,7 @@ func (setter *TypeSetter) needLayout () { } else { setter.layoutBounds.Min.Y = -metrics.Ascent.Round() setter.layoutBounds.Max.Y = - setter.maxHeight - + setter.height - metrics.Ascent.Round() } setter.layoutBoundsSpace.Min.Y = setter.layoutBounds.Min.Y @@ -98,13 +104,18 @@ func (setter *TypeSetter) needAlignedLayout () { setter.alignClean = true for index := range setter.lines { - align := setter.align - if align == AlignJustify { + align := setter.hAlign + + // if the horizontal align is even, align lines with breaks + // after them to the left anyways + if align == AlignEven { except := index == len(setter.lines) - 1 || setter.lines[index].BreakAfter - if except { align = AlignLeft } + if except { align = AlignStart } } + + // align line setter.lines[index].Align(align) } } @@ -118,9 +129,10 @@ func (setter *TypeSetter) SetWrap (wrap bool) { // SetAlign sets the alignment method of the typesetter. func (setter *TypeSetter) SetAlign (horizontal, vertical Align) { - if setter.align == horizontal { return } + if setter.hAlign == horizontal && setter.vAlign == vertical { return } setter.alignClean = false - setter.align = horizontal + setter.hAlign = horizontal + setter.vAlign = vertical } // SetText sets the text content of the typesetter. @@ -141,20 +153,20 @@ func (setter *TypeSetter) SetFace (face font.Face) { // SetWidth sets the width of the typesetter. Text will still be able // to overflow outside of this width if wrapping is disabled. func (setter *TypeSetter) SetWidth (width int) { - if setter.maxWidth == width { return } + if setter.width == width { return } setter.layoutClean = false setter.alignClean = false - setter.maxWidth = width + setter.width = width } // SetHeight sets the height of the typesetter. If the height is greater than // zero, no lines will be laid out past it. If the height is zero, the text's // maximum height will not be constrained. func (setter *TypeSetter) SetHeight (heignt int) { - if setter.maxHeight == heignt { return } + if setter.height == heignt { return } setter.layoutClean = false setter.alignClean = false - setter.maxHeight = heignt + setter.height = heignt } // Em returns the width of one emspace according to the typesetter's font, which @@ -173,12 +185,12 @@ func (setter *TypeSetter) LineHeight () fixed.Int26_6 { // Width returns the height of the typesetter as set by SetWidth. func (setter *TypeSetter) Width () int { - return setter.maxWidth + return setter.width } // Height returns the height of the typesetter as set by SetHeight. func (setter *TypeSetter) Height () int { - return setter.maxHeight + return setter.height } // Face returns the TypeSetter's font face as set by SetFace.