diff --git a/src/buildHTML.js b/src/buildHTML.js index b1b023243..7f0dcbbc1 100644 --- a/src/buildHTML.js +++ b/src/buildHTML.js @@ -530,9 +530,11 @@ var groupTypes = { var inrow = group.value.body[r]; var height = arstrutHeight; // \@array adds an \@arstrut var depth = arstrutDepth; // to each tow (via the template) + if (nc < inrow.length) { nc = inrow.length; } + var outrow = new Array(inrow.length); for (c = 0; c < inrow.length; ++c) { var elt = buildGroup(inrow[c], options); @@ -544,6 +546,7 @@ var groupTypes = { } outrow[c] = elt; } + var gap = 0; if (group.value.rowGaps[r]) { gap = group.value.rowGaps[r].value; @@ -566,6 +569,7 @@ var groupTypes = { gap = 0; } } + outrow.height = height; outrow.depth = depth; totalHeight += height; @@ -573,21 +577,64 @@ var groupTypes = { totalHeight += depth + gap; // \@yargarraycr body[r] = outrow; } + var offset = totalHeight / 2 + fontMetrics.metrics.axisHeight; - var coldescriptions = group.value.cols || []; + var colDescriptions = group.value.cols || []; var cols = []; - var colsep; - for (c = 0; c < nc; ++c) { - var coldescr = coldescriptions[c] || {}; + var colSep; + var colDescrNum; + for (c = 0, colDescrNum = 0; + // Continue while either there are more columns or more column + // descriptions, so trailing separators don't get lost. + c < nc || colDescrNum < colDescriptions.length; + ++c, ++colDescrNum) { + + var colDescr = colDescriptions[colDescrNum] || {}; + + var firstSeparator = true; + while (colDescr.type === "separator") { + // If there is more than one separator in a row, add a space + // between them. + if (!firstSeparator) { + colSep = makeSpan(["arraycolsep"], []); + colSep.style.width = + fontMetrics.metrics.doubleRuleSep + "em"; + cols.push(colSep); + } + + if (colDescr.separator === "|") { + var separator = makeSpan( + ["vertical-separator"], + []); + separator.style.height = totalHeight + "em"; + separator.style.verticalAlign = + -(totalHeight - offset) + "em"; + + cols.push(separator); + } else { + throw new ParseError( + "Invalid separator type: " + colDescr.separator); + } + + colDescrNum++; + colDescr = colDescriptions[colDescrNum] || {}; + firstSeparator = false; + } + + if (c >= nc) { + continue; + } + var sepwidth; if (c > 0 || group.value.hskipBeforeAndAfter) { - sepwidth = utils.deflt(coldescr.pregap, arraycolsep); + sepwidth = utils.deflt(colDescr.pregap, arraycolsep); if (sepwidth !== 0) { - colsep = makeSpan(["arraycolsep"], []); - colsep.style.width = sepwidth + "em"; - cols.push(colsep); + colSep = makeSpan(["arraycolsep"], []); + colSep.style.width = sepwidth + "em"; + cols.push(colSep); } } + var col = []; for (r = 0; r < nr; ++r) { var row = body[r]; @@ -600,17 +647,19 @@ var groupTypes = { elem.height = row.height; col.push({type: "elem", elem: elem, shift: shift}); } + col = buildCommon.makeVList(col, "individualShift", null, options); col = makeSpan( - ["col-align-" + (coldescr.align || "c")], + ["col-align-" + (colDescr.align || "c")], [col]); cols.push(col); + if (c < nc - 1 || group.value.hskipBeforeAndAfter) { - sepwidth = utils.deflt(coldescr.postgap, arraycolsep); + sepwidth = utils.deflt(colDescr.postgap, arraycolsep); if (sepwidth !== 0) { - colsep = makeSpan(["arraycolsep"], []); - colsep.style.width = sepwidth + "em"; - cols.push(colsep); + colSep = makeSpan(["arraycolsep"], []); + colSep.style.width = sepwidth + "em"; + cols.push(colSep); } } } diff --git a/src/environments.js b/src/environments.js index 7575a158f..ff294ca7a 100644 --- a/src/environments.js +++ b/src/environments.js @@ -65,14 +65,19 @@ var environmentDefinitions = [ numArgs: 1, handler: function(pos, mode, envName, colalign, positions) { var parser = this; - // Currently only supports alignment, no separators like | yet. colalign = colalign.value.map ? colalign.value : [colalign]; var cols = colalign.map(function(node) { var ca = node.value; if ("lcr".indexOf(ca) !== -1) { return { + type: "align", align: ca }; + } else if (ca === "|") { + return { + type: "separator", + separator: "|" + }; } throw new ParseError( "Unknown column alignment: " + node.value, @@ -134,10 +139,12 @@ var environmentDefinitions = [ type: "array", arraystretch: 1.2, cols: [{ + type: "align", align: "l", pregap: 0, postgap: fontMetrics.metrics.quad }, { + type: "align", align: "l", pregap: 0, postgap: 0 diff --git a/src/fontMetrics.js b/src/fontMetrics.js index faa046f3f..b66bc276a 100644 --- a/src/fontMetrics.js +++ b/src/fontMetrics.js @@ -67,6 +67,10 @@ var xi13 = 0.1; // match. var ptPerEm = 10.0; +// The space between adjacent `|` columns in an array definition. From +// `\showthe\doublerulesep` in LaTeX. +var doubleRuleSep = 2.0 / ptPerEm; + /** * This is just a mapping from common names to real metrics */ @@ -94,6 +98,7 @@ var metrics = { bigOpSpacing5: xi13, ptPerEm: ptPerEm, emPerEx: sigma5 / sigma6, + doubleRuleSep: doubleRuleSep, // TODO(alpert): Missing parallel structure here. We should probably add // style-specific metrics for all of these. diff --git a/static/katex.less b/static/katex.less index 43d6d7450..626aab74f 100644 --- a/static/katex.less +++ b/static/katex.less @@ -497,20 +497,28 @@ } } - .arraycolsep { - display: inline-block; - } + .mtable { + .vertical-separator { + display: inline-block; + margin: 0 -0.025em; + border-right: 0.05em solid black; + } - .col-align-c > .vlist { - text-align: center; - } + .arraycolsep { + display: inline-block; + } - .col-align-l > .vlist { - text-align: left; - } + .col-align-c > .vlist { + text-align: center; + } - .col-align-r > .vlist { - text-align: right; + .col-align-l > .vlist { + text-align: left; + } + + .col-align-r > .vlist { + text-align: right; + } } } diff --git a/test/katex-spec.js b/test/katex-spec.js index 271cf5e8c..9d2867f1f 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -1463,7 +1463,22 @@ describe("An array environment", function() { it("should accept a single alignment character", function() { var parse = getParsed("\\begin{array}r1\\\\20\\end{array}"); expect(parse[0].type).toBe("array"); - expect(parse[0].value.cols).toEqual([{align:"r"}]); + expect(parse[0].value.cols).toEqual([ + { type: "align", align: "r" } + ]); + }); + + it("should accept vertical separators", function() { + var parse = getParsed("\\begin{array}{|l||c|}\\end{array}"); + expect(parse[0].type).toBe("array"); + expect(parse[0].value.cols).toEqual([ + { type: "separator", separator: "|" }, + { type: "align", align: "l" }, + { type: "separator", separator: "|" }, + { type: "separator", separator: "|" }, + { type: "align", align: "c" }, + { type: "separator", separator: "|" } + ]); }); }); diff --git a/test/screenshotter/images/Arrays-firefox.png b/test/screenshotter/images/Arrays-firefox.png index 9f963254d..297c0e133 100644 Binary files a/test/screenshotter/images/Arrays-firefox.png and b/test/screenshotter/images/Arrays-firefox.png differ diff --git a/test/screenshotter/ss_data.yaml b/test/screenshotter/ss_data.yaml index 48a1d6a5a..9a31d4ce6 100644 --- a/test/screenshotter/ss_data.yaml +++ b/test/screenshotter/ss_data.yaml @@ -13,7 +13,7 @@ Accents: \vec{A}\vec{x}\vec x^2\vec{x}_2^2\vec{A}^2\vec{xA}^2 Arrays: | - \left(\begin{array}{rlc} + \left(\begin{array}{|rl|c||} 1&2&3\\ 1+1&2+1&3+1\cr1\over2&\scriptstyle 1/2&\frac12\\[1ex] \begin{pmatrix}x\\y\end{pmatrix}&0&\begin{vmatrix}a&b\\c&d\end{vmatrix}