Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion primitives.go
Original file line number Diff line number Diff line change
Expand Up @@ -1859,7 +1859,9 @@ func (e *Engine) loadMore() {
e.prim("bibliography", func(e *Engine) { e.doBibliography() }) // read .bib, emit thebibliography
e.prim("gotex@putbib", func(e *Engine) { e.doPutbib() }) // bibunits: \input the current unit's bu<N>.bbl
e.prim("tabular", func(e *Engine) { e.doTabular() })
e.prim("endtabular", func(e *Engine) {}) // consumed by doTabular; defined for safety
e.prim("tabular*", func(e *Engine) { e.doTabularStar() })
e.prim("endtabular*", func(e *Engine) {}) // consumed by doTabularStar
e.prim("endtabular", func(e *Engine) {}) // consumed by doTabular; defined for safety
e.prim("tabularx", func(e *Engine) { e.doTabularx() })
e.prim("endtabularx", func(e *Engine) {}) // consumed by doTabularx; defined for safety
e.prim("minipage", func(e *Engine) { e.doMinipage() })
Expand Down
26 changes: 26 additions & 0 deletions tabular.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,32 @@ func (e *Engine) doTabular() {
e.place(e.buildTabularBox(aligns, pwidths, vrules, items))
}

// doTabularStar typesets a tabular* environment: \begin{tabular*}{W}[pos]{spec}.
// LaTeX reads the width and then IS tabular, with the alignment packed to it
// (latex.ltx:12092):
//
// \@namedef{tabular*}#1{\setlength\dimen@{#1}\edef\@halignto{to\the\dimen@}\@tabular}
//
// so the width is a target for the inter-column glue, and the body, the rules,
// \\ and & behave exactly as in tabular. Here the assembled box is packed to that
// width, which is what \extracolsep{\fill} in the preamble is asking for; when the
// natural table is wider, packing to a smaller width would overlap columns, so the
// natural box is kept (an overfull \hbox in TeX's terms).
//
// Undefined, its 10 uses in one IEEEtran paper each set their body as running text
// and the paper came out 43 pages against a reference of 27.
func (e *Engine) doTabularStar() {
width := e.readBraceDimen()
e.scanOptBracketToks() // optional [t]/[b]/[c], after the width for tabular*
aligns, pwidths, vrules := e.scanColSpec()
items := e.collectTabularBody("tabular*")
box := e.buildTabularBox(aligns, pwidths, vrules, items)
if width > box.width {
box = hpackSP([]node{box, glueNode{spec: glueSpec{stretch: unity, stretchOrder: 1}}}, packTo, width)
}
e.place(box)
}

// doTabularx typesets a tabularx environment: \begin{tabularx}{W}{spec}. Unlike
// tabular it takes a leading {width} argument (a rigid <dimen>, e.g. \hsize or
// \linewidth); every X column in {spec} is a paragraph column whose width is
Expand Down
40 changes: 39 additions & 1 deletion tabular_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package engine

import "testing"
import (
"strings"
"testing"
)

func lastVbox(e *Engine) *boxNode {
for i := len(e.mvl) - 1; i >= 0; i-- {
Expand Down Expand Up @@ -283,3 +286,38 @@ func TestTabularxOptionalPlacement(t *testing.T) {
}
}
}

// tabular* takes a {width} before its column spec (latex.ltx:12092) and is then
// tabular. Undefined, the width and the spec were typeset as running text and the
// body set as a paragraph: one IEEEtran paper used it ten times.
func TestTabularStarTakesItsWidthAndSetsATable(t *testing.T) {
e := New()
if err := e.LoadLaTeX(); err != nil {
t.Fatal(err)
}
e.SetFont(spMock{})
src := `\begin{tabular*}{200pt}{@{\extracolsep{\fill}}lr}` +
`un & deux \\ trois & quatre \\` +
`\end{tabular*}\par`
if _, err := e.Run(src); err != nil {
t.Fatal(err)
}
txt := mvlText(e.mvl)
for _, leak := range []string{"200pt", "extracolsep", "lr"} {
if strings.Contains(txt, leak) {
t.Errorf("%q leaked into the text: %q", leak, txt)
}
}
var box *boxNode // packed to the requested width, so an hbox around the table vbox
for _, n := range e.mvl {
if b, ok := n.(*boxNode); ok {
box = b
}
}
if box == nil {
t.Fatal("no table box was placed")
}
if box.width < ptToSP(200) {
t.Errorf("table width %d, want at least the requested 200pt (%d)", box.width, ptToSP(200))
}
}