Skip to content

cvpr: fidelity pass aligning with cvpr.sty + LaTeX article-class#72

Open
DzmingLi wants to merge 11 commits into
daskol:mainfrom
DzmingLi:cvpr-fidelity-pass
Open

cvpr: fidelity pass aligning with cvpr.sty + LaTeX article-class#72
DzmingLi wants to merge 11 commits into
daskol:mainfrom
DzmingLi:cvpr-fidelity-pass

Conversation

@DzmingLi

@DzmingLi DzmingLi commented May 6, 2026

Copy link
Copy Markdown

11-commit fidelity pass over cvpr/ aligning with cvpr.sty + LaTeX article-class behavior. Each commit is atomic with cvpr.sty line refs in the message — see individual commits for rationale.

Partially addresses #14: cvpr.csl now matches cvpr.sty + ieeenat_fullname.bst field-for-field. Backref support (LaTeX pagebackref) is not included — typst's bibliography() collapses entries into one opaque block with no per-entry hook, blocking show-rule injection. When typst#942 (Bibliography customization API) lands a per-entry rendering hook, backref can be added via show rules without bypassing bibliography() ourselves.

I made some design choices that might be up for debate. Feel free to discuss with me!

@DzmingLi DzmingLi marked this pull request as draft May 7, 2026 06:24
@daskol daskol self-assigned this May 7, 2026
@daskol daskol added the enhancement New feature or request label May 7, 2026
@DzmingLi DzmingLi force-pushed the cvpr-fidelity-pass branch 7 times, most recently from f0f43d2 to 8b90bab Compare May 7, 2026 13:37
DzmingLi added 3 commits May 7, 2026 21:53
cvpr.sty:49 loads the caption package as:

    \RequirePackage[
        format=plain,
        labelformat=simple,
        labelsep=period,
        font=small,
        compatibility=false
    ]{caption}

Notably absent is `singlelinecheck=false`. The caption package's
default `singlelinecheck=true` behavior is therefore active:

  - captions that fit on a single line  → centered
  - captions that wrap to multiple lines → left-justified

The previous show rule was effectively always-left:

    show figure.caption: set align(center)
    show figure.caption: it => block({
        align(left, it)
    })

The outer `set align(center)` was overridden by the inner
`align(left, it)` inside the block — single-line captions never
got centered.

Fix uses `layout(container => measure(...))` to detect natural
caption width vs available container width: if it fits one line,
center; otherwise left-align with wrapping.
cvpr.sty:34 includes \RequirePackage{amsmath}. With amsmath, LaTeX
authors signal numbering intent via the choice of environment:

  - $$ ... $$ / \[ ... \]                 → display math, NOT numbered
  - \begin{equation} ... \end{equation}   → auto-numbered (1), (2), ...

typst has no equivalent dual environment — every block equation is
numbered when `set math.equation(numbering: "(1)")` is in effect.
This prints sequential numbers next to throwaway display math
(intermediate algebra steps, derivations) that authors never
intended to reference.

We use the typst `<eq:foo>` label as the "intent to number" signal:

    $ x = y $              ← unlabeled, no number
    $ x = y $ <eq:foo>     ← labeled, gets "(1)"

This mirrors LaTeX practice: only equations the author plans to
\eqref{...} get a number; the rest stay unnumbered.

The recursion guard (`if it.numbering == none`) prevents the
recreated equation below from re-entering the show rule. The
counter rollback fixes the otherwise-off-by-one numbering: typst's
outer `set` rule increments the equation counter eagerly, so an
unlabeled equation between two labeled ones would skip a number
without the rollback.
@DzmingLi DzmingLi force-pushed the cvpr-fidelity-pass branch 2 times, most recently from 650fa22 to bc89bc1 Compare May 7, 2026 14:05
cvpr.sty defines only three section command levels:

    \cvprsection      (level 1)
    \cvprsubsection   (level 2)
    \cvprsubsubsection (level 3)

`\paragraph{...}` (level 4) is left to the article-class default,
which renders as a bold run-in inline heading on the same line as
the paragraph that follows, with about 6pt of above-skip glue.

Empirical pdftotext-bbox measurement against the LaTeX render:

    normal baseline-to-baseline gap:    11.96pt
    above a \paragraph{...} mini-head:  18.17pt
                                        ─────
                                         6.21pt extra above-skip

