quarto-dev / quarto-cli

Open-source scientific and technical publishing system built on Pandoc.
https://quarto.org
Other
3.77k stars 309 forks source link

quarto 1.4 eats LaTeX \providecommand #8564

Open hughjonesd opened 7 months ago

hughjonesd commented 7 months ago

Bug description

Quarto 1.4 causes huxtable tables to fail when compiling to PDF, because it doesn't print out some LaTeX \providecommand commands that huxtable uses. This is new in 1.4.

Steps to reproduce

The following document will fail if compiled to PDF on quarto 1.4.549 or 1.5.6. It works with quarto 1.3.450.

---
title: "Quarto tester"
format: html
---

Reference to @tbl-hux.

```{r}
#| tbl-cap: "Huxtable Jams"
#| label: tbl-hux
library(huxtable)
jams

### Expected behavior

I'd expect the .tex file produced to have the same \providecommand\huxvb TeX in there as can be seen in the .pdf.md file.

### Actual behavior

The \providecommand TeX is not in the .tex file, and as a result xelatex fails with "! Undefined control sequence."

### Your environment

_No response_

### Quarto check output

Quarto 1.5.6 [✓] Checking versions of quarto binary dependencies... Pandoc version 3.1.11: OK Dart Sass version 1.69.5: OK Deno version 1.37.2: OK [✓] Checking versions of quarto dependencies......OK [✓] Checking Quarto installation......OK Version: 1.5.6 Path: /Applications/quarto/bin

[✓] Checking tools....................OK TinyTeX: (external install) Chromium: (not installed)

[✓] Checking LaTeX....................OK Using: TinyTex Path: /Users/davidhugh-jones/Library/TinyTeX/bin/universal-darwin Version: 2023

[✓] Checking basic markdown render....OK

[✓] Checking Python 3 installation....OK Version: 3.11.5 Path: /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 Jupyter: (None)

  Jupyter is not available in this Python installation.
  Install with python3 -m pip install jupyter

[✓] Checking R installation...........OK Version: 4.3.2 Path: /Library/Frameworks/R.framework/Resources LibPaths:

[✓] Checking Knitr engine render......OK

cscheid commented 7 months ago

Before I dig in deeper:

---
title: "Quarto tester"
format: html
---

The following document will fail if compiled to PDF

Can you clarify and fix your example?

cscheid commented 7 months ago

It'll be hard to make this work in Quarto's new crossref system. Huxtable is emitting its own opinionated crossref entries and those are interfering with Quarto's. The raw LaTeX being emitted here is:

```{=latex}

  \providecommand{\huxb}[2]{\arrayrulecolor[RGB]{#1}\global\arrayrulewidth=#2pt}
  \providecommand{\huxvb}[2]{\color[RGB]{#1}\vrule width #2pt}
  \providecommand{\huxtpad}[1]{\rule{0pt}{#1}}
  \providecommand{\huxbpad}[1]{\rule[-#1]{0pt}{#1}}

\begin{table}[ht]
\begin{centerbox}
\begin{threeparttable}
 \label{tab:tbl-hux}
\setlength{\tabcolsep}{0pt}
\begin{tabular}{l l}

\hhline{}
\arrayrulecolor{black}

\multicolumn{1}{!{\huxvb{0, 0, 0}{0}}l!{\huxvb{0, 0, 0}{0}}}{\huxtpad{6pt + 1em}\raggedright \hspace{6pt} Type \hspace{6pt}\huxbpad{6pt}} &
\multicolumn{1}{r!{\huxvb{0, 0, 0}{0}}}{\huxtpad{6pt + 1em}\raggedleft \hspace{6pt} Price \hspace{6pt}\huxbpad{6pt}} \tabularnewline[-0.5pt]

\hhline{}
\arrayrulecolor{black}

\multicolumn{1}{!{\huxvb{0, 0, 0}{0}}l!{\huxvb{0, 0, 0}{0}}}{\huxtpad{6pt + 1em}\raggedright \hspace{6pt} Strawberry \hspace{6pt}\huxbpad{6pt}} &
\multicolumn{1}{r!{\huxvb{0, 0, 0}{0}}}{\huxtpad{6pt + 1em}\raggedleft \hspace{6pt} 1.90 \hspace{6pt}\huxbpad{6pt}} \tabularnewline[-0.5pt]

\hhline{}
\arrayrulecolor{black}

\multicolumn{1}{!{\huxvb{0, 0, 0}{0}}l!{\huxvb{0, 0, 0}{0}}}{\huxtpad{6pt + 1em}\raggedright \hspace{6pt} Raspberry \hspace{6pt}\huxbpad{6pt}} &
\multicolumn{1}{r!{\huxvb{0, 0, 0}{0}}}{\huxtpad{6pt + 1em}\raggedleft \hspace{6pt} 2.10 \hspace{6pt}\huxbpad{6pt}} \tabularnewline[-0.5pt]

\hhline{}
\arrayrulecolor{black}

\multicolumn{1}{!{\huxvb{0, 0, 0}{0}}l!{\huxvb{0, 0, 0}{0}}}{\huxtpad{6pt + 1em}\raggedright \hspace{6pt} Plum \hspace{6pt}\huxbpad{6pt}} &
\multicolumn{1}{r!{\huxvb{0, 0, 0}{0}}}{\huxtpad{6pt + 1em}\raggedleft \hspace{6pt} 1.80 \hspace{6pt}\huxbpad{6pt}} \tabularnewline[-0.5pt]

\hhline{}
\arrayrulecolor{black}
\end{tabular}
\end{threeparttable}\par\end{centerbox}

\end{table}


We try to detect and correct `\begin{table}` usage because this means that these tables don't play nicely with our crossreferencing system, but this is a best-effort approach that can't work in all cases. Here, we're getting confused by the \providecommand preamble. (and, in general, we can't detect and handle arbitrary LaTeX in raw code, because parsing LaTeX is impossible).

As a direct example of why we can't support this, the emitted output will never be usable in a Quarto [Supplemental Table](https://quarto.org/docs/authoring/cross-references-custom.html#example-supplemental-figures), because we need to use a different counter than that of the `table` environment. There are other issues too: note eg the label `tab:tbl-hux` emitted by huxtable is incorrect.

The fundamental problem here is that huxtable is trying to decide how its table should be crossreferenced, and that's not working with Quarto's expectations of the LaTeX code.

If you can convince huxtable to produce its own captions directly in the code, then you could use that, and remove the `tbl-cap` entry from your code cell. Then, you could use raw latex references to `tab:tbl-hux` by typing `\ref{tab:tbl-hux}` in your Markdown code (that should pass through to LaTeX directly).
hughjonesd commented 7 months ago

The text reference @tbl-hux comes out correctly with 1.3.450. What's changed? Huxtable adds "tab:" before knitr labels; It looks like quarto used to rewrite it and remove the "tab:" part. Adding "tab:" was to work with bookdown, IIRC; see https://bookdown.org/yihui/bookdown/tables.html.

In any case, what's breaking TeX compilation is that quarto is removing the \providecommand commands, even though the .md file produced has a block marked as raw LaTeX:

```{=latex}

  \providecommand{\huxb}[2]{\arrayrulecolor[RGB]{#1}\global\arrayrulewidth=#2pt}
  \providecommand{\huxvb}[2]{\color[RGB]{#1}\vrule width #2pt}
  \providecommand{\huxtpad}[1]{\rule{0pt}{#1}}
  \providecommand{\huxbpad}[1]{\rule[-#1]{0pt}{#1}}

...

Is there a way I can mark huxtable output as "please leave as is"? If so then I'm happy to make it follow the rules for cross-referencing, if you can explain what these are.

As a process note, I first discovered this when I got a message from CRAN that huxtable was throwing test errors when used with the new quarto. More notice would have been helpful. I've now got 2 weeks to fix this before the package is archived.

cscheid commented 7 months ago

What's changed?

All the 1.4 crossref features.

In any case, what's breaking TeX compilation is that quarto is removing the \providecommand commands, even though the .md file produced has a block marked as raw LaTeX:

As I explained in my comment, if we don't attempt to parse that content, things will break in a different way.

Is there a way I can mark huxtable output as "please leave as is"?

Again, as I explained in my comment:

If you can convince huxtable to produce its own captions directly in the code, then you could use that, and remove the tbl-cap entry from your code cell. Then, you could use raw latex references to tab:tbl-hux by typing \ref{tab:tbl-hux} in your Markdown code (that should pass through to LaTeX directly).

More notice would have been helpful. I've now got 2 weeks to fix this before the package is archived.

I'm sorry you're getting hit by this, by that's CRAN policy that we are not involved with and don't control.

mcanouil commented 7 months ago

@hughjonesd See https://quarto.org/docs/prerelease/1.4/ to get a view of "what's changed".

As a process note, I first discovered this when I got a message from CRAN that huxtable was throwing test errors when used with the new quarto. More notice would have been helpful. I've now got 2 weeks to fix this before the package is archived.

May I suggest to use CI to test on Quarto pre-release. You are already using CI (with outdated GitHub Actions). CI is useful precisely for these cases. Note that your R CMD Checks on GitHub Actions has been failing for more than a year (https://github.com/hughjonesd/huxtable/actions).

hughjonesd commented 7 months ago

OK, but I'd like people to be able to use the quarto chunk labels, as they can do for bookdown/rmarkdown and could before. It's nicer for the user. Is there a way I can do that? For example, what if I just print the tex label as tbl-hux without the tab: prefix?

More generally, is there a way I can avoid quarto removing the \providecommand? What conditions trigger that? I need some rules to follow, otherwise I am flying blind. If you don't want people to write arbitrary TeX and/or HTML tables via code, OK, but then I think clarify that.

cscheid commented 7 months ago

More generally, is there a way I can avoid quarto removing the \providecommand?

Sorry, but that's what I've tried to answer to you twice. If the LaTeX output is not in a cell that Quarto considers part of its native crossref, then you'll have to manage those yourself, and the raw LaTeX should go through unchanged.

cscheid commented 7 months ago

If your question is "can I use Quarto syntax and still use huxtable's complex LaTeX code", then the answer is currently no, unless you figure out a way for huxtable to emit LaTeX code that does table formatting without also doing table crossreferencing.

hughjonesd commented 7 months ago

Right. I'm probably being extremely slow, but could you explain how quarto decides whether a LaTeX table is part of its native crossref? Is it if it emits a \label command? Or is it just anything in a cell with a label: attribute?

mcanouil commented 7 months ago

You need to emit only the table without \label{tab:tbl-hux} which is handled by Quarto. The label is taken from #| label: tbl-mytable in the code cell.

cscheid commented 7 months ago

but could you explain how quarto decides whether a LaTeX table is part of its native crossref? Is it if it emits a \label command?

Our documentation is here: https://quarto.org/docs/authoring/cross-references.html#computations

But, in your case, a code cell with label: tbl-... and tbl-cap: ... will be interpreted by Quarto as a table. There are a numbe of other things that we try to detect for backwards compatibility, and that includes pure LaTeX code with \label{} and \begin{table}. That's ultimately the reason that you're seeing this failure right now; Quarto is trying to take the LaTeX from huxtable and failing to process it appropriately.

hughjonesd commented 7 months ago

Okay. I did some more testing. Here just for reference is how it works as of 1.4.549:

I'll write code to emit informative errors/warnings.

hughjonesd commented 3 months ago

An update for 1.5: the following table gets its label eaten, whether or not the label starts with tbl-.

---
title: "quarto minimal table"
format: 
  pdf:
    keep-tex: true
---

Table \ref{foo}.

```{=tex}
\begin{table}[h]
\caption{A table\label{foo}}
\begin{tabular}{l l}
1 & 2
\end{tabular}
\end{table}

Table \ref{foo}.


Part of the resulting output:

Table \ref{foo}.

\begin{table}[h] \caption{A table} \begin{tabular}{l l} 1 & 2 \end{tabular} \end{table}

Table \ref{foo}.



Quarto emits a warning. 
Quarto version: 1.5.45