quarto-dev / quarto-cli

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

Book: chapters specified with with `./` in their path fail crossref resolution #9304

Open AlloriMD opened 5 months ago

AlloriMD commented 5 months ago

Bug description

What I did... In a Quarto Book project edited by plaintext editor and compiled to HTML (web page) at the command line, I created a document with several figures and subsequently crossref'd them using the @fig-id notation.

What happened...

What I expected to happen...

Steps to reproduce


# Bugs

## Figure crossref not working

Consider these figures:

![A simple image](_figures/figure_xkcd_1987.png){#fig-simple-image width=250}

::: {#fig-image-within-div}
![](_figures/figure_xkcd_353.png){width=250}

An image within a DIV
:::

```{python}
#| label: fig-python-code-chunk
#| fig-cap: "Figure generated by running a code chunk."
#| echo: true

import numpy as np
import matplotlib.pyplot as plt

r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
fig, ax = plt.subplots(
  subplot_kw = {'projection': 'polar'} 
)
ax.plot(theta, r)
ax.set_rticks([0.5, 1, 1.5, 2])
ax.grid(True)
plt.show()

::: {#fig-python-code-chunk-within-div width=150}

#| echo: true

import numpy as np
import matplotlib.pyplot as plt

r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
fig, ax = plt.subplots(
  subplot_kw = {'projection': 'polar'} 
)
ax.plot(theta, r)
ax.set_rticks([0.5, 1, 1.5, 2])
ax.grid(True)
plt.show()

Figure generated from running a code chunk within a DIV. :::

Now we will try to crossref each of them:

Fin.


### Expected behavior

- Correct crossrefs should display with the figure label followed by the auto-number, e.g. "Figure 1", "Figure 2", etc. If `chapters: true` is set in the `crossref:` section of `_quarto.yml`, then the figure numbers should be prefixed by the chapter, e.g. "Figure 1.1" for Chapter 1 or "Figure H.1" for Appendix H.
- A crossref to a non-existent `fig-id` should display a question mark in front of the figure, as happens with bibliographic citations; e.g., "Figure ?fig-fake-name-that-doesnt-exist".

### Actual behavior

- All figures *did* display correctly.
- All figure captions *did* display correctly immediately below the figures -- They do appear as "Figure 1: A simple image", "Figure 2: An image within a DIV", "Figure 3: Figure generated by running a code chunk.", and "Figure 4: Figure generated from running a code chunk within a DIV."
- **The crossref displays incorrectly** -- Instead of showing up as an auto-numbered "Figure 1", "Figure 2", etc., the crossrefs are listed as "Figure fig-id". The label "Figure" was added by Quarto, but the `fig-id` is not being resolved to the number.
- **No '?' appears prefixing a crossref to an invalid `fig-id`** -- With a crossref to a fake figure (e.g., `@fig-fake-name-that-doesnt-exist`) displays in the same way, as "Figure fig-fake-name-that-doesnt-exist" rather than "Figure ?fig-fake-name-that-doesnt-exist".
- The error occurs no matter what kind of figure embedding I use: simple image, an image within a div, programmatic plotting from a code chunk, programmatic plotting from a code chunk contained within a div, etc.

Specific to this sample document, this is what I see as the crossref output:

- Figure fig-simple-image – should be “Figure 1”
- Figure fig-image-within-div – should be “Figure 2”
- Figure fig-python-code-chunk – should be “Figure 3”
- Figure fig-python-code-chunk-within-div – should be “Figure 4”
- Figure fig-fake-figure-id – should not resolve because there is no such figure id.

I will paste a screencap below.

![CleanShot 2024-04-08--14-02-54](https://github.com/quarto-dev/quarto-cli/assets/20407950/b5b57e94-9cb3-4e01-a668-57c6faa78629)

It is noteworthy that this problem only occurs in HTML (web page output). The PDF rendering generates correct figure crossrefs.

<img width="472" alt="image" src="https://github.com/quarto-dev/quarto-cli/assets/20407950/737a812b-aa92-40b7-befe-5fc65e770955">

### Your environment

- macOS Sonoma 14.3
- Quarto 1.4.553 and pre-release 1.5.28 -- both produce the same result
- Plaintext workflow with `quarto preview` or `quarto render` at command line

### Quarto check output

```bash
quarto check

Quarto 1.5.28
[✓] Checking versions of quarto binary dependencies...
      Pandoc version 3.1.11: OK
      Dart Sass version 1.70.0: OK
      Deno version 1.41.0: OK
      Typst version 0.10.0: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
      Version: 1.5.28
      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: 2024

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

[✓] Checking Python 3 installation....OK
      Version: 3.11.8 (Conda)
      Path: /Applications/miniconda3/envs/quarto/bin/python
      Jupyter: 5.7.1
      Kernels: python3

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

[✓] Checking R installation...........OK
      Version: 4.2.3
      Path: /Library/Frameworks/R.framework/Resources
      LibPaths:
        - /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
      knitr: 1.42
      rmarkdown: 2.21

[✓] Checking Knitr engine render......OK
mcanouil commented 5 months ago

Unfortunately, your example is not small, reproducible or self-contained.

For instance, if I create a book:

quarto create project book demo Demo

Then add the following snippet to intro.qmd or index.qmd:

:::{#fig-figureone}

```{python}
import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [1, 4, 9, 16, 25]

plt.plot(x, y)
plt.show()

Some caption :::

An image{#fig-figuretwo}

@fig-figureone

@fig-figuretwo


I get the expected results.

---

You can share a **self-contained "working" (reproducible)** Quarto document using the following syntax, _i.e._, using more backticks than you have in your document (usually four ` ```` `).
See <https://quarto.org/bug-reports.html#small-is-beautiful-aim-for-a-single-document-with-10-lines>.

If you have multiple files (and if it is **absolutely required** to have multiple files), please share as a Git repository.

<table>
<tr><th>R</th><th>Python</th></tr>
<tr><td>

`````md
````qmd
---
title: "Reproducible Quarto Document"
format: html
engine: knitr
---

This is a reproducible Quarto document.

```{r}
x <- c(1, 2, 3, 4, 5)
y <- c(1, 4, 9, 16, 25)

plot(x, y)

An image

The end.

````qmd
---
title: "Reproducible Quarto Document"
format: html
engine: jupyter
---

This is a reproducible Quarto document.

```{python}
import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [1, 4, 9, 16, 25]

plt.plot(x, y)
plt.show()

An image

The end.

Additionally and if not already given, please share the output of quarto check within a code blocks (i.e., using three backticks ```txt), see https://quarto.org/bug-reports.html#check.

cscheid commented 5 months ago

We're aware of this bug but have had a hard time reproducing it.

I'd prefer a big example as opposed to not having one at all. Would you mind sending a link to a github repository? Thank you.

cscheid commented 5 months ago

Let's not close this issue just yet, but I think it is a duplicate of https://github.com/quarto-dev/quarto-cli/issues/9294

AlloriMD commented 5 months ago

Thanks for pointing me in the right direction. When I create a new Book Project that has a flat layout, everything worked as expected.

I tracked down the problem to how chapters are listed in _quarto.yml -- If the path to the chapter file is prefixed with a ./, then the figure crossref problem happens:

chapters:
  - index.qmd
  - part: "PART ONE"
    chapters:
      - ./_chapters/ch01.qmd
      - ./_chapters/ch02.qmd
image

Note that the book otherwise appears completely normal. All pages render, and the web site is navigable. It's just that the figure crossref doesn't work right.

However, when I removed the ./ prefix in the YAML chapter listing, then the crossrefs appear correctly.

chapters:
  - index.qmd
  - part: "PART ONE"
    chapters:
      - _chapters/ch01.qmd
      - _chapters/ch02.qmd
image

(I guess I'm so used to using ./ and ../ when specifying paths that I must've changed it to "clean it up" at some point, not realizing that Quarto YAML would not like that.)

Anyway, thanks so much for the help. It wasn't as much a bug with crossref as understanding how Quarto expects the chapter filenames need to appear within the YAML definition of the book structure.

It may be worth giving an example of a book that organizes the structure into folders/subfolders, since all the examples on the Quarto web site represent a "flat" organizational scheme for all the files. To see an example where the path is specified without any ./ prefix might help someone else out. Or maybe a callout box to highlight this tidbit.

cscheid commented 5 months ago

I see some of what you're reporting, but I still can't fully reproduce it. with ./ch1.qmd instead of ch1.qmd, I do get a loud warning about failing to resolve cross-references:

book:
  title: "Issue-9304"
  author: "Norah Jones"
  date: "4/8/2024"
  chapters:
    - index.qmd
    - intro.qmd
    - part: "Part I"
      chapters:
        - ./ch1.qmd
        - ./ch2.qmd      
    - summary.qmd
    - references.qmd
$ quarto render
...
WARN: index.html: Unable to resolve crossref @fig-1
WARN: index.html: Unable to resolve crossref @fig-2
...

This is already a bug on our side, but even in this case, I don't get the result you do. Instead, I simply get the (standard, otherwise intended) failed crossref id:

image

I really would prefer to have a full example from you that we can run. Could you provide that if at all possible?

AlloriMD commented 4 months ago

Sorry it took a while, but I was able to create an example Quarto Book that reproducibly demonstrates the bug: cf. https://github.com/AlloriMD/quarto_bug_9304

Briefly, this Quarto Book specifies chapters in the subdirectory _chapters. The _quarto.yml file lists these chapter filenames using a relative path, eg ./_chapters/ch01.qmd. If you preview the Quarto Book and then look at the second chapter with all the figures, you will see that the figure cross-references all appear as "fig-id" instead of as "Figure 1".

By simply removing the ./ in _quarto.yml and saving the file, the Quarto Book preview will update and will resolve the figure cross-references correctly, eg "Figure 1".

This sample GitHub repository also includes a .gif screen recording that demonstrates the problem.

mcanouil commented 4 months ago

Side question: Why are you using the _ prefix for directories? In Quarto, it means that those are hidden and should not be discovered by Quarto.

cscheid commented 4 months ago

In Quarto, it means that those are hidden and should not be discovered by Quarto.

Just to clarify: they're hidden by default if no render list is specified, but if they show in the configuration, we do respect it. (I've also tested and confirmed that the bug is unrelated to _chapters vs chapters, it really is only about ./path vs path)

AlloriMD commented 4 months ago

Side question: Why are you using the _ prefix for directories?

@cscheid, I decided to prefix all the "Quarto bits" with the _ (I even named the refs file "_references.bib") so that they must be explicitly listed in the _quarto.yml file in order to be processed. Thus, the directory structure of the project has "_chapters", "_figures", "_data", etc.

Perhaps it is "belts and suspenders," but that makes sure that quarto render only assembles the pieces as defined by the project, and I don't risk having the chapters being rendered separately. (I think I had that issue when I was trying to keep parts of a Manuscript Project in separate files, and I carried over the practice to Book Projects.)