Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Three commands, all of which must pass:
```
npm run lint # banned phrases, heading rules, figure and chapter-reference rules
node scripts/check-figure-geometry.mjs # renders every SVG in Chromium: text overlap, arrowheads, coin marks
npm run build-pdf # the whole book; prints only the output path on success
npm run build-pdf-digital # the whole book; prints only the output path on success
```

The first two also run in GitHub Actions on every pull request and every push to `main`
Expand All @@ -16,8 +16,8 @@ does not run there, so run it yourself. The build reads the finished PDF back an
quote printed outside the code face, which is the one check on the typographic quotes that does not
trust the build's own conversion.

`npm run preview -- <chapter> "<phrase>"` renders a single chapter around a phrase, which is faster than
building the book when checking one edit.
`npm run preview -- <chapter> "<phrase>"` renders the page of the digital edition carrying a phrase,
which is faster than paging through the book when checking one edit.

`node scripts/check-figure-bands.mjs` reports horizontal dead space in the figures — the hole a
deletion leaves when the columns around it stay put. It is advisory, not a gate: it over-reports on
Expand Down
26 changes: 12 additions & 14 deletions PRINT-SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Figure captions and table cells are ragged right for the same reason: they are s

## Running Heads and Folios

The header carries two items: the book's title on the left, the current chapter's title on the right, both 8pt Gelasio italic in grey. That is the conventional pair, and it is all that fits. At this measure a third box wraps both of them onto two lines, which is why the prerelease marker sits in the footer at bottom left instead, with the folio centered. A build carrying `REVIEWER_NAME` fills the third footer box, bottom right, with that name and the short hash of the commit it was built from, so a single leaf out of an advance copy still says whose it is and which revision of the text it shows. The hash carries a `-dirty` suffix when the working tree had uncommitted changes, because that text matches no commit exactly. Unstamped builds leave that box empty.
The header carries two items: the book's title on the left, the current chapter's title on the right, both 8pt Gelasio italic in grey. That is the conventional pair, and it is all that fits. At this measure a third box wraps both of them onto two lines, which is why the prerelease marker sits in the footer at bottom left instead, with the folio centered. A draft, which always carries `--reviewer`, fills the third footer box, bottom right, with that name and the short hash of the commit it was built from, so a single leaf out of an advance copy still says whose it is and which revision of the text it shows. The hash carries a `-dirty` suffix when the working tree had uncommitted changes, because that text matches no commit exactly. Final builds leave that box empty.

Headers and folios are suppressed on the cover, the title page, chapter openers (headers only), part titles, and the blank versos paged.js inserts. The reviewer stamp follows the folio: present wherever the folio is, absent from the cover and the blanks.

Expand All @@ -92,32 +92,30 @@ The drawing is vector throughout and its type is outlined, so the file embeds no

**The press cover is a separate export, sized by `npm run resize-cover`.** A file going to KDP wraps the whole book: 0.125in bleed + 7.44in back cover + spine + 7.44in front cover + 0.125in bleed wide, and 9.69 + 0.25in tall. The spine is the interior page count times KDP's per-page thickness (0.002252in for white paper, 0.0025in for cream), so at 308 interior pages on white (309 with the cover) the target is 15.824 x 9.94in with a 0.694in spine. None of that belongs in `assets/cover.pdf`, which is the front cover at trim and nothing else.

