quarto-dev / quarto-cli

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

Quarto issue with processing latex commands in col.names of kable() #7792

Closed coltongearhart closed 11 months ago

coltongearhart commented 11 months ago

Bug description

Solution discussed in quarto-dev/quarto-cli/issues/5737 does not correct the processing of latex commands in col.names argument of kable()

Steps to reproduce


title: "Untitled" format: html

library(tidyverse)
library(kableExtra)

# some random latex commands in the col.names argument of some table, in this case iris data for MWE
iris %>% 
  kable(table.attr = 'data-quarto-disable-processing="true"',
                      col.names = c("$X$", "$f(X) = E(Y \\mid X)$", "$\\epsilon \\sim N(0,1)$", "$Y = f(X) + \\epsilon$", "$\\beta$")) %>% 
  kableExtra::kable_styling(bootstrap_options = "striped",
                full_width = FALSE,
                position = "left")

Expected behavior

Output of rendered html table should look like that of the inline output.

Screenshot 2023-12-04 at 7 57 32 PM

Actual behavior

Output of rendered html table should look has unprocessed column names.

Screenshot 2023-12-04 at 8 00 20 PM

Your environment

Quarto check output

[✓] Checking versions of quarto binary dependencies... Pandoc version 3.1.9: 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.4.517 Path: /Applications/quarto/bin

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

[✓] Checking LaTeX....................OK Using: Installation From Path Path: /Library/TeX/texbin Version: 2018

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

[✓] Checking Python 3 installation....OK Version: 3.11.6 Path: /usr/local/opt/python@3.11/bin/python3.11 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: /usr/local/Cellar/r/4.3.2/lib/R LibPaths:

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

cscheid commented 11 months ago

You have data-quarto-disable-processing="true", so Quarto is not going to process the content of the table. I'm not quite sure why you were expecting Quarto to process the contents of the table with that input.

cscheid commented 11 months ago

With that said, it is surprising to me that Pandoc's table reader does not appear to be inserting Math elements inside table header elements. We can see that Pandoc parses the HTML table into the following AST (using head(iris) for brevity):

          - t: "Table"
            attr: "('', ['table', 'table-striped'], ['quarto-postprocess,true', 'style,width: auto !important; '])"
            caption:
              - null
              - []
            colspecs:
              - "(AlignRight, undefined)"
              - "(AlignRight, undefined)"
              - "(AlignRight, undefined)"
              - "(AlignRight, undefined)"
              - "(AlignLeft, undefined)"
            head:
              t: "TableHead"
              attr: "('', [], [])"
              rows:
                - t: "TableRow"
                  attr: "('', [], [])"
                  cells:
                    - t: "TableCell"
                      attr: "('', [], ['quarto-table-cell-role,th'])"
                      alignment: "AlignRight"
                      row_span: 1
                      col_span: 1
                      content:
                        - t: "Plain"
                          content: "$X$"
                    - t: "TableCell"
                      attr: "('', [], ['quarto-table-cell-role,th'])"
                      alignment: "AlignRight"
                      row_span: 1
                      col_span: 1
                      content:
                        - t: "Plain"
                          content: "$f(X) = E(Y \\mid X)$"
                    - t: "TableCell"
                      attr: "('', [], ['quarto-table-cell-role,th'])"
                      alignment: "AlignRight"
                      row_span: 1
                      col_span: 1
                      content:
                        - t: "Plain"
                          content: "$\\epsilon \\sim N(0,1)$"
                    - t: "TableCell"
                      attr: "('', [], ['quarto-table-cell-role,th'])"
                      alignment: "AlignRight"
                      row_span: 1
                      col_span: 1
                      content:
                        - t: "Plain"
                          content: "$Y = f(X) + \\epsilon$"
                    - t: "TableCell"
                      attr: "('', [], ['quarto-table-cell-role,th'])"
                      alignment: "AlignLeft"
                      row_span: 1
                      col_span: 1
                      content:
                        - t: "Plain"
                          content: "$\\beta$"
            body:
              - t: "TableBody"
                row_head_columns: 0
                attr: "('', [], [])"
                intermediate_head: []
                body:
                  - t: "TableRow"
                    attr: "('', [], [])"
                    cells:
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "5.1"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "3.5"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "1.4"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "0.2"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignLeft"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "setosa"
                  - t: "TableRow"
                    attr: "('', [], [])"
                    cells:
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "4.9"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "3.0"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "1.4"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "0.2"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignLeft"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "setosa"
                  - t: "TableRow"
                    attr: "('', [], [])"
                    cells:
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "4.7"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "3.2"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "1.3"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "0.2"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignLeft"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "setosa"
                  - t: "TableRow"
                    attr: "('', [], [])"
                    cells:
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "4.6"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "3.1"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "1.5"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "0.2"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignLeft"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "setosa"
                  - t: "TableRow"
                    attr: "('', [], [])"
                    cells:
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "5.0"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "3.6"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "1.4"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "0.2"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignLeft"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "setosa"
                  - t: "TableRow"
                    attr: "('', [], [])"
                    cells:
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "5.4"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "3.9"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "1.7"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignRight"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "0.4"
                      - t: "TableCell"
                        attr: "('', [], [])"
                        alignment: "AlignLeft"
                        row_span: 1
                        col_span: 1
                        content:
                          - t: "Plain"
                            content: "setosa"
            foot:
              t: "TableFoot"
              attr: "('', [], [])"
              rows: []
cscheid commented 11 months ago

This isn't a regression from 1.3, from what I can tell. And, on further reflection, the behavior makes sense - the HTML format really doesn't support Math blocks like that, so I shouldn't expect Pandoc to parse it naturally.

I'm pretty confident this is correct behavior: kable (or kableExtra? I don't know who's ultimately responsible when both are used) is emitting HTML (as you can see with keep-md: true), and HTML doesn't support math blocks. If you want to use latex content, you'll need to find a different way to do it.

The way to do this in Quarto is to have the library emit markdown content inside a span with data-qmd attribute. But kable properly escapes table names, so you can't simply write

head(iris) %>% 
  kable(col.names = c("<span data-qmd='$X$'></span>",...

I don't know what the solution for your problem is here besides figuring out how to make the libraries emit the correct output, but this isn't a bug in the behavior of quarto.

cscheid commented 11 months ago

Here's the output from keep-md: true:

```{.r .cell-code}
# some random latex commands in the col.names argument of some table, in this case iris data for MWE
head(iris) %>% 
  kable(col.names = c("<span data-qmd='$X$'></span>", "$f(X) = E(Y \\mid X)$", "$\\epsilon \\sim N(0,1)$", "$Y = f(X) + \\epsilon$", "$\\beta$")) %>% 
  kableExtra::kable_styling(bootstrap_options = "striped",
                full_width = FALSE,
                position = "left")

::: {.cell-output-display}

<table class="table table-striped" style="width: auto !important; ">
 <thead>
  <tr>
   <th style="text-align:right;"> &lt;span data-qmd='$X$'&gt;&lt;/span&gt; </th>
   <th style="text-align:right;"> $f(X) = E(Y \mid X)$ </th>
   <th style="text-align:right;"> $\epsilon \sim N(0,1)$ </th>
   <th style="text-align:right;"> $Y = f(X) + \epsilon$ </th>
   <th style="text-align:left;"> $\beta$ </th>
...