const WORD_FONT_SIZE_PT = 14; const WORD_LINE_SPACING = 1.5; const A4_WIDTH_MM = 210; const A4_HEIGHT_MM = 297; const MARGIN_TOP_MM = 20; const MARGIN_BOTTOM_MM = 20; const MARGIN_LEFT_MM = 30; const MARGIN_RIGHT_MM = 10; const FIRST_LINE_INDENT_MM = 12.5; const MM_PER_POINT = 0.3527777778; const TEXT_WIDTH_MM = A4_WIDTH_MM - MARGIN_LEFT_MM - MARGIN_RIGHT_MM; const TEXT_HEIGHT_MM = A4_HEIGHT_MM - MARGIN_TOP_MM - MARGIN_BOTTOM_MM; const LINE_HEIGHT_MM = WORD_FONT_SIZE_PT * WORD_LINE_SPACING * MM_PER_POINT; const LINES_PER_PAGE = Math.floor(TEXT_HEIGHT_MM / LINE_HEIGHT_MM); const AVG_CHAR_WIDTH_EM = 0.58; const AVG_CHAR_WIDTH_MM = WORD_FONT_SIZE_PT * AVG_CHAR_WIDTH_EM * MM_PER_POINT; const CHARS_PER_LINE = Math.floor(TEXT_WIDTH_MM / AVG_CHAR_WIDTH_MM); const AVG_PARAGRAPHS_PER_PAGE = 4; // average paragraph count across typical student papers const INDENT_CHARS_LOSS = Math.round( (FIRST_LINE_INDENT_MM / AVG_CHAR_WIDTH_MM) * AVG_PARAGRAPHS_PER_PAGE, ); const LAYOUT_FUDGE_FACTOR = 0.96; export const CHARACTERS_PER_PAGE = Math.round( (LINES_PER_PAGE * CHARS_PER_LINE - INDENT_CHARS_LOSS) * LAYOUT_FUDGE_FACTOR, ); export function pagesToCharacters(pages: number): number { if (!Number.isFinite(pages) || pages <= 0) return 0; return Math.max(0, Math.round(pages * CHARACTERS_PER_PAGE)); } export function charactersToPages(characters: number): number { if (!Number.isFinite(characters) || characters <= 0) return 0; const raw = characters / CHARACTERS_PER_PAGE; return Math.max(0, Math.ceil(raw)); }