`npm run resize-cover` rescales the Figma export of the full wrap, kept at `assets/cover-wrap.pdf` (another path can be passed as an argument, `--pages N` and `--paper white|cream` as options), to that exact size, filling the target and cropping overflow symmetrically into the bleed. Page count is read from the built book unless `--pages` is passed; pass the count of the file actually uploaded as the interior (a `NO_COVER=1` interior is one page shorter than the built book). The script also measures the spine the art was drawn for, by aligning trim heights and taking the width left over after two covers and two bleeds, and warns when that disagrees with the real spine: the art then bleeds onto or short of the true spine edges by half the difference per side, and spine text rides off-center by the same amount. A warning means the rescale is good enough for a proof and the production cover should be re-exported from a Figma frame drawn for the frozen page count (1 px = 1 pt: 15.824 x 9.94in is a 1139 x 716 px frame).
`npm run resize-cover` rescales the Figma export of the full wrap, kept at `assets/cover-wrap.pdf` (another path can be passed as an argument, `--pages N` and `--paper white|cream` as options), to that exact size, filling the target and cropping overflow symmetrically into the bleed. Page count is read from the paperback interior, the file actually uploaded, unless `--pages` is passed. `npm run build-pdf-paperback` runs it straight after building that interior, so the two always agree; run it alone to pass `--paper cream` or `--pages`. The script also measures the spine the art was drawn for, by aligning trim heights and taking the width left over after two covers and two bleeds, and warns when that disagrees with the real spine: the art then bleeds onto or short of the true spine edges by half the difference per side, and spine text rides off-center by the same amount. A warning means the rescale is good enough for a proof and the production cover should be re-exported from a Figma frame drawn for the frozen page count (1 px = 1 pt: 15.824 x 9.94in is a 1139 x 716 px frame).

## US Letter

`--size letter` builds the same manuscript at 8.5 x 11in with 1.1in side margins, and writes it to a filename carrying the size so it cannot overwrite the print build. It is for reading on screen and for running a draft off an office printer, where Letter is the paper in the tray.
`--size letter` builds the same manuscript at 8.5 x 11in with 1.1in side margins, and writes it to a filename carrying the size so it cannot overwrite a build at the trim. It is for reading on screen and for running a draft off an office printer, where Letter is the paper in the tray.

It is not a book size and is not what gets printed. Letter's margins leave a 6.3in measure, about 100 characters of 10pt type, which is longer than a reader tracks comfortably, and print on demand costs more per unit at that trim. The page count differs too: 267 pages against 309, because the larger page carries more.

## Building It

```
npm install
npm run build-pdf # print size, into build/
npm run build-pdf -- --size letter # US Letter
NO_COVER=1 npm run build-pdf # interior only
FINAL=1 NO_COVER=1 NO_LINKS=1 npm run build-pdf # the interior uploaded to KDP
FINAL=1 npm run build-pdf # no "prerelease draft" note on the title page or in the footers
REVIEWER_NAME="..." npm run build-pdf # advance copy: name and commit hash on the title page and every folio
npm run build-pdf -- out.pdf # explicit output path
npm run build-pdf -- --help # the switches, from the script itself
npm run preview -- <name> "<phrase>" # rasterize one page to check it
npm run build-pdf-digital # the finished book for screens: cover and links, no draft markings
npm run build-pdf-paperback # the KDP upload: the interior (no cover, no links, no draft markings), then the cover sized to it
npm run build-pdf-draft -- --reviewer "..." # advance copy: name and commit hash on the title page and every folio
npm run build-pdf-draft -- --reviewer "..." --size letter # the same at US Letter
npm run build-pdf-digital -- out.pdf # explicit output path
npm run build-pdf-digital -- --help # the switches, from the script itself
npm run preview -- <name> "<phrase>" # rasterize one page of the digital edition to check it
```

An unrecognized switch, a `--size` with no value or an unknown one, and more than one output path all print the usage and exit nonzero, rather than taking the stray argument for a filename.
An unrecognized switch, a `--size` or `--reviewer` with no value or an unknown size, and more than one output path all print the usage and exit nonzero, rather than taking the stray argument for a filename. So does a draft with no `--reviewer`, since a review copy nobody can trace is the one the stamp exists to prevent, and a final build with one, since a review copy is not final.

The switches compose: `NO_COVER=1 npm run build-pdf -- --size letter` is the cover-less Letter build.
Each build writes its own file in `build/`: the digital edition is `Building Financial Software on Solana.pdf`, and every other build adds a label, `(paperback interior)`, `(draft, <reviewer>)` or `(letter)`, so one build never overwrites another. The three scripts set `FINAL=1`, `NO_COVER=1` and `NO_LINKS=1` for `scripts/build-pdf.mjs`, and those still compose by hand for an unusual combination: `FINAL=1 NO_COVER=1 node scripts/build-pdf.mjs --size letter` is a cover-less final Letter build.

