// Copyright 2017-2022 The MathJax Consortium // SPDX-License-Identifier: Apache-2.0 // Source: BaseItems.ArrayItem.checkItem/EndEntry/EndRow/EndTable.
package tex import ( ) // Each array kind keeps its own callbacks. Entry and CR are emitted by the // parser, after command argument readers and dynamic maps have had ownership. // Fresh cell parsers implement ArrayItem.clearEnv without clearing Stack.global. type arrayBodyOwner struct { live bool environment *environmentFrame ordinary *ordinaryArrayItem requireClose bool rules *arrayRules configure func(*parser) prepare func(*parser) ([]*mml.Node, error) prepareEntry func(*parser, *cellItem) ([]*mml.Node, error) hasEntries func() bool endEntry func([]*mml.Node, *arrayCellState) error endRow func() error addSpacing func(string) } func ( *parser) ( string, arrayBodyOwner) error { := byte(0) if .requireClose { = '}' if !.live { += "}" } } := 0 if .live || .environment != nil { , = .source, .pos } var *parser defer func() { if (.live || .environment != nil) && != nil { .source, .pos = .source, .pos } }() var *cellItem for { := .matrixCellParser() = .pos = if .environment != nil { .environmentRow = .environment } else if .live { // A standalone Matrix Array is above the enclosing recipient; // it does not install an environment Begin of its own. .environmentRow = nil } if .live { .liveMatrix = true } .ordinaryArray = .ordinary .matrixClose, .cdArrayEntry = .requireClose, true .arrayCell.rules = .rules if .configure != nil { .configure() } var []*mml.Node var error if .prepare != nil { , = .prepare() if != nil { return } } if .prepareEntry != nil { , := .prepareEntry(, ) if != nil { return } = append(, ...) } , , := .parseRowContinuation(, false, false, nil, "", ) if != nil { return } if .ordinary != nil && .ordinary.popped { // Pop.toMml uses only current nodes, not completed row/table // buffers. Preserve their creation-time font before resuming // the surrounding Begin's original lexical environment. matrixCellContent() .ordinary.nodes = return nil } := .stoppedCell // EndTable omits only a pending empty final row. Explicit Entry and // CR always deliver their empty entry, and CR always ends its row. if == nil && len() == 0 && !.hasEntries() { return nil } if := .endEntry(, .arrayCell); != nil { return } if == nil || .cr { if := .endRow(); != nil { return } if != nil && .spacing != "" && .addSpacing != nil { .addSpacing(.spacing) } } if == nil { return nil } // Macro expansion may replace the current source. Do not resume a // substring of the original captured program or a pre-split row. if .live || .environment != nil { , = .source, .pos } else { , = .source[.pos:], 0 } = } } type arrayRowSpacing struct { rows int adjustments map[int]string } func ( *arrayRowSpacing) ( string) { if .adjustments == nil { .adjustments = make(map[int]string) } .adjustments[.rows-1] = } func ( *arrayRowSpacing) ( *mml.Node, string) { if .adjustments == nil { .Attributes.Set("rowspacing", ) return } := matrixDimensionEm() := make([]string, .rows) for := range { [] = arraySpacingEm(math.Max(0, +matrixDimensionEm(.adjustments[]))) } .Attributes.Set("rowspacing", strings.Join(, " ")) } // ArrayItem.addRowSpacing uses ParseUtil.Em, whose zero cutoff and zero // spelling differ from the output renderer's length formatter. func arraySpacingEm( float64) string { if math.Abs() < .0006 { return "0em" } return strings.TrimSuffix(strings.TrimRight(jscompat.ToFixed(, 3), "0"), ".") + "em" } func tableRowSpacing( *mml.Node) any { , := .Attributes.Get("rowspacing") return } func ( *parser) ( string, bool) (*mml.Node, *arrayRowSpacing, error) { := node("mtable") := &arrayRowSpacing{} var []*mml.Node := .parseArrayBody(, arrayBodyOwner{ rules: newArrayRules(), configure: func( *parser) { .arrayCell.multline = }, hasEntries: func() bool { return len() != 0 }, endEntry: func( []*mml.Node, *arrayCellState) error { if len(.Children) != 0 { = fixInitialMO() } := node("mtd", matrixCellContent()) if .shove != "" { .Attributes.Set("columnalign", .shove) } = append(, ) return nil }, endRow: func() error { if len() != 1 { return texError("MultlineRowsOneCol", "The rows within the %s environment must have exactly one column", "multline") } .AppendChild(node("mtr", ...)) = nil .rows++ return nil }, addSpacing: .add, }) return , , }