Without an explicit show rule, typst falls through to the default
block heading style — `\paragraph{Foo.}` becomes a freestanding
bold line, breaking the run-in semantics. This show rule:

  - emits a 7.1pt strut (`weak: false` so it survives layout
    collapsing against the preceding paragraph break)
  - wraps the heading body in a `box(...)` so it stays inline and
    the next paragraph continues on the same baseline
  - appends an en-space (0.6em) gap to the body sentence
@DzmingLi DzmingLi force-pushed the cvpr-fidelity-pass branch 4 times, most recently from 290b752 to 4b2042a Compare May 7, 2026 14:26
DzmingLi added 2 commits May 7, 2026 22:29
cvpr.sty:36 declares \RequirePackage{booktabs} — booktabs-style
horizontal rules are part of the canonical CVPR table aesthetic:

    \toprule    1.5pt thick rule (\heavyrulewidth = 0.08em)
    \midrule    0.5pt thin rule  (\lightrulewidth = 0.05em)
    \bottomrule 1.5pt thick rule
    \aboverulesep / \belowrulesep   2pt vertical gap around each rule

typst's `table.hline()` draws a line at row boundaries but provides
no padding semantics — applying it directly produces lines flush
against adjacent cell baselines, visually wrong for booktabs.

These three helpers expand to hline + colspan-aware empty cells
that reproduce the booktabs vertical gaps. Naming follows booktabs
LaTeX commands directly so users coming from a LaTeX cvpr.sty
workflow recognize the API immediately.
…titlesupplementary

The previous `appendix` parameter only did `set heading(numbering:
"A.1") + counter(heading).update(0)` and rendered the content
in-flow.

But the semantics were incomplete. cvpr.sty's macro is:

    \def\maketitlesupplementary{
       \newpage
       \twocolumn[
          \centering \Large \textbf{\thetitle}\\
          \vspace{0.5em}Supplementary Material \\
          \vspace{1.0em}
       ]
    }

This does a pagebreak, re-prints the paper title in cross-column
space at the top of the new page, and adds a "Supplementary
Material" subtitle, before continuing in the body's 2-column
layout. The previous typst implementation skipped all of that
and just shifted heading numbering.