**The interior uploaded to KDP carries no links.** A link in a PDF is an annotation laid over the page rather than ink on it, and KDP removes every one it finds, then reports each page it removed one from as "non-printable markup" to be checked: every table of contents page, and every page with a cross-reference or an address. Nothing printed changes when they go, because every address is printed in full after its link text, but the report cannot say so. `NO_LINKS=1` builds the file without them, and without the named destinations the internal links pointed at, so KDP has nothing to remove. Every other build keeps them, for reading on screen.

Expand Down
16 changes: 6 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,20 @@ The book's home is [solanabook.org](https://solanabook.org), which hosts the pub

## Building the PDF

`npm install` then `npm run build-pdf` renders the whole manuscript, in table-of-contents order, as a single-column textbook PDF (serif type, running headers, page numbers, a contents page with page numbers) into `build/`. The page is 7.44 x 9.69in, the KDP standard trim closest in proportion to the 7 x 9.19in size technical books are usually printed at; `npm run build-pdf -- --size letter` builds the same manuscript at US Letter for screen reading and office printers. `npm run resize-cover` sizes the full-wrap cover export (`assets/cover-wrap.pdf`) to KDP's exact requirement, spine width included. [PRINT-SPECIFICATION.md](PRINT-SPECIFICATION.md) records the trim, margins, type, cover math, and the rest of the physical book. Requires a Chromium binary; the script uses `/opt/pw-browsers/chromium` or the `CHROMIUM_PATH` environment variable.
`npm install` then `npm run build-pdf-digital` renders the whole manuscript, in table-of-contents order, as a single-column textbook PDF (serif type, running headers, page numbers, a contents page with page numbers) into `build/`. The page is 7.44 x 9.69in, the KDP standard trim closest in proportion to the 7 x 9.19in size technical books are usually printed at. Three builds share it: `build-pdf-digital` is the finished book for reading on screen, `build-pdf-paperback` the interior uploaded to KDP, and `build-pdf-draft -- --reviewer "<name>"` an advance copy stamped for one reviewer, which takes `--size letter` for a reviewer printing on an office printer. `npm run resize-cover` sizes the full-wrap cover export (`assets/cover-wrap.pdf`) to KDP's exact requirement, spine width included. [PRINT-SPECIFICATION.md](PRINT-SPECIFICATION.md) records the trim, margins, type, cover math, and the rest of the physical book. Requires a Chromium binary; the script uses `/opt/pw-browsers/chromium` or the `CHROMIUM_PATH` environment variable.

## Preparing for press

1. Build the interior without the cover page, without the clickable links that KDP would otherwise strip as non-printable markup, and without the prerelease draft markings:
1. Export the full-wrap cover (back, spine and front, with bleed) from Figma as a PDF to `assets/cover-wrap.pdf`.
2. Build the interior without the cover page, without the clickable links that KDP would otherwise strip as non-printable markup, and without the prerelease draft markings, then resize the cover to KDP's exact size, spine width taken from the interior's page count:

```
FINAL=1 NO_COVER=1 NO_LINKS=1 npm run build-pdf
npm run build-pdf-paperback
```

2. Export the full-wrap cover (back, spine and front, with bleed) from Figma as a PDF to `assets/cover-wrap.pdf`.
3. Resize the cover to KDP's exact size. Run this after step 1, because the spine width comes from the page count of the book in `build/`:
`npm run resize-cover` runs the second half alone, for cream paper (`-- --paper cream`) or a different page count (`-- --pages N`).

```
npm run resize-cover
```

4. Upload both to Amazon KDP: `build/Building Financial Software on Solana.pdf` as the manuscript, and `build/cover-kdp-<pages>pp-white.pdf` as the cover.
3. Upload both to Amazon KDP: `build/Building Financial Software on Solana (paperback interior).pdf` as the manuscript, and `build/cover-kdp-<pages>pp-white.pdf` as the cover.

## Status

Expand Down
Loading
Loading