Let a document be printed on A4 or US Letter, either way up (#344) #1084

Merged
tiagoagueda merged 1 commit from 344-paper-sizes into main 2026-10-29 18:38:17 +00:00
Owner

Every document was A4 portrait: a constant for Chromium, another in the
Word and OpenDocument writers, and size: A4 in every theme. Nobody could
choose a paper, and a theme could not say what it prints on.

documents/paper.py is the vocabulary: four papers (A4 and US Letter,
portrait and landscape), what a theme declares, and the page rule. Theme
gains papers, a list for every kind or a mapping per kind, each entry a
paper's key or "any"; a theme that declares nothing is A4 portrait only.
Plain and Classic are fluid and declare "any".

Postulo writes the page rule, not the theme: one @page { size: … } and a
<meta name="postulo-paper"> are written last in the document's head (with
the preview's nonce), so a theme's own rule loses to it, and the themes'
size: A4 is gone. The template is handed paper too. WeasyPrint reads
the rule; Chromium reads the meta and is given the width and height in
place of PAGE_FORMAT; the outline carries the paper, and the Word writer
sets pgSz (with w:orient) and the OpenDocument writer the page layout.

documents/paper_choice.py is the chain: SiteSettings.default_paper (Server
settings > Defaults > Documents; blank is A4 portrait), then
Profile.paper (Settings > Export > Paper; blank follows the instance),
then one export's choice. A default the theme does not print on gives way
to A4 portrait, or the theme's first paper; a paper asked for one export
that the theme does not print on raises CannotRender, said on the page.

The CV page offers the paper beside Download draft PDF, posted with the
file-properties form by Export PDF, the draft and every format; the
letter's draft and the report's page offer it too, and the errands carry
it. The picker offers only what the theme prints on and says why where
that is fewer than four. Record what you sent draws on the default.

The account archive moves to format 79 and carries paper on the
profile, read back against the column's choices. Migrations
accounts/0051_paper and core/0040_default_paper. The render tests draw
each built-in theme's every kind, the report included, on each paper and
measure the page; tests/test_paper_sizes.py holds the rest, with Chromium
drawing each paper where a browser launches.

Closes #344

Notes for review

  • Papers: A4 and US Letter, each portrait or landscape (documents/paper.py). The choice runs from the instance's default (Server settings → Defaults → Documents), to the person's (Settings → Export → Paper), to one export's choice. The picker sits on the CV page, the letter's draft and the report.
  • Themes: a theme declares its papers (Theme.papers, a list or a mapping by kind). One that declares nothing gets A4 portrait only. Plain and Classic declare "any", and size: A4 is gone from every theme template.
  • The page rule: Postulo writes @page { size: … } itself. Chromium, DOCX and ODT honour the choice too; ODT also hard-coded A4 before.
  • Refusals and fallbacks:
    • An export choice the theme doesn't support is refused on the page.
    • A default the theme doesn't support falls back to the theme's first paper.
  • Migrations and format: accounts 0051_paper, core 0040_default_paper. Archive format 79, which clashes with #331 (PR #1080); the second to land takes 80.
  • WeasyPrint render tests (every theme × kind × paper, measuring the page and checking for overflow) run only in CI, which has Pango. The Chromium ones passed locally.

🤖 Generated with Claude Code

Every document was A4 portrait: a constant for Chromium, another in the Word and OpenDocument writers, and `size: A4` in every theme. Nobody could choose a paper, and a theme could not say what it prints on. documents/paper.py is the vocabulary: four papers (A4 and US Letter, portrait and landscape), what a theme declares, and the page rule. Theme gains `papers`, a list for every kind or a mapping per kind, each entry a paper's key or "any"; a theme that declares nothing is A4 portrait only. Plain and Classic are fluid and declare "any". Postulo writes the page rule, not the theme: one `@page { size: … }` and a `<meta name="postulo-paper">` are written last in the document's head (with the preview's nonce), so a theme's own rule loses to it, and the themes' `size: A4` is gone. The template is handed `paper` too. WeasyPrint reads the rule; Chromium reads the meta and is given the width and height in place of PAGE_FORMAT; the outline carries the paper, and the Word writer sets pgSz (with w:orient) and the OpenDocument writer the page layout. documents/paper_choice.py is the chain: SiteSettings.default_paper (Server settings > Defaults > Documents; blank is A4 portrait), then Profile.paper (Settings > Export > Paper; blank follows the instance), then one export's choice. A default the theme does not print on gives way to A4 portrait, or the theme's first paper; a paper asked for one export that the theme does not print on raises CannotRender, said on the page. The CV page offers the paper beside Download draft PDF, posted with the file-properties form by Export PDF, the draft and every format; the letter's draft and the report's page offer it too, and the errands carry it. The picker offers only what the theme prints on and says why where that is fewer than four. Record what you sent draws on the default. The account archive moves to format 79 and carries `paper` on the profile, read back against the column's choices. Migrations accounts/0051_paper and core/0040_default_paper. The render tests draw each built-in theme's every kind, the report included, on each paper and measure the page; tests/test_paper_sizes.py holds the rest, with Chromium drawing each paper where a browser launches. Closes #344 ## Notes for review - **Papers:** A4 and US Letter, each portrait or landscape (`documents/paper.py`). The choice runs from the instance's default (Server settings → Defaults → Documents), to the person's (Settings → Export → Paper), to one export's choice. The picker sits on the CV page, the letter's draft and the report. - **Themes:** a theme declares its papers (`Theme.papers`, a list or a mapping by kind). One that declares nothing gets A4 portrait only. Plain and Classic declare `"any"`, and `size: A4` is gone from every theme template. - **The page rule:** Postulo writes `@page { size: … }` itself. Chromium, DOCX and ODT honour the choice too; ODT also hard-coded A4 before. - **Refusals and fallbacks:** - An export choice the theme doesn't support is refused on the page. - A *default* the theme doesn't support falls back to the theme's first paper. - **Migrations and format:** `accounts 0051_paper`, `core 0040_default_paper`. Archive format **79**, which clashes with #331 (PR #1080); the second to land takes 80. - **WeasyPrint render tests** (every theme × kind × paper, measuring the page and checking for overflow) run only in CI, which has Pango. The Chromium ones passed locally. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Let a document be printed on A4 or US Letter, either way up (#344)
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m55s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m42s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m53s
CI / Security audit (pull_request) Successful in 1m13s
CI / Browser tests (Chromium) (pull_request) Successful in 33m52s
bd01b96ccc
Every document was A4 portrait: a constant for Chromium, another in the
Word and OpenDocument writers, and `size: A4` in every theme. Nobody could
choose a paper, and a theme could not say what it prints on.

documents/paper.py is the vocabulary: four papers (A4 and US Letter,
portrait and landscape), what a theme declares, and the page rule. Theme
gains `papers`, a list for every kind or a mapping per kind, each entry a
paper's key or "any"; a theme that declares nothing is A4 portrait only.
Plain and Classic are fluid and declare "any".

Postulo writes the page rule, not the theme: one `@page { size: … }` and a
`<meta name="postulo-paper">` are written last in the document's head (with
the preview's nonce), so a theme's own rule loses to it, and the themes'
`size: A4` is gone. The template is handed `paper` too. WeasyPrint reads
the rule; Chromium reads the meta and is given the width and height in
place of PAGE_FORMAT; the outline carries the paper, and the Word writer
sets pgSz (with w:orient) and the OpenDocument writer the page layout.

documents/paper_choice.py is the chain: SiteSettings.default_paper (Server
settings > Defaults > Documents; blank is A4 portrait), then
Profile.paper (Settings > Export > Paper; blank follows the instance),
then one export's choice. A default the theme does not print on gives way
to A4 portrait, or the theme's first paper; a paper asked for one export
that the theme does not print on raises CannotRender, said on the page.

The CV page offers the paper beside Download draft PDF, posted with the
file-properties form by Export PDF, the draft and every format; the
letter's draft and the report's page offer it too, and the errands carry
it. The picker offers only what the theme prints on and says why where
that is fewer than four. Record what you sent draws on the default.

The account archive moves to format 79 and carries `paper` on the
profile, read back against the column's choices. Migrations
accounts/0051_paper and core/0040_default_paper. The render tests draw
each built-in theme's every kind, the report included, on each paper and
measure the page; tests/test_paper_sizes.py holds the rest, with Chromium
drawing each paper where a browser launches.

Closes #344

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tiagoagueda force-pushed 344-paper-sizes from bd01b96ccc
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m55s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m42s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m53s
CI / Security audit (pull_request) Successful in 1m13s
CI / Browser tests (Chromium) (pull_request) Successful in 33m52s
to fddcca5272
Some checks failed
CI / Browser tests (Chromium) (pull_request) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (pull_request) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Has been cancelled
CI / Database tests on PostgreSQL (pull_request) Has been cancelled
CI / Security audit (pull_request) Has been cancelled
2026-10-29 18:34:45 +00:00
Compare
tiagoagueda force-pushed 344-paper-sizes from fddcca5272
Some checks failed
CI / Browser tests (Chromium) (pull_request) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (pull_request) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Has been cancelled
CI / Database tests on PostgreSQL (pull_request) Has been cancelled
CI / Security audit (pull_request) Has been cancelled
to c9523eceae
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 13m21s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m45s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m41s
CI / Security audit (pull_request) Successful in 46s
CI / Browser tests (Chromium) (pull_request) Successful in 33m39s
2026-10-29 18:35:06 +00:00
Compare
tiagoagueda deleted branch 344-paper-sizes 2026-10-29 18:38:22 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo!1084
No description provided.