Files
sjy01-image-proc/vendor/github.com/go-latex/latex/tex/box.go
2024-10-24 15:46:01 +08:00

1227 lines
30 KiB
Go

// Copyright ©2020 The go-latex Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tex
import (
"fmt"
"log"
"math"
"github.com/go-latex/latex/font"
)
const (
// How much text shrinks when going to the next-smallest level. growFactor
// must be the inverse of shrinkFactor.
shrinkFactor = 0.7
growFactor = 1.0 / shrinkFactor
// The number of different sizes of chars to use, beyond which they will not
// get any smaller
numSizeLevels = 6
)
// FontConstants is a set of magical values that control how certain things,
// such as sub- and superscripts are laid out.
// These are all metrics that can't be reliably retrieved from the font metrics
// in the font itself.
type FontConstants struct {
// Percentage of x-height of additional horiz. space after sub/superscripts
ScriptSpace float64
// Percentage of x-height that sub/superscripts drop below the baseline
SubDrop float64
// Percentage of x-height that superscripts are raised from the baseline
Sup1 float64
// Percentage of x-height that subscripts drop below the baseline
Sub1 float64
// Percentage of x-height that subscripts drop below the baseline when a
// superscript is present
Sub2 float64
// Percentage of x-height that sub/supercripts are offset relative to the
// nucleus edge for non-slanted nuclei
Delta float64
// Additional percentage of last character height above 2/3 of the
// x-height that supercripts are offset relative to the subscript
// for slanted nuclei
DeltaSlanted float64
// Percentage of x-height that supercripts and subscripts are offset for
// integrals
DeltaIntegral float64
}
var DefaultFontConstants = FontConstants{
ScriptSpace: 0.05,
SubDrop: 0.4,
Sup1: 0.7,
Sub1: 0.3,
Sub2: 0.5,
Delta: 0.025,
DeltaSlanted: 0.2,
DeltaIntegral: 0.1,
}
// Node represents a node in the TeX box model.
type Node interface {
// Kerning returns the amount of kerning between this and the next node.
Kerning(next Node) float64
// Shrinks one level smaller.
// There are only three levels of sizes, after which things
// will no longer get smaller.
Shrink()
// Grows one level larger.
// There is no limit to how big something can get.
Grow()
// Render renders the node at (x,y) on the canvas.
Render(x, y float64)
// Width returns the width of this node.
Width() float64
// Height returns the height of this node.
Height() float64
// Depth returns the depth of this node.
Depth() float64
}
// Box is a node with a physical location
type Box struct {
size int
width float64
height float64
depth float64
}
func newBox(w, h, d float64) *Box {
return &Box{width: w, height: h, depth: d}
}
func (*Box) Kerning(next Node) float64 { return 0 }
func (box *Box) Shrink() {
box.size--
if box.size < numSizeLevels {
box.width *= shrinkFactor
box.height *= shrinkFactor
box.depth *= shrinkFactor
}
}
func (box *Box) Grow() {
box.size++
box.width *= growFactor
box.height *= growFactor
box.depth *= growFactor
}
func (*Box) Render(x, y float64) {}
// Width returns the width of this node.
func (box *Box) Width() float64 { return box.width }
// Height returns the height of this node.
func (box *Box) Height() float64 { return box.height }
// Depth returns the depth of this node.
func (box *Box) Depth() float64 { return box.depth }
func (box *Box) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
*width += box.width
if math.IsInf(box.height, 0) || math.IsInf(box.depth, 0) {
return
}
*height = math.Max(*height, box.height)
*depth = math.Max(*depth, box.depth)
}
func (box *Box) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
*height += *depth + box.height
*depth = box.depth
if math.IsInf(box.width, 0) {
return
}
*width = math.Max(*width, box.width)
}
// VBox is a box with a height but no width.
func VBox(h, d float64) *Box {
return newBox(0, h, d)
}
// HBox is a box with a width but no height nor depth.
func HBox(w float64) *Box {
return newBox(w, 0, 0)
}
// Char is a single character.
//
// Unlike TeX, the font information and metrics are stored with each `Char`
// to make it easier to lookup the font metrics when needed. Note that TeX
// boxes have a width, height, and depth, unlike Type1 and TrueType which use
// a full bounding box and an advance in the x-direction. The metrics must
// be converted to the TeX model, and the advance (if different from width)
// must be converted into a `Kern` node when the `Char` is added to its parent
// `HList`.
type Char struct {
c string
size int
width float64
height float64
depth float64
metrics font.Metrics
be font.Backend
font font.Font
dpi float64
math bool
}
func NewChar(c string, state State, math bool) *Char {
ch := &Char{
c: c,
be: state.Backend(),
font: state.Font,
dpi: state.DPI,
math: math,
}
ch.updateMetrics()
return ch
}
func (ch *Char) updateMetrics() {
ch.metrics = ch.be.Metrics(
ch.c, ch.font, ch.dpi,
ch.math,
)
switch ch.c {
case " ":
ch.width = ch.metrics.Advance
default:
ch.width = ch.metrics.Width
}
ch.height = ch.metrics.Iceberg
ch.depth = -(ch.metrics.Iceberg - ch.metrics.Height)
}
func (c *Char) String() string { return c.c }
func (c *Char) Kerning(next Node) float64 {
adv := c.metrics.Advance - c.Width()
kern := 0.0
switch next := next.(type) {
case *Char:
kern = c.be.Kern(c.font, c.c, next.font, next.c, c.dpi)
case *Accent:
kern = c.be.Kern(c.font, c.c, next.char.font, next.char.c, c.dpi)
}
return adv + kern
}
func (box *Char) Shrink() {
box.size--
if box.size < numSizeLevels {
box.font.Size *= shrinkFactor
box.width *= shrinkFactor
box.height *= shrinkFactor
box.depth *= shrinkFactor
}
}
func (box *Char) Grow() {
box.size++
box.font.Size *= growFactor
box.width *= growFactor
box.height *= growFactor
box.depth *= growFactor
}
func (c *Char) Render(x, y float64) {
c.be.RenderGlyph(x, y, c.font, c.c, c.dpi)
}
// Width returns the width of this node.
func (c *Char) Width() float64 { return c.width }
// Height returns the height of this node.
func (c *Char) Height() float64 { return c.height }
// Depth returns the depth of this node.
func (c *Char) Depth() float64 { return c.depth }
func (c Char) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
*width += c.width
*height = math.Max(*height, c.height)
*depth = math.Max(*depth, c.depth)
}
func (*Char) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
panic("Char node in VList")
}
// Accent is a character with an accent.
// Accents need to be dealt with separately as they are already offset
// from the baseline in TrueType fonts.
type Accent struct {
char Char
}
func NewAccent(c string, state State, math bool) *Accent {
acc := &Accent{
char: Char{
c: c,
be: state.Backend(),
font: state.Font,
dpi: state.DPI,
math: math,
},
}
acc.updateMetrics()
return acc
}
func (acc *Accent) updateMetrics() {
acc.char.metrics = acc.char.be.Metrics(
acc.char.c, acc.char.font, acc.char.dpi,
acc.char.math,
)
acc.char.width = acc.char.metrics.XMax - acc.char.metrics.XMin
acc.char.height = acc.char.metrics.YMax - acc.char.metrics.YMin
acc.char.depth = 0
}
func (acc *Accent) String() string { return acc.char.String() }
func (acc *Accent) Kerning(next Node) float64 { return acc.char.Kerning(next) }
func (acc *Accent) Shrink() {
acc.char.Shrink()
acc.updateMetrics()
}
func (acc *Accent) Grow() {
acc.char.Grow()
acc.updateMetrics()
}
func (acc *Accent) Render(x, y float64) {
acc.char.be.RenderGlyph(
x-acc.char.metrics.XMin,
y+acc.char.metrics.YMin,
acc.char.font,
acc.char.c,
acc.char.dpi,
)
}
// Width returns the width of this node.
func (acc *Accent) Width() float64 { return acc.char.width }
// Height returns the height of this node.
func (acc *Accent) Height() float64 { return acc.char.height }
// Depth returns the depth of this node.
func (acc *Accent) Depth() float64 { return acc.char.depth }
func (acc *Accent) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
acc.char.hpackDims(width, height, depth, stretch, shrink)
}
func (*Accent) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
panic("Accent node in VList")
}
// List is a list of vertical or horizontal nodes.
type List struct {
box Box
shift float64 // shift is an arbitrary offset.
children []Node // children nodes of this list.
glue struct {
set float64 // glue setting of this list
sign int // 0: normal, -1: shrinking, 1: stretching
order int // the order of infinity (0 - 3) for the glue.
ratio float64
}
}
func ListOf(elements []Node) *List {
list := &List{children: make([]Node, len(elements))}
copy(list.children, elements)
return list
}
// determineOrder determines the highest order of glue used by the members
// of a List.
//
// used by VPack and HPack.
func determineOrder(totals []float64) int {
for i := len(totals) - 1; i >= 0; i-- {
if totals[i] != 0 {
return i
}
}
return 0
}
func (lst *List) setGlue(x float64, sign int, totals []float64, errMsg, typ string) {
o := determineOrder(totals)
lst.glue.order = o
lst.glue.sign = sign
switch {
case totals[o] != 0:
lst.glue.set = x / totals[o]
default:
lst.glue.sign = 0
lst.glue.ratio = 0
}
if o == 0 {
if len(lst.children) > 0 {
log.Printf("%s %s: %v", errMsg, typ, lst.children)
}
}
}
func (lst *List) Kerning(next Node) float64 {
return lst.box.Kerning(next)
}
func (lst *List) Shrink() {
for _, node := range lst.children {
node.Shrink()
}
lst.box.Shrink()
if lst.box.size < numSizeLevels {
lst.shift *= shrinkFactor
lst.glue.set *= shrinkFactor
}
}
func (lst *List) Grow() {
for _, node := range lst.children {
node.Grow()
}
lst.box.Grow()
lst.shift *= growFactor
lst.glue.set *= growFactor
}
func (lst *List) Render(x, y float64) {
lst.box.Render(x, y)
}
// Width returns the width of this node.
func (lst *List) Width() float64 { return lst.box.Width() }
// Height returns the height of this node.
func (lst *List) Height() float64 { return lst.box.Height() }
// Depth returns the depth of this node.
func (lst *List) Depth() float64 { return lst.box.Depth() }
func (lst *List) Nodes() []Node { return lst.children }
func (lst *List) GlueOrder() int { return lst.glue.order }
func (lst *List) GlueSign() int { return lst.glue.sign }
func (lst *List) GlueSet() float64 { return lst.glue.set }
func (lst *List) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
*width += lst.box.width
if math.IsInf(lst.box.height, 0) || math.IsInf(lst.box.depth, 0) {
return
}
*height = math.Max(*height, lst.box.height-lst.shift)
*depth = math.Max(*depth, lst.box.depth+lst.shift)
}
func (lst *List) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
*height += *depth + lst.box.height
*depth = lst.box.depth
if math.IsInf(lst.box.width, 0) {
return
}
*width = math.Max(*width, lst.box.width)
}
// HList is a horizontal list of boxes.
type HList struct {
lst List
}
func HListOf(elements []Node, doKern bool) *HList {
lst := &HList{
lst: *ListOf(elements),
}
if doKern {
lst.kern()
}
const (
width = 0
additional = true
)
lst.HPack(width, additional)
return lst
}
// kern inserts Kern nodes between Char nodes to set kerning.
//
// The Char nodes themselves determine the amount of kerning they need.
// This method just creates the correct list.
func (lst *HList) kern() {
if len(lst.lst.children) == 0 {
return
}
var (
n = len(lst.lst.children)
children = make([]Node, 0, n)
)
for i := range lst.lst.children {
var (
elem = lst.lst.children[i]
next Node
dist float64
)
if i < n-1 {
next = lst.lst.children[i+1]
}
dist = elem.Kerning(next)
children = append(children, elem)
if dist != 0 {
children = append(children, NewKern(dist))
}
}
lst.lst.children = children
}
// HPack computes the dimensions of the resulting boxes, and adjusts the glue
// if one of those dimensions is pre-specified.
//
// The computed sizes normally enclose all of the material inside the new box;
// but some items may stick out if negative glue is used, if the box is
// overfull, or if a `\vbox` includes other boxes that have been shifted left.
//
// If additional is false, HPack will produce a box whose width is exactly as
// wide as the given 'width'.
// Otherwise, HPack will produce a box with the natural width of the contents,
// plus the given 'width'.
func (lst *HList) HPack(width float64, additional bool) {
var (
h float64
d float64
x float64
totStretch = make([]float64, 4)
totShrink = make([]float64, 4)
)
for _, node := range lst.lst.children {
switch node := node.(type) {
case hpacker:
node.hpackDims(&x, &h, &d, totStretch, totShrink)
default:
panic(fmt.Errorf("unknown node type %T", node))
}
}
lst.lst.box.height = h
lst.lst.box.depth = d
if additional {
width += x
}
lst.lst.box.width = width
x = width - x
switch {
case x == 0:
lst.lst.glue.sign = 0
lst.lst.glue.order = 0
lst.lst.glue.ratio = 0
case x > 0:
lst.lst.setGlue(x, 1, totStretch, "overfull", "HList")
default:
lst.lst.setGlue(x, -1, totShrink, "underfull", "HList")
}
}
func (lst *HList) Kerning(next Node) float64 { return lst.lst.Kerning(next) }
func (lst *HList) Shrink() { lst.lst.Shrink() }
func (lst *HList) Grow() { lst.lst.Grow() }
func (lst *HList) Render(x, y float64) { lst.lst.Render(x, x) }
// Width returns the width of this node.
func (lst *HList) Width() float64 { return lst.lst.Width() }
// Height returns the height of this node.
func (lst *HList) Height() float64 { return lst.lst.Height() }
// Depth returns the depth of this node.
func (lst *HList) Depth() float64 { return lst.lst.Depth() }
func (lst *HList) Nodes() []Node { return lst.lst.Nodes() }
func (lst *HList) GlueOrder() int { return lst.lst.GlueOrder() }
func (lst *HList) GlueSign() int { return lst.lst.GlueSign() }
func (lst *HList) GlueSet() float64 { return lst.lst.GlueSet() }
func (lst *HList) Shift() float64 { return lst.lst.shift }
func (lst *HList) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
lst.lst.hpackDims(width, height, depth, stretch, shrink)
}
func (lst *HList) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
lst.lst.vpackDims(width, height, depth, stretch, shrink)
}
// VList is a vertical list of boxes.
type VList struct {
lst List
}
func (lst *VList) SetShift(s float64) { lst.lst.shift = s }
func VListOf(elements []Node) *VList {
lst := &VList{lst: *ListOf(elements)}
var (
height float64
additional = true
max = math.Inf(+1)
)
lst.VPack(height, additional, max)
return lst
}
// VPack computes the dimensions of the resulting boxes, and adjusts the
// glue if one of those dimensions is pre-specified.
//
// If additional is false, VPack will produce a box whose height is exactly as
// tall as the given 'height'.
// Otherwise, VPack will produce a box with the natural height of the contents,
// plus the given 'height'.
func (lst *VList) VPack(height float64, additional bool, l float64) {
var (
w float64
d float64
x float64
totStretch = make([]float64, 4)
totShrink = make([]float64, 4)
)
for _, node := range lst.lst.children {
switch node := node.(type) {
case vpacker:
node.vpackDims(&w, &x, &d, totStretch, totShrink)
}
}
lst.lst.box.width = w
switch {
case d > l:
x += d - l
lst.lst.box.depth = l
default:
lst.lst.box.depth = d
}
if additional {
height += x
}
lst.lst.box.height = height
x = height - x
switch {
case x == 0:
lst.lst.glue.sign = 0
lst.lst.glue.order = 0
lst.lst.glue.ratio = 0
case x > 0:
lst.lst.setGlue(x, +1, totStretch, "overfull", "VList")
default:
lst.lst.setGlue(x, -1, totShrink, "underfull", "VList")
}
}
func (lst *VList) Kerning(next Node) float64 { return lst.lst.Kerning(next) }
func (lst *VList) Shrink() { lst.lst.Shrink() }
func (lst *VList) Grow() { lst.lst.Grow() }
func (lst *VList) Render(x, y float64) { lst.lst.Render(x, y) }
// Width returns the width of this node.
func (lst *VList) Width() float64 { return lst.lst.Width() }
// Height returns the height of this node.
func (lst *VList) Height() float64 { return lst.lst.Height() }
// Depth returns the depth of this node.
func (lst *VList) Depth() float64 { return lst.lst.Depth() }
func (lst *VList) Nodes() []Node { return lst.lst.Nodes() }
func (lst *VList) GlueOrder() int { return lst.lst.GlueOrder() }
func (lst *VList) GlueSign() int { return lst.lst.GlueSign() }
func (lst *VList) GlueSet() float64 { return lst.lst.GlueSet() }
func (lst *VList) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
lst.lst.hpackDims(width, height, depth, stretch, shrink)
}
func (lst *VList) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
lst.lst.vpackDims(width, height, depth, stretch, shrink)
}
// Rule is a solid black rectangle.
//
// Like a HList, Rule has a width, a depth and a height.
// However, if any of these dimensions is ∞, the actual value will be
// determined by running the rule up to the boundary of the innermost
// enclosing box.
// This is called a "running dimension".
// The width is never running in an HList; the height and depth are never
// running in a VList.
type Rule struct {
box Box
out font.Backend
}
func NewRule(w, h, d float64, state State) *Rule {
return &Rule{
box: *newBox(w, h, d),
out: state.Backend(),
}
}
func (rule *Rule) String() string {
return fmt.Sprintf(
"Rule{w=%g, h=%g, d=%g}",
rule.Width(), rule.Height(), rule.Depth(),
)
}
func (rule *Rule) render(x, y, w, h float64) {
rule.out.RenderRectFilled(x, y, x+w, y+h)
}
func (rule *Rule) Kerning(next Node) float64 { return rule.box.Kerning(next) }
func (rule *Rule) Shrink() { rule.box.Shrink() }
func (rule *Rule) Grow() { rule.box.Grow() }
func (rule *Rule) Render(x, y float64) { rule.box.Render(x, y) }
// Width returns the width of this node.
func (rule *Rule) Width() float64 { return rule.box.Width() }
// Height returns the height of this node.
func (rule *Rule) Height() float64 { return rule.box.Height() }
// Depth returns the depth of this node.
func (rule *Rule) Depth() float64 { return rule.box.Depth() }
func (rule *Rule) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
rule.box.hpackDims(width, height, depth, stretch, shrink)
}
func (rule *Rule) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
rule.box.vpackDims(width, height, depth, stretch, shrink)
}
// HRule is a horizontal rule.
func HRule(state State, thickness float64) *Rule {
if thickness < 0 {
thickness = state.Backend().UnderlineThickness(state.Font, state.DPI)
}
var (
height = 0.5 * thickness
depth = 0.5 * thickness
)
return NewRule(math.Inf(+1), height, depth, state)
}
// VRule is a vertical rule.
func VRule(state State) *Rule {
thickness := state.Backend().UnderlineThickness(state.Font, state.DPI)
return NewRule(thickness, math.Inf(+1), math.Inf(+1), state)
}
type Glue struct {
size int
width float64
stretch float64
stretchOrder int
shrink float64
shrinkOrder int
}
func NewGlue(typ string) *Glue {
switch typ {
case "fil":
return newGlue(0, 1, 1, 0, 0)
case "fill":
return newGlue(0, 1, 2, 0, 0)
case "filll":
return newGlue(0, 1, 3, 0, 0)
case "neg_fil":
return newGlue(0, 0, 0, 1, 1)
case "neg_fill":
return newGlue(0, 0, 0, 1, 2)
case "neg_filll":
return newGlue(0, 0, 0, 1, 3)
case "empty":
return &Glue{}
case "ss":
return newGlue(0, 1, 1, -1, 1)
default:
panic(fmt.Errorf("tex: unknown Glue spec %q", typ))
}
}
func newGlue(w, st float64, sto int, sh float64, sho int) *Glue {
return &Glue{
size: 0,
width: w,
stretch: st,
stretchOrder: sto,
shrink: sh,
shrinkOrder: sho,
}
}
func (g *Glue) Kerning(next Node) float64 { return 0 }
func (g *Glue) Shrink() {
g.size--
if g.size < numSizeLevels {
g.width *= shrinkFactor
}
}
func (g *Glue) Grow() {
g.size++
g.width *= growFactor
}
func (g *Glue) Render(x, y float64) {}
// Width returns the width of this node.
func (g *Glue) Width() float64 { return g.width }
// Height returns the height of this node.
func (g *Glue) Height() float64 { return 0 }
// Depth returns the depth of this node.
func (g *Glue) Depth() float64 { return 0 }
func (g *Glue) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
*width += g.width
stretch[g.stretchOrder] += g.stretch
shrink[g.shrinkOrder] += g.shrink
}
func (g *Glue) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
*height += *depth
*depth = 0
*height += g.width
stretch[g.stretchOrder] += g.stretch
shrink[g.shrinkOrder] += g.shrink
}
// HCentered creates an HList whose contents are centered within
// its enclosing box.
func HCentered(elements []Node) *HList {
const doKern = false
nodes := make([]Node, 0, len(elements)+2)
nodes = append(nodes, NewGlue("ss"))
nodes = append(nodes, elements...)
nodes = append(nodes, NewGlue("ss"))
return HListOf(nodes, doKern)
}
// VCentered creates a VList whose contents are centered within
// its enclosing box.
func VCentered(elements []Node) *VList {
nodes := make([]Node, 0, len(elements)+2)
nodes = append(nodes, NewGlue("ss"))
nodes = append(nodes, elements...)
nodes = append(nodes, NewGlue("ss"))
return VListOf(nodes)
}
// Kern is a node with a width to specify a (normally negative) amount of spacing.
//
// This spacing correction appears in horizontal lists between letters
// like A and V, when the font designer decided it looks better to move them
// closer together or further apart.
// A Kern node can also appear in a vertical list, when its width denotes
// spacing in the vertical direction.
type Kern struct {
size int
width float64
}
func NewKern(width float64) *Kern {
return &Kern{width: width}
}
func (k *Kern) String() string { return fmt.Sprintf("k%.02f", k.width) }
func (k *Kern) Kerning(next Node) float64 { return 0 }
func (k *Kern) Shrink() {
k.size--
if k.size < numSizeLevels {
k.width *= shrinkFactor
}
}
func (k *Kern) Grow() {
k.size++
k.width *= growFactor
}
func (k *Kern) Render(x, y float64) {}
// Width returns the width of this node.
func (k *Kern) Width() float64 { return k.width }
// Height returns the height of this node.
func (k *Kern) Height() float64 { return 0 }
// Depth returns the depth of this node.
func (k *Kern) Depth() float64 { return 0 }
func (k *Kern) hpackDims(width, height, depth *float64, stretch, shrink []float64) {
*width += k.width
}
func (k *Kern) vpackDims(width, height, depth *float64, stretch, shrink []float64) {
*height += *depth + k.width
*depth = 0
}
type SubSuperCluster struct {
*HList
// nucleus interface{} // FIXME
// sub interface{} // FIXME
// super interface{} // FIXME
}
// AutoHeightChar creats a character as close to the given height and depth
// as possible.
func AutoHeightChar(c string, height, depth float64, state State, factor float64) *HList {
// FIXME(sbinet): implement sized-alternatives-for-symbol
alts := []struct {
font string
sym string
}{
{state.Font.Name, c},
}
const math = true
var (
xheight = state.Backend().XHeight(state.Font, state.DPI)
target = height + depth
ch *Char
shift float64
)
for _, v := range alts {
state.Font.Name = v.font
ch = NewChar(c, state, math)
// ensure that size 0 is chosen when the text is regular sized
// but with descender glyphs by subtracting 0.2*xheight
if ch.Height()+ch.Depth() >= target-0.2*xheight {
break
}
}
if state.Font.Name != "" {
if factor == 0 {
factor = target / (ch.Height() + ch.Depth())
}
state.Font.Size *= factor
ch = NewChar(c, state, math)
shift = depth - ch.Depth()
}
hlist := HListOf([]Node{ch}, true)
hlist.lst.shift = shift
return hlist
}
// Ship boxes to output once boxes have been set up.
//
// Since boxes can be inside of boxes inside of boxes... the main work of
// Ship is done by two mutually recursive routines, hlistOut and vlistOut,
// which traverse the HList and VList nodes inside of horizontal and vertical
// boxes.
type Ship struct {
maxPush int // deepest nesting of push commands, so far.
cur struct {
s int
v float64
h float64
}
off struct {
h float64
v float64
}
}
func (ship *Ship) Call(ox, oy float64, box Tree) {
ship.maxPush = 0
ship.cur.s = 0
ship.cur.v = 0
ship.cur.h = 0
ship.off.h = ox
ship.off.v = oy + box.Height()
ship.hlistOut(box)
}
func (ship *Ship) hlistOut(box Tree) {
var (
curG int
curGlue float64
glueOrder = box.GlueOrder()
glueSign = box.GlueSign()
baseLine = ship.cur.v
)
ship.cur.s++
ship.maxPush = maxInt(ship.cur.s, ship.maxPush)
for _, node := range box.Nodes() {
switch node := node.(type) {
case *Char:
node.Render(ship.cur.h+ship.off.h, ship.cur.v+ship.off.v)
ship.cur.h += node.Width()
case *Accent:
node.Render(ship.cur.h+ship.off.h, ship.cur.v+ship.off.v)
ship.cur.h += node.Width()
case *Kern:
ship.cur.h += node.Width()
case *HList:
// node623
switch len(node.Nodes()) {
case 0:
ship.cur.h += node.Width()
default:
edge := ship.cur.h
ship.cur.v = baseLine + node.lst.shift
ship.hlistOut(node)
ship.cur.h = edge + node.Width()
ship.cur.v = baseLine
}
case *VList:
// node623
switch len(node.Nodes()) {
case 0:
ship.cur.h += node.Width()
default:
edge := ship.cur.h
ship.cur.v = baseLine + node.lst.shift
ship.vlistOut(node)
ship.cur.h = edge + node.Width()
ship.cur.v = baseLine
}
case *Glue:
// node625
ruleWidth := node.width - float64(curG)
if glueSign != 0 { // normal
switch {
case glueSign == 1: // stretching
if node.stretchOrder == glueOrder {
curGlue += node.stretch
curG = int(math.Round(clamp(box.GlueSet() * curGlue)))
}
case node.shrinkOrder == glueOrder: // shrinking
curGlue += node.shrink
curG = int(math.Round(clamp(box.GlueSet() * curGlue)))
}
}
ruleWidth += float64(curG)
ship.cur.h += ruleWidth
case Node:
// node624
ruleHeight := node.Height()
ruleDepth := node.Depth()
ruleWidth := node.Width()
if math.IsInf(ruleHeight, 0) {
ruleHeight = box.Height()
}
if math.IsInf(ruleDepth, 0) {
ruleDepth = box.Depth()
}
if ruleHeight > 0 && ruleWidth > 0 {
ship.cur.v = baseLine + ruleDepth
type renderXYWH interface {
render(x, y, w, h float64)
}
node.(renderXYWH).render(
ship.cur.h+ship.off.h,
ship.cur.v+ship.off.v,
ruleWidth, ruleHeight,
)
ship.cur.v = baseLine
}
ship.cur.h += ruleWidth
}
}
ship.cur.s--
}
func (ship *Ship) vlistOut(box Tree) {
var (
curG int
curGlue float64
glueOrder = box.GlueOrder()
glueSign = box.GlueSign()
leftEdge = ship.cur.h
)
ship.cur.s++
ship.maxPush = maxInt(ship.cur.s, ship.maxPush)
ship.cur.v -= box.Height()
for _, node := range box.Nodes() {
switch node := node.(type) {
case *Kern:
ship.cur.v += node.Width()
case *HList:
switch len(node.Nodes()) {
case 0:
ship.cur.v += node.Height() + node.Depth()
default:
ship.cur.v += node.Height()
ship.cur.h = leftEdge + node.lst.shift
curV := ship.cur.v
node.lst.box.width = box.Width()
ship.hlistOut(node)
ship.cur.v = curV + node.Depth()
ship.cur.h = leftEdge
}
case *VList:
switch len(node.Nodes()) {
case 0:
ship.cur.v += node.Height() + node.Depth()
default:
ship.cur.v += node.Height()
ship.cur.h = leftEdge + node.lst.shift
curV := ship.cur.v
node.lst.box.width = box.Width()
ship.vlistOut(node)
ship.cur.v = curV + node.Depth()
ship.cur.h = leftEdge
}
case *Glue:
ruleHeight := node.width - float64(curG)
if glueSign != 0 { // normal
switch {
case glueSign == 1: // stretching
if node.stretchOrder == glueOrder {
curGlue += node.stretch
curG = int(math.Round(clamp(box.GlueSet() * curGlue)))
}
case glueOrder == node.shrinkOrder: // shrinking
curGlue += node.shrink
curG = int(math.Round(clamp(box.GlueSet() * curGlue)))
}
}
ruleHeight += float64(curG)
ship.cur.v += ruleHeight
case *Char:
panic("tex: Char node found in vlist")
case *Accent:
panic("tex: Accent node found in vlist")
case Node:
var (
ruleHeight = node.Height()
ruleDepth = node.Depth()
ruleWidth = node.Width()
)
if math.IsInf(ruleWidth, 0) {
ruleWidth = box.Width()
}
ruleHeight += ruleDepth
if ruleHeight > 0 && ruleDepth > 0 {
ship.cur.v += ruleHeight
type renderXYWH interface {
render(x, y, w, h float64)
}
if p, ok := node.(renderXYWH); ok {
p.render(
ship.cur.h+ship.off.h,
ship.cur.v+ship.off.v,
ruleWidth, ruleHeight,
)
}
}
}
}
ship.cur.s--
}
type hpacker interface {
hpackDims(width, height, depth *float64, stretch, shrink []float64)
}
type vpacker interface {
vpackDims(width, height, depth *float64, stretch, shrink []float64)
}
type Tree interface {
Node
Nodes() []Node
GlueOrder() int
GlueSign() int
GlueSet() float64
}
var (
_ Node = (*Box)(nil)
_ Node = (*Char)(nil)
_ Node = (*Accent)(nil)
_ Node = (*List)(nil)
_ Node = (*HList)(nil)
_ Node = (*VList)(nil)
_ Node = (*Rule)(nil)
_ Node = (*Glue)(nil)
_ Node = (*Kern)(nil)
_ Node = (*SubSuperCluster)(nil)
_ hpacker = (*Box)(nil)
_ hpacker = (*Char)(nil)
_ hpacker = (*Accent)(nil)
_ hpacker = (*List)(nil)
_ hpacker = (*HList)(nil)
_ hpacker = (*VList)(nil)
_ hpacker = (*Rule)(nil)
_ hpacker = (*Glue)(nil)
_ hpacker = (*Kern)(nil)
_ hpacker = (*SubSuperCluster)(nil)
_ vpacker = (*Box)(nil)
_ vpacker = (*Char)(nil)
_ vpacker = (*Accent)(nil)
_ vpacker = (*List)(nil)
_ vpacker = (*HList)(nil)
_ vpacker = (*VList)(nil)
_ vpacker = (*Rule)(nil)
_ vpacker = (*Glue)(nil)
_ vpacker = (*Kern)(nil)
_ vpacker = (*SubSuperCluster)(nil)
_ Tree = (*List)(nil)
_ Tree = (*HList)(nil)
_ Tree = (*VList)(nil)
)