package tex
import (
"strings"
"github.com/d2lang/mathjax-go/internal/mml"
)
type equationRowState struct {
entries []*mml .Node
table *equationTableState
}
type equationTableState struct {
rules *arrayRules
rows int
flushAbove int
spacing []string
appendRow func (*mml .Node ) error
initialSpacing string
}
func newEquationTableState(appendRow func (*mml .Node ) error ) *equationTableState {
state := &equationTableState {flushAbove : -1 , appendRow : appendRow }
state .rules = &arrayRules {rows : func () int { return state .rows }}
return state
}
func (state *equationRowState ) endEntry (nodes []*mml .Node ) {
if len (state .entries ) != 0 {
nodes = fixInitialMO (nodes )
}
state .entries = append (state .entries , node ("mtd" , matrixCellContent (nodes )))
}
func (state *equationRowState ) endRow () error {
if err := state .table .appendRow (node ("mtr" , state .entries ...)); err != nil {
return err
}
state .entries = nil
state .table .rows ++
return nil
}
func (state *equationTableState ) addSpacing (adjust string ) {
initial , base := "3pt" , .3
if state .initialSpacing != "" {
initial , base = state .initialSpacing , matrixDimensionEm (state .initialSpacing )
}
if state .spacing == nil {
state .spacing = []string {initial }
}
for len (state .spacing ) < state .rows {
state .spacing = append (state .spacing , arraySpacingEm (base ))
}
if state .rows != 0 {
spacing := base + matrixDimensionEm (adjust )
if spacing < 0 {
spacing = 0
}
state .spacing [state .rows -1 ] = arraySpacingEm (spacing )
}
}
func (state *equationTableState ) applySpacing (table *mml .Node ) {
if state != nil {
table .SetProperty (arrayRulesProperty , state .rules )
}
if state != nil && state .spacing != nil {
initial := "0.3em"
if state .initialSpacing != "" {
initial = arraySpacingEm (matrixDimensionEm (state .initialSpacing ))
}
for len (state .spacing ) < state .rows {
state .spacing = append (state .spacing , initial )
}
table .Attributes .Set ("rowspacing" , strings .Join (state .spacing , " " ))
}
}
func notInAlignment(name string ) error {
return texError ("NotInAlignment" , "\\%s can only be used in aligment environments" , name )
}
func isMathtoolsArrayCommand(name string ) bool {
switch name {
case "ArrowBetweenLines" , "Aboxed" , "vdotswithin" , "shortvdotswithin" , "MTFlushSpaceAbove" , "MTFlushSpaceBelow" :
return true
}
return false
}
func (p *parser ) readMathtoolsStar () bool {
for p .pos < len (p .source ) && internalTextSpace (p .peekRune ()) {
p .consumeRune ()
}
star := p .pos < len (p .source ) && p .source [p .pos ] == '*'
if star {
p .pos ++
}
return star
}
func (p *parser ) readMathtoolsArgument (name string ) (string , error ) {
for p .pos < len (p .source ) && internalTextSpace (p .peekRune ()) {
p .consumeRune ()
}
argument , _ , err := p .readArgumentAtCursor (name , false )
return argument , err
}
func (p *parser ) arrowBetweenLines (name string ) error {
star := p .readMathtoolsStar ()
defaultArrow := "\\Updownarrow"
symbol , _ , err := p .readBrackets (&defaultArrow )
if err != nil {
if e , ok := err .(*Error ); ok && e .ID == "MissingCloseBracket" {
return texError (e .ID , "Could not find closing ']' for argument to \\%s" , name )
}
return err
}
state := p .arrayCell .equation
expansion := symbol + "\\quad"
if star {
state .endEntry (nil )
state .endEntry (nil )
expansion = "\\quad" + symbol
}
content , err := p .parseChild (expansion )
if err != nil {
return err
}
if p .activeFont != "" || p .fontExplicitEmpty {
applyScopedMathVariant (content , p .activeFont )
}
state .endEntry (unwrapInferred (content ))
return state .endRow ()
}
func (p *parser ) equationAboxed (name string ) error {
state := p .arrayCell .equation
if len (state .entries )%2 == 1 {
state .entries = append (state .entries , node ("mtd" ))
}
argument , err := p .readMathtoolsArgument (name )
if err != nil {
return err
}
rest := p .source [p .pos :]
p .source , p .pos = argument +"&&\\endAboxed" , 0
left , err := p .readUpTo (name , "&" )
if err != nil {
return err
}
right , err := p .readUpTo (name , "&" )
if err != nil {
return err
}
if _ , err := p .readUpTo (name , "\\endAboxed" ); err != nil {
return err
}
expansion := "\\rlap{\\boxed{" + left + "{}" + right + "}}\\kern.267em\\phantom{" + left + "}&\\phantom{{}" + right + "}\\kern.267em"
p .source , p .pos = expansion +rest , 0
return nil
}
func (p *parser ) equationVDots (name string , flush bool ) (*mml .Node , error ) {
argument , err := p .readMathtoolsArgument (name )
if err != nil {
return nil , err
}
base , err := p .parseChild ("\\mmlToken{mi}{}" + argument + "\\mmlToken{mi}{}" )
if err != nil {
return nil , err
}
inner := node ("mpadded" , p .token ("mo" , "⋮" ))
inner .Attributes .Set ("width" , 0 )
inner .Attributes .Set ("lspace" , "-.5width" )
if flush {
inner .Attributes .Set ("height" , "-.6em" )
inner .Attributes .Set ("voffset" , "-.18em" )
}
outer := node ("mpadded" , inner , node ("mphantom" , base ))
outer .Attributes .Set ("lspace" , ".5width" )
return outer , nil
}
func (p *parser ) parseEquationRow (cells []string , final bool , appendRow func (*mml .Node ) error , tableState ...*equationTableState ) error {
table := newEquationTableState (appendRow )
if len (tableState ) != 0 {
table = tableState [0 ]
}
source := strings .Join (cells , "&" )
if !final {
source += "\\cr"
}
return p .parseEquationBody (source , table )
}
func (p *parser ) parseEquationBody (source string , table *equationTableState ) error {
state := &equationRowState {table : table }
return p .parseArrayBody (source , arrayBodyOwner {
rules : table .rules ,
configure : func (sub *parser ) { sub .arrayCell .equation = state },
hasEntries : func () bool { return len (state .entries ) != 0 },
endEntry : func (children []*mml .Node , _ *arrayCellState ) error {
state .endEntry (children )
return nil
},
endRow : state .endRow ,
addSpacing : table .addSpacing ,
})
}
func isEquationArray(environment string ) bool {
switch environment {
case "align" , "align*" , "aligned" , "alignedat" , "alignat" , "alignat*" , "split" , "gather" , "gather*" , "gathered" , "lgathered" , "rgathered" :
return true
}
return false
}
func (p *parser ) parseEquationTable (body , environment string ) (*mml .Node , *equationTableState , error ) {
state := p .amsTags ()
taggable := environment == "alignat" || environment == "alignat*"
state .start (environment , taggable , environment == "alignat" )
defer state .end ()
var mrows , tags []*mml .Node
appendRow := func (row *mml .Node ) error {
mrows = append (mrows , row )
tag , err := state .getTag (p )
if err != nil {
return err
}
tags = append (tags , tag )
state .clearTag ()
return nil
}
spacing := newEquationTableState (appendRow )
if err := p .parseEquationBody (body , spacing ); err != nil {
return nil , nil , err
}
table := node ("mtable" , mrows ...)
for i , tag := range tags {
if tag != nil {
table .Children [i ] = node ("mlabeledtr" , append ([]*mml .Node {tag }, table .Children [i ].Children ...)...)
table .Children [i ].Parent = table
}
}
return table , spacing , nil
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .