From d082082dd547ba19a32bf0002a030dc062ccf394 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 14 Dec 2023 13:38:58 -0500 Subject: [PATCH] p, m, b column types in tabular environment --- CHANGELOG.md | 5 ++-- lib/formats.coffee | 65 ++++++++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a82db..72ed5e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,10 @@ instead of version numbers. ## 2023-12-14 * `\begin{tabular}` improvements: + * `p{width}`, `m{width}`, and `b{width}` columns supported * `*` repetitions supported: `*3c` is shorthand for `ccc`, - `*{10}{c}` is shorthand for `cccccccccc`, - `*3{lr}` is shorthand for `lrlrlr`. + `*3{lr}` is shorthand for `lrlrlr`, + `*{10}{c}` is shorthand for `cccccccccc`. * Copy/pasting a URL from a message no longer adds extraneous zero-width spaces * `\verb|$|` now works both inside and outside math mode diff --git a/lib/formats.coffee b/lib/formats.coffee index 03a129f..2b624c0 100644 --- a/lib/formats.coffee +++ b/lib/formats.coffee @@ -288,6 +288,14 @@ splitOutside = (text, re) -> parts.push text[start..] parts +colStyle = + c: 'text-align: center;' + l: 'text-align: left;' + r: 'text-align: right;' + p: 'vertical-align: top;' + m: 'vertical-align: middle;' + b: 'vertical-align: bottom;' + ## Process all commands starting with \ followed by a letter a-z. ## This is not a valid escape sequence in Markdown, so can be safely supported ## in Markdown too. @@ -299,17 +307,31 @@ latex2htmlCommandsAlpha = (tex, math) -> (match, char, verb) => "#{latexEscape verb}" ## Process tabular environments first in order to split cells at & ## (so e.g. \bf is local to the cell) - .replace /\\begin\s*{tabular}\s*{((?:[^{}]|{[^{}]*})*)}([^]*?)\\end\s*{tabular}/g, (m, cols, body) -> + .replace /\\begin\s*{tabular}\s*{((?:[^{}]|{(?:[^{}]|{[^{}]*})*})*)}([^]*?)\\end\s*{tabular}/g, (m, cols, body) -> cols = cols.replace /\|/g, '' # not yet supported body = body.replace /\\hline\s*|\\cline\s*{[^{}]*}/g, '' # not yet supported - cols = cols.replace /\*(\d|{\d+})([^{}]|{(?:[^{}]|{[^{}]*})*})/g, + cols = cols.replace /\*\s*(\d|{\s*\d+\s*})\s*([^{}\s]|{(?:[^{}]|{[^{}]*})*})/g, (match, repeat, body) => body = body[1...-1] if body[0] == '{' - repeat = parseInt (repeat.replace /[{}]/g, ''), 10 + repeat = repeat[1...-1] if repeat.startsWith '{' + repeat = parseInt repeat, 10 repeat = 0 if repeat < 0 repeat = 1000 if repeat > 1000 body.repeat repeat - skip = (0 for colnum in [0...cols.length]) + parseAlign = (pattern) => + pattern = pattern[1...-1] if pattern.startsWith '{' + align = pattern[0] + width = pattern[1..] + width = width[1...-1] if width.startsWith '{' + style = '' + if align of colStyle + style += colStyle[align] + if width + style += "width: #{width};" + style + colStyles = [] + cols.replace /(\w)({[^{}]*})?/g, (match) => colStyles.push parseAlign match + skip = {} '' + (for row in splitOutside body, /(?:\\\\|\[DOUBLEBACKSLASH\])/ #(?:\s*\\(?:hline|cline\s*{[^{}]*}))?/ #console.log row @@ -321,38 +343,31 @@ latex2htmlCommandsAlpha = (tex, math) -> skip[colnum]-- colnum++ continue - align = cols[colnum] attrs = '' - style = '' + style = colStyles[colnum] ## "If you want to use both \multirow and \multicolumn on the same ## entry, you must put the \multirow inside the \multicolumn" ## [http://ctan.mirrors.hoobly.com/macros/latex/contrib/multirow/multirow.pdf] - if (match = /\\multicolumn\s*(\d+|{\s*(\d+)\s*})\s*(\w|{([^{}]*)})\s*{((?:[^{}]|{(?:[^{}]|{[^{}]*})*})*)}/.exec col)? - colspan = parseInt match[2] ? match[1], 10 + if (match = /\\multicolumn\s*(\d|{\s*\d+\s*})\s*(\w|{[^{}]*})\s*{((?:[^{}]|{(?:[^{}]|{[^{}]*})*})*)}/.exec col)? + colspan = parseInt (match[1].replace /[{}]/g, ''), 10 attrs += " colspan=\"#{colspan}\"" - align = match[4] ? match[3] - col = match[5] + style = parseAlign match[2] + col = match[3] else colspan = 1 ## In HTML, rowspan means that later rows shouldn't specify \n" ).join('') +
s ## for that column, while in LaTeX, they are still present. - if (match = /\\multirow\s*(\d+|{\s*(\d+)\s*})\s*(\*|{([^{}]*)})\s*{((?:[^{}]|{(?:[^{}]|{[^{}]*})*})*)}/.exec col)? - rowspan = parseInt match[2] ? match[1] + if (match = /\\multirow\s*(\d|{\s*\d+\s*})\s*([\w\*]|{[^{}]*})\s*{((?:[^{}]|{(?:[^{}]|{[^{}]*})*})*)}/.exec col)? + rowspan = parseInt (match[1].replace /[{}]/g, ''), 10 + skip[colnum] ?= 0 skip[colnum] += rowspan - 1 attrs += " rowspan=\"#{rowspan}\"" - style = 'vertical-align: middle; ' - #width = match[4] ? match[3] - col = match[5] - attrs += - switch align - when 'c' - " style=\"#{style}text-align: center\"" - when 'l' - " style=\"#{style}text-align: left\"" - when 'r' - " style=\"#{style}text-align: right\"" - else - style + style += 'vertical-align: middle;' + if (width = match[2]) and width != '*' + width = width[1...-1] if width.startsWith '{' + style += "width: #{width};" + col = match[3] + attrs += " style=\"#{style}\"" if style colnum += colspan "#{col}