This commit:

  - adds `pagebreak(weak: true)` on the supplementary entry
  - adds a `place(top, scope: "parent", float: true, ...)` that
    re-renders the title + "Supplementary Material" subtitle as a
    cross-column header (typst's equivalent of LaTeX `\twocolumn[...]`)
  - keeps the heading numbering shift to "A.1." (preserved daskol
    behavior — supplementary sections numbered A.1, A.2, ...)
  - updates README.md to rename `appendix` → `supplementary`
@DzmingLi DzmingLi force-pushed the cvpr-fidelity-pass branch 5 times, most recently from 18f9646 to 95aaf93 Compare May 7, 2026 16:00
…sys.inputs

The previous `accepted: false | true | none` parameter encoded
modes via boolean tri-state, with `accepted != none and not
accepted` scattered across six conditional branches (banner,
ruler, anonymous-author replacement, page-number footer, ...).
Three issues:

  1. cvpr.sty:55-61 declares `[review]`, `[rebuttal]`, and `[final]`
     as first-class string options on one axis, plus an orthogonal
     `[pagenumbers]` toggle. A boolean tri-state can't directly
     express rebuttal, and forced an opaque encoding for the third
     option. The orthogonal pagenumbers axis was missing entirely.

  2. The conditional `accepted != none and not accepted` reads like
     a riddle. Every reader has to mentally unfold which mode that is
     (review).

  3. Switching mode required editing main.typ — passing a different
     literal to `accepted:`. cvpr.sty's LaTeX equivalent
     `\usepackage[review]{cvpr}` switches at compile time, no source
     edit needed.

Replaces with a mirror of cvpr.sty's two-axis design:

    mode: "review"   ← line numbers, banner, anon authors
    mode: "final"    ← camera-ready; real authors, no banner (default)
    mode: "rebuttal" ← rebuttal; smaller title, no author block,
                       top vspace pulled up per cvpr.sty:282-287

    page-numbers: auto | true | false   ← orthogonal toggle;
                                          auto = mode=review default

Default is `mode: "final"` to match cvpr.sty:55-58 (`\toggletrue
{cvprfinal}` is set unconditionally; only an explicit option flips
it). Authors who haven't passed `--input mode=...` get camera-ready
output, the same as `\usepackage{cvpr}` without options.

Daskol's previous `accepted: none` ("arxiv preprint") is dropped.
That state was visually identical to `accepted: true` (final): same
no-banner, no-ruler, no-line-numbers layout. The only difference
was a hint in PDF metadata, which the template did not actually
emit. So the third state was redundant — users who wanted "looks
like final but not yet camera-ready" just write `mode: "final"`,
since the visual is the same and PDF metadata isn't differentiated.
The `[pagenumbers]` axis covers the remaining variation (camera-
ready look + visible page numbers, useful for length-checking
before the final submission).

Rebuttal-mode title layout is implemented in this commit:
cvpr.sty:282-307 specifies smaller title font (\large vs \Large),
top pulled up by ~0.3in (negative \vspace*), tightened post-title
gap (-22pt vs +24pt) to fit the rebuttal page budget, and an
empty author block (the `\iftoggle{cvprrebuttal}{}{...}` branch
is empty — reviewers know the authors at rebuttal stage but the
slot is intentionally blank to keep the title page tight).
@DzmingLi DzmingLi force-pushed the cvpr-fidelity-pass branch from 95aaf93 to cfab2db Compare May 7, 2026 16:11
@DzmingLi DzmingLi marked this pull request as ready for review May 7, 2026 16:15
DzmingLi added 4 commits May 8, 2026 00:28
cvpr.sty inherits article.cls's paragraph behavior, where the first
paragraph after `\@startsection`-derived elements (\section,
\subsection, ...) is set with `\@afterindentfalse` (no indent), but
the first paragraph after non-heading blocks (`\quote`, `\figure`,
`\begin{equation}`, ...) IS indented as a normal continuation
paragraph. CVPR papers use `\quote` regularly (e.g. for showing
example author guidelines, citing reviewer-style abstracts in the
rebuttal-mode preamble), so getting this case right matters.

This fully matches article-class / cvpr.sty paragraph behavior:

  - heading → next paragraph: not indented (suppressed by state)
  - quote/figure/equation → next paragraph: indented (auto via all: true)
  - first paragraph inside a quote: indented (auto via all: true,
    once the quote's first-line-indent override is dropped)
  - consecutive paragraphs: indented (typst default consecutive rule)

The `#indent = h(12pt)` helper that previously existed for manual
post-block indentation is removed: the auto-indent show rule
covers every site that called it.
cvpr.sty ships a coordinated set of 13 emphasized + period-aware
abbreviation macros via:

    \def\eg{\emph{e.g}\onedot}    \def\Eg{\emph{E.g}\onedot}
    \def\ie{\emph{i.e}\onedot}    \def\Ie{\emph{I.e}\onedot}
    \def\cf{\emph{cf}\onedot}     \def\Cf{\emph{Cf}\onedot}
    \def\etc{\emph{etc}\onedot}   \def\vs{\emph{vs}\onedot}
    \def\wrt{w.r.t\onedot}        \def\dof{d.o.f\onedot}
    \def\iid{i.i.d\onedot}        \def\wolog{w.l.o.g\onedot}
    \def\etal{\emph{et al}\onedot}

`\onedot` (cvpr.sty:461-463) is a smart period-and-xspace combinator
that adds a trailing `.` only when the next character isn't already
one — so authors can write `\eg, foo` (gets period auto), `\eg.` (no
double period), `\eg foo` (xspace handles trailing space) without
worrying which case they're in. The typst port previously had only
`#eg` and `#etal` as hardcoded literals, missing the other 11
abbrevs and the `\onedot` guard.

Adds the 11 missing abbreviations matching cvpr.sty's emphasis
decisions: `eg`/`Eg`/`ie`/`Ie`/`cf`/`Cf`/`etc`/`vs`/`etal` are
italicized; `wrt`/`dof`/`iid`/`wolog` are plain (cvpr.sty
intentionally doesn't `\emph` them). The trailing period lives
outside the `emph` (`emph[X] + "."`, not `emph[X.]`) so it sits
in the same plain-text run as any author-typed `.` and the
collapse regex below can fire across the boundary.

Adds a single regex show rule that emulates `\onedot`:

    show regex("\.\.[^.]"): m => "." + m.text.slice(2)

Matches `.` `.` `<non-period>`, replaces with `.` `<non-period>` —
collapsing double periods while preserving the next character.
typst auto-ligatures `...` to Unicode ellipsis (single codepoint)
so the regex never sees `...` and ellipsis stays intact. Real `..`
in numeric ranges (e.g. "1..5") is rare in CVPR writing.
cvpr.sty inherits LaTeX article-class's \thanks{...} mechanism for
title-block footnotes (e.g. corresponding-author marker). Authors
write inline:

    \author{... Hao Lu\thanks{Corresponding author.} ...}

article-class's `\thanks` does three things:

  1. \footnotemark + \footnotetext pair sharing the regular footnote
     counter — so \thanks and \footnote tap the same counter.

  2. Inside \maketitle, the mark is rendered via \fnsymbol — the
     symbol cycle `*, †, ‡, §, ¶, ‖, **, ††, ‡‡, §§, ¶¶, ‖‖`.
     Outside \maketitle the mark is \arabic.

  3. article.cls calls `\setcounter{footnote}{0}` at the end of
     `\maketitle`. So body footnotes start fresh at `1` even after
     N \thanks markers. With three \thanks and two body footnotes,
     marks render as: `*`, `†`, `‡`, `1`, `2`.

This commit ports those semantics by exporting `thanks(body)` with
the \fnsymbol numbering cycle, resetting the footnote counter after
make-title, and making `author.name` content-typed (so authors can
put `name: [Hao Lu#thanks[...]]` without crashing the document
metadata path).
cvpr.sty:37 declares `\RequirePackage[numbers,sort&compress]{natbib}`
and CVPR papers conventionally use `\bibliographystyle{ieeenat_fullname}`,
which gives:

  1. Compact citation clusters `[1, 3-5]` — sorted, ranged, single
     bracket pair per cluster.
  2. Full author names (no initials), Oxford comma only with 3+ authors.
  3. Sentence-case titles (no quotes).
  4. Compact journal `vol(issue):pages, year` form.
  5. Conference: `In Container, pages X-Y. Pub, year.` with publisher,
     `In Container, pages X-Y, year.` without.

Rename `cvpr/ieee.csl` → `cvpr/cvpr.csl` and apply the CVPR-specific
patches below. `<info>` is rewritten to declare CVPR identity (title,
id, summary, links to cvpr-org/author-kit + ieee_fullname.bst),
matching what the file actually formats.

Patches applied:

  - `<citation collapse="citation-number">` + brackets moved from
    `<group>` to `<layout>` so each cluster wraps in one pair (was
    `[7], [11], [12], [13], [14]`, now `[7, 11-14]`).

  - `<bibliography><sort>` by author then date — alphabetical entry
    order (was insertion order).

  - `<name>` element: drop `initialize-with=". "` for full names,
    raise `et-al-min` to 11 with `et-al-use-first="10"`, drop
    `delimiter-precedes-last="always"` so 2-author cites read
    `A and B` not `A, and B`.

  - `<macro name="title">`: drop `quotes="true"`, add
    `text-case="sentence"` to non-italic branch.

  - Author suffix: `, ` → `. ` in bibliography layout.

  - `<macro name="event">`: capitalize "in" → "In" before container.

  - New `<macro name="vol-pages-compact">`: emits `VOL(ISSUE):PAGES`
    when issue exists (`9(6):544`), else `VOL:PAGES` (`37:48810-48837`).

  - New `<macro name="page-long">`: emits `pages X-Y` with long form.

  - article-journal: title `. ` then `container, vol-pages-compact, year.`

  - paper-conference: conditional on `publisher` —
      with: `title. event, page-long. publisher, year.`
      without: `title. event, page-long, year.`

  - chapter: title `. ` then `In container, ..., page-long, year.`

`cvpr/lib.typ` switches `bibliography(style: ...)` to `cvpr.csl`.
Output now matches LaTeX/ieeenat_fullname.bst field-for-field, modulo
hayagriva sentence-case lowercasing hyphenated and all-caps words
differently from BST (`Context-Motion` → `Con-text-motion`,
`UAVs` → `Uavs`); use `{...}` brace protection in source bib/yml to
opt out per-word.

LaTeX `pagebackref` (review-mode page-numbers-where-cited annotation
appended to each bibliography entry) is not implemented in this
commit. typst's `bibliography()` element collapses entries into one
opaque block with no per-entry hook, and the community `retrofit`
package's grid/block detection broke on typst 0.14. When typst#942
(Bibliography customization API) lands a per-entry rendering hook,
backref can be added via show rules without bypassing
`bibliography()` ourselves.
@DzmingLi DzmingLi force-pushed the cvpr-fidelity-pass branch from cfab2db to dd0b292 Compare May 7, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants