import type { BookDocument } from '@trek/shared'
import { SpreadView } from './SpreadView'
import { MARK_LENGTH, MARK_WEIGHT, sheetBox, sheetsFor, type Sheet, type SheetMode, foliosOf } from './bookSheets'
/**
* The book laid out for a press.
*
* ── One renderer ─────────────────────────────────────────────────────────
*
* These are the same `SpreadView`s the editor draws, at 1:1 with `print` on.
* That is the whole reason Studio renders in DOM and positions in millimetres:
* a second renderer for output is a second renderer to proofread against, and
* the day it drifts is the day someone finds out by opening a printed book.
*
* ── What is added on top ─────────────────────────────────────────────────
*
* Only what a press needs and a screen does not: the bleed carried out past the
* trim on all four sides, and crop marks outside it saying where to cut.
* Nothing here changes where anything sits on the page.
*/
export function BookSheetsView({
doc, mode, marks,
}: {
doc: BookDocument
mode: SheetMode
/** Crop marks, and the room around the bleed they need. */
marks: boolean
}) {
const sheets = sheetsFor(doc, mode)
return (
{sheets.map((sheet, i) => (
))}
)
}
function SheetView({
sheet, doc, marks, last,
}: {
sheet: Sheet
doc: BookDocument
marks: boolean
last: boolean
}) {
/*
* Each sheet is sized to what is on it, not to the widest in the book.
*
* A cover is one page wide and a spread is two, and in spread mode they sit
* in the same document. Making every sheet the wider of the two — so that one
* `@page` rule would cover them all — put a cover in the middle of a
* spread-sized page with white either side, which is not a cover. The print
* CSS carries a named page rule for the narrow ones instead.
*/
const box = sheetBox(sheet.width, sheet.height, doc.page.bleed, marks)
return (
{/*
The window on the spread. One page wide plus bleed on both sides, which
is why a photograph crossing the gutter comes out aligned: both halves
are the same spread seen through openings a page apart.
*/}
{marks &&
}
)
}
/**
* Where to cut.
*
* Eight hairlines, two per corner, starting at the bleed edge and reaching
* outward — never crossing the trim, because a mark printed inside the trim is
* a mark that stays in the finished book.
*/
function CropMarks({
box, width, height,
}: {
box: ReturnType
width: number
height: number
}) {
const left = box.margin
const top = box.margin
const right = left + width
const bottom = top + height
const gap = box.bleed
const line = (x: number, y: number, w: number, h: number, key: string) => (
)
const marks = []
for (const [x, dx] of [[left, -1], [right, 1]] as const) {
for (const [y, dy] of [[top, -1], [bottom, 1]] as const) {
// Horizontal arm, reaching out from the corner past the bleed.
marks.push(line(
dx < 0 ? x - gap - MARK_LENGTH : x + gap,
y - MARK_WEIGHT / 2,
MARK_LENGTH, MARK_WEIGHT,
`h${x}-${y}`,
))
// Vertical arm.
marks.push(line(
x - MARK_WEIGHT / 2,
dy < 0 ? y - gap - MARK_LENGTH : y + gap,
MARK_WEIGHT, MARK_LENGTH,
`v${x}-${y}`,
))
}
}
return <>{marks}>
}