diff --git a/docs/CARD.md b/docs/CARD.md index 1a7ed5c..81ee67b 100644 --- a/docs/CARD.md +++ b/docs/CARD.md @@ -23,9 +23,10 @@ Obiekt `card` identyfikuje serię, numer, wersję, UUID, autora i status. Obiekt `generated` wskazuje ścieżki wyjściowe TeX/HTML/CSS. Pole `template` wybiera layout. -Opcjonalny obiekt `title_block` włącza tabliczkę zasobu o wysokości dokładnie -3 cm na każdej stronie, także pierwszej, bibliografii, słownika i stronach -kontynuacji. Tabliczka zastępuje dużą tabelę metadanych strony tytułowej. +Opcjonalny obiekt `title_block` włącza tabliczkę zasobu o skonfigurowanej +wysokości 2,6 lub 3 cm na każdej stronie, także pierwszej, bibliografii, +słownika i stronach kontynuacji. Tabliczka zastępuje dużą tabelę metadanych +strony tytułowej. Zawiera kategorię, tytuł, metadane karty, numer strony oraz widoczny, kanoniczny URL. Ten sam URL jest kodowany lokalnie w QR: jako SVG w HTML i przez pakiet `qrcode` w TeX. Opcjonalny `repository_url` dodaje drugi QR do @@ -36,7 +37,8 @@ nagłówek na każdej logicznej karcie, a wydruk używa `@page { size: A4; }` or pól marginesu, dlatego nagłówek powtarza się również po automatycznym podziale długiej sekcji. -Pola `height_cm: 3`, `repeat_on_every_page: true` i +Pole `height_cm` przyjmuje `2.6` (kompaktowy nagłówek z QR 22 mm i równym +światłem) albo `3`; pola `repeat_on_every_page: true` i `replace_front_matter: true` dokumentują ten kontrakt. `show_qr: true` wymaga kanonicznego adresu HTTP(S) w `url` albo pary `url_host_uuid`/`doc_uuid`, z której powstaje `https:///`. diff --git a/schemas/card-source.schema.json b/schemas/card-source.schema.json index 4d74416..21aff82 100644 --- a/schemas/card-source.schema.json +++ b/schemas/card-source.schema.json @@ -311,7 +311,7 @@ }, "show_qr": { "type": "boolean" }, "show_repository_qr": { "type": "boolean" }, - "height_cm": { "const": 3 }, + "height_cm": { "enum": [2.6, 3] }, "repeat_on_every_page": { "const": true }, "replace_front_matter": { "const": true } }, diff --git a/tools/render_card.py b/tools/render_card.py index 90f75be..6c2ec42 100644 --- a/tools/render_card.py +++ b/tools/render_card.py @@ -422,8 +422,8 @@ def validate_source(data: dict) -> None: errors.append( "title_block.show_qr=true wymaga title_block.url albo pary url_host_uuid/doc_uuid" ) - if title_block.get("height_cm", 3) not in (3, 3.0): - errors.append("title_block.height_cm musi wynosić dokładnie 3") + if title_block.get("height_cm", 3) not in (2.6, 3, 3.0): + errors.append("title_block.height_cm musi wynosić 2.6 albo 3") if title_block.get("repeat_on_every_page", True) is not True: errors.append("title_block.repeat_on_every_page musi być true") if title_block.get("replace_front_matter", True) is not True: @@ -672,6 +672,10 @@ def macro(name: str, value: str) -> str: def render_preamble(data: dict) -> list[str]: card = data["card"] + header_height_mm = resource_header_height_mm(data) + source_url = repository_url(data) + doc_uuid = str((data.get("title_block") or {}).get("doc_uuid", "")) + card_number = f"{card.get('number', '')}/{card.get('count', '')}" return [ r"\documentclass[12pt,a4paper]{article}", "", @@ -683,7 +687,7 @@ def render_preamble(data: dict) -> list[str]: r"\usepackage{amsmath}", r"\usepackage{amssymb}", r"\usepackage{xcolor}", - r"\usepackage[a4paper,left=2.15cm,right=2.15cm,top=3.75cm,bottom=2.2cm,includehead]{geometry}", + rf"\usepackage[a4paper,left=2.15cm,right=2.15cm,top=0.5cm,bottom=0.5cm,headheight={header_height_mm:g}mm,headsep=3mm,footskip=8mm,includehead,includefoot]{{geometry}}", r"\usepackage{eso-pic}", r"\usepackage{marginnote}", r"\usepackage{array}", @@ -726,9 +730,9 @@ def render_preamble(data: dict) -> list[str]: r"\newcommand{\EscWriteRow}[1][2.8em]{\rule{0pt}{#1}}", r"\setlength{\parindent}{0pt}", r"\setlength{\parskip}{0.45em}", - r"\setlength{\headheight}{30mm}", + rf"\setlength{{\headheight}}{{{header_height_mm:g}mm}}", r"\setlength{\headsep}{3mm}", - r"\setlength{\footskip}{24pt}", + r"\setlength{\footskip}{8mm}", r"\setlength{\marginparwidth}{1.5cm}", r"\setlength{\marginparsep}{0.25cm}", r"\renewcommand{\arraystretch}{1.22}", @@ -738,10 +742,18 @@ def render_preamble(data: dict) -> list[str]: r"\pagestyle{fancy}", r"\fancyhf{}", r"\fancyhead[C]{\ESCPageResourceHeader}", - r"\lfoot{\scriptsize rewizja \BuildCommit}", - r"\cfoot{\scriptsize \thepage/\pageref{LastPage}}", - r"\rfoot{\scriptsize \DocumentUUID}", + r"\fancyfoot[C]{%", + r" \begin{minipage}{\textwidth}%", + r" \hrule height0.35pt\vspace{0.5mm}%", + r" \fontsize{4.2}{4.8}\selectfont\ttfamily%", + r" \begin{tabularx}{\textwidth}{@{}p{27mm}p{49mm}Xr@{}}%", + rf" \textbf{{KARTA}} {tex_escape(card_number)} & \textbf{{UUID}} {tex_escape(card.get('uuid', ''))} & \textbf{{DOKUMENT}} {tex_escape(doc_uuid)} & \textbf{{ARKUSZ}} \thepage/\pageref{{LastPage}} \\%", + rf" \textbf{{WERSJA}} {tex_escape(card.get('version', ''))} · {tex_escape(card.get('revision_date', ''))} & \multicolumn{{2}}{{l}}{{\fontsize{{3.6}}{{4.1}}\selectfont\textbf{{GITEA}} \href{{{source_url}}}{{\nolinkurl{{{source_url}}}}}}} & \textbf{{AUTOR}} {tex_escape(card.get('author', ''))}%", + r" \end{tabularx}%", + r" \end{minipage}%", + r"}", r"\renewcommand{\headrulewidth}{0pt}", + r"\renewcommand{\footrulewidth}{0pt}", "", r"\newcommand{\ESCMarginTag}[4]{%", r" \hspace*{#1}\textcolor{#2}{#3~#4}%", @@ -2849,7 +2861,8 @@ def render_main_html(data: dict) -> str: f'
{render_running_header_html(card) if not use_resource_header else ""}' f'{heading}' f'{render_step_map_html(section, data)}{content}
' - f'' + f'' + f'{render_page_footer_html(data, page_number, total_pages) if use_resource_header else ""}' ) if bibliography_html: bibliography_page = len(data.get("sections", [])) + 2 @@ -2859,7 +2872,8 @@ def render_main_html(data: dict) -> str: f'' f'
{render_running_header_html(card) if not use_resource_header else ""}' f'

Bibliografia i źródła

{bibliography_html}
' - f'' + f'' + f'{render_page_footer_html(data, bibliography_page, total_pages) if use_resource_header else ""}' ) scope = data.get("front_page_scope", {}) @@ -2882,17 +2896,17 @@ def render_main_html(data: dict) -> str: css = r""" :root{--ink:#111;--muted:#666;--line:#b9b9b9;--line-soft:#dddddd;--paper:#fff;--desk:#eeeeee;--og:#008000;--tech:#d36b00;--kw:#0018c8;--we:#e00000} -*{box-sizing:border-box} html{scroll-behavior:smooth} body{margin:0;background:var(--desk);color:var(--ink);font-family:Georgia,"Times New Roman",serif;line-height:1.42} +*{box-sizing:border-box} html{scroll-behavior:smooth} body{margin:0;background:var(--desk);color:var(--ink);font-family:"Latin Modern Roman","LM Roman 12",Georgia,"Times New Roman",serif;line-height:1.25} .topbar{position:sticky;top:0;z-index:20;background:rgba(245,245,245,.94);border-bottom:1px solid #d0d0d0;padding:8px 14px;font-family:system-ui,-apple-system,Segoe UI,sans-serif} .topbar details{max-width:1120px;margin:0 auto}.topbar summary{cursor:pointer;font-weight:650}.toc-links{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.toc-links a{font-size:13px;color:#333;text-decoration:none;border:1px solid #ccc;background:white;border-radius:4px;padding:3px 7px} -.paper{padding:18px 12px 44px;overflow-x:auto}.sheet{width:210mm;min-height:297mm;margin:0 auto 18px;background:var(--paper);box-shadow:0 2px 12px rgba(0,0,0,.13);display:grid;grid-template-columns:22mm minmax(0,1fr) 22mm;column-gap:4mm;align-content:start;padding:13mm 21.5mm 11mm}.resource-header-enabled .sheet{padding-top:5mm;row-gap:0} -.pdf-main{min-width:0}.running-header{display:flex;justify-content:space-between;gap:18px;align-items:flex-end;border-bottom:1px solid #111;padding-bottom:3px;margin-bottom:26px;font-size:15px}.running-header span{white-space:nowrap} +.paper{padding:18px 12px 44px;overflow-x:auto}.sheet{position:relative;width:210mm;min-height:297mm;margin:0 auto 18px;background:var(--paper);box-shadow:0 2px 12px rgba(0,0,0,.13);display:block;padding:13mm 21.5mm 15mm}.resource-header-enabled .sheet{padding-top:5mm}.sheet>.page-resource-header{margin-bottom:3mm} +.pdf-main{min-width:0;width:100%}.running-header{display:flex;justify-content:space-between;gap:18px;align-items:flex-end;border-bottom:1px solid #111;padding-bottom:3px;margin-bottom:26px;font-size:15px}.running-header span{white-space:nowrap} .hero h1{font-size:24px;line-height:1.15;margin:0 0 4px;border-bottom:1px solid #111;padding-bottom:4px}.hero .byline{float:right;margin-top:-31px;font-size:15px} .meta{clear:both;display:grid;grid-template-columns:148px minmax(0,1fr);gap:4px 18px;padding:34px 0 24px;border-bottom:1px solid #111;font-size:17px}.meta dt{color:#333}.meta dd{margin:0;font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace} -.front-scope{padding:26px 0 10px;border-bottom:1px solid var(--line-soft)}.front-scope h2{font-size:28px;margin:0 0 12px}.front-scope p{font-size:18px} -.card-section h2{font-size:28px;line-height:1.2;margin:0 0 18px}.section-heading{display:flex;align-items:flex-start;gap:0}.section-heading .section-index{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:26px;margin-right:20px;font-weight:400}.section-heading .section-title{min-width:0}.task-identity{margin-left:auto;padding-left:22px;text-align:right;font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:10px;line-height:1.25;color:#555;white-space:nowrap}.task-identity strong,.task-identity-name,.task-identity code{display:block}.task-identity strong{font-size:12px;color:#111}.task-identity-name{max-width:240px;overflow:hidden;text-overflow:ellipsis}.task-identity code{font-size:9px;color:#777} -p{font-size:17px;margin:0 0 12px}.pdf-main ul{font-size:17px;margin:8px 0 14px 24px;padding:0}.pdf-main li{margin:5px 0} -.pdf-margin{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:7px;line-height:1.14;color:#333;align-self:start;position:sticky;top:48px;max-height:calc(100vh - 80px);overflow:hidden}.pdf-margin.left{text-align:left}.pdf-margin.right{text-align:left} +.front-scope{padding:0 0 3mm;border-bottom:1px solid var(--line-soft)}.front-scope h2{font-size:24px;line-height:1.15;margin:0 0 3mm}.front-scope p{font-size:16px} +.card-section h2{font-size:24px;line-height:1.15;margin:0 0 4mm}.section-heading{display:flex;align-items:flex-start;gap:0}.section-heading .section-index{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:23px;margin-right:12mm;font-weight:400}.section-heading .section-title{min-width:0}.task-identity{margin-left:auto;padding-left:22px;text-align:right;font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:10px;line-height:1.25;color:#555;white-space:nowrap}.task-identity strong,.task-identity-name,.task-identity code{display:block}.task-identity strong{font-size:12px;color:#111}.task-identity-name{max-width:240px;overflow:hidden;text-overflow:ellipsis}.task-identity code{font-size:9px;color:#777} +p{font-size:16px;margin:0 0 3mm}.pdf-main ul{font-size:16px;margin:2mm 0 3mm 7mm;padding:0}.pdf-main li{margin:1.2mm 0} +.pdf-margin{position:absolute;top:34mm;width:18mm;font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:5px;line-height:1.12;color:#333;max-height:245mm;overflow:hidden}.pdf-margin.left{left:2mm;text-align:left}.pdf-margin.right{right:2mm;text-align:left} .margin-title{font-weight:700;color:#666;margin-bottom:3px;letter-spacing:.02em}.margin-list{display:flex;flex-direction:column;align-items:flex-start}.margin-tag{display:block;margin:0 0 1px;padding:0;border:0;background:transparent;font:inherit;line-height:inherit;text-align:left;cursor:pointer;margin-left:var(--indent)}.margin-tag.we{color:var(--we)}.margin-tag.og,.margin-tag.tech{color:var(--og)}.margin-tag.kw{color:var(--kw)}.margin-tag .local{color:#777}.margin-tag:hover{text-decoration:underline}.margin-empty{color:#aaa} .step-map{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;line-height:1.3;color:#5c5c5c;background:transparent;border:0;border-top:1px solid #cfcfcf;border-bottom:1px solid #e0e0e0;border-radius:0;padding:8px 0;margin:0 0 16px}.step-map summary{cursor:pointer;font-weight:400;color:#555} .tree-legend{display:flex;flex-wrap:wrap;gap:4px;margin:7px 0}.steps{display:grid;gap:6px}.step-row{border-top:1px dashed #c9c9c9;padding-top:6px}.step-row:first-child{border-top:0}.step-title span{font-weight:700;margin-right:7px}.step-trees{display:flex;flex-wrap:wrap;gap:4px;margin-top:3px}.step-refs{display:flex;flex-wrap:wrap;gap:6px;margin-top:3px}.step-refs a{font-size:10px;color:#666} @@ -2901,12 +2915,12 @@ p{font-size:17px;margin:0 0 12px}.pdf-main ul{font-size:17px;margin:8px 0 14px 2 figure{margin:24px 0;text-align:center}figure img{max-width:100%;display:block;margin:0 auto;border:0;border-radius:0}figcaption{font-size:14px;color:#444;margin-top:8px;text-align:left}.card-asset.asset-screenshot img{border:1px solid #aaa}.card-asset a{display:block;cursor:zoom-in} .table-wrap{overflow:auto;margin:14px 0}table{border-collapse:collapse;width:100%;font-size:16px}th,td{border:1px solid #111;padding:5px 8px;text-align:left;vertical-align:top}th{font-weight:400;background:white} .empty-check{display:inline-block;width:1em;height:1em;border:1px solid #111;vertical-align:-.12em}.answer-line,.dotfill{display:block;border-bottom:1px dotted #111;height:1.45em;min-width:10em}.write-row{display:block;min-height:2.8em} -code{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace}.tasks{font-size:17px}.task-legacy{margin:10px 0}.task-legacy>strong{display:block;font:700 11px/1.2 "Latin Modern Mono",ui-monospace,monospace;color:#555}.task-flow{border:1px solid #aaa;margin:8px 0 18px}.task-flow-header{display:grid;grid-template-columns:auto 1fr auto;align-items:baseline;gap:10px;padding:8px 11px;background:#f3f3f3;border-bottom:1px solid #aaa}.task-flow-header span{font:700 10px/1.2 "Latin Modern Mono",ui-monospace,monospace;color:#555}.task-flow-header h3{font-size:18px;margin:0}.task-flow-header code{font-size:8px;color:#777}.task-block,.task-exercise{margin:10px 12px;padding:0 0 10px;border-bottom:1px solid #ddd}.task-block header,.task-exercise header{display:flex;align-items:baseline;gap:8px;margin-bottom:6px}.task-block header span,.task-exercise header span{font:700 9px/1.2 "Latin Modern Mono",ui-monospace,monospace;text-transform:uppercase;color:#666}.task-block h4,.task-exercise h4{font-size:16px;margin:0}.block-steps{counter-reset:block-step;margin:5px 0 0 24px!important}.block-steps li{margin:7px 0}.block-steps li::marker{font-family:"Latin Modern Mono",ui-monospace,monospace}.task-exercise{border:1px solid #777;border-left:4px solid #111;padding:9px 11px;background:#fafafa}.task-exercise header{flex-wrap:wrap}.exercise-based-on{margin-left:auto;font-size:8px;color:#777}.exercise-evidence{border-top:1px dashed #bbb;margin-top:8px;padding-top:6px}.exercise-evidence>strong{font-size:11px;text-transform:uppercase}.exercise-criterion{font-size:13px;margin:7px 0 0}.task-flow>footer{padding:8px 11px;border-top:1px solid #aaa;font-size:12px}.we-block{border-top:1px solid #ddd;padding:8px 0}.we-block summary{cursor:pointer}.dictionary-sheet .pdf-main{grid-column:2}.dictionary-sheet .pdf-margin{display:none} +code{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace}.tasks{font-size:16px}.task-legacy{margin:2.5mm 0}.task-legacy>strong{display:block;font:700 10px/1.2 "Latin Modern Mono",ui-monospace,monospace;color:#555}.task-flow{border:1px solid #aaa;margin:2mm 0 4mm}.task-flow-header{display:grid;grid-template-columns:auto 1fr auto;align-items:baseline;gap:10px;padding:8px 11px;background:#f3f3f3;border-bottom:1px solid #aaa}.task-flow-header span{font:700 10px/1.2 "Latin Modern Mono",ui-monospace,monospace;color:#555}.task-flow-header h3{font-size:18px;margin:0}.task-flow-header code{font-size:8px;color:#777}.task-block,.task-exercise{margin:10px 12px;padding:0 0 10px;border-bottom:1px solid #ddd}.task-block header,.task-exercise header{display:flex;align-items:baseline;gap:8px;margin-bottom:6px}.task-block header span,.task-exercise header span{font:700 9px/1.2 "Latin Modern Mono",ui-monospace,monospace;text-transform:uppercase;color:#666}.task-block h4,.task-exercise h4{font-size:16px;margin:0}.block-steps{counter-reset:block-step;margin:5px 0 0 24px!important}.block-steps li{margin:7px 0}.block-steps li::marker{font-family:"Latin Modern Mono",ui-monospace,monospace}.task-exercise{border:1px solid #777;border-left:4px solid #111;padding:9px 11px;background:#fafafa}.task-exercise header{flex-wrap:wrap}.exercise-based-on{margin-left:auto;font-size:8px;color:#777}.exercise-evidence{border-top:1px dashed #bbb;margin-top:8px;padding-top:6px}.exercise-evidence>strong{font-size:11px;text-transform:uppercase}.exercise-criterion{font-size:13px;margin:7px 0 0}.task-flow>footer{padding:8px 11px;border-top:1px solid #aaa;font-size:12px}.we-block{border-top:1px solid #ddd;padding:8px 0}.we-block summary{cursor:pointer}.dictionary-sheet .pdf-margin{display:none} .inspector{position:fixed;right:14px;bottom:14px;width:min(360px,calc(100vw - 28px));max-height:46vh;overflow:auto;background:white;border:1px solid #cfcfcf;box-shadow:0 4px 18px rgba(0,0,0,.18);border-radius:6px;padding:12px;font-family:system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;z-index:30}.inspector h2{font-size:14px;margin:0 0 8px}.inspector .empty{color:#777}.tree-card h3{font-size:15px;margin:6px 0}.tree-card code{display:inline-block;margin:4px 0}.tree-card .line{font-weight:700}.tree-card .line.OG{color:var(--og)}.tree-card .line.TECH{color:var(--tech)}.kw-list{padding-left:18px}.kw-list code{color:var(--kw)} @media(max-width:900px){.sheet{display:block;min-height:0;padding:26px 18px}.pdf-margin{position:static;max-height:none;overflow:visible;margin:10px 0;padding:8px;border:1px solid #ddd}.pdf-margin.left{border-left:3px solid var(--tech)}.pdf-margin.right{border-left:3px solid var(--og)}.hero .byline{float:none;margin:0 0 14px}.meta{grid-template-columns:1fr}.section-heading{flex-wrap:wrap}.task-identity{width:100%;padding:6px 0 0}.inspector{position:static;width:auto;max-height:none;margin:0 12px 18px}.topbar{position:static}} -@media print{html,body{background:white}.topbar,.inspector{display:none}.paper{padding:0}.sheet{display:grid;grid-template-columns:22mm minmax(0,1fr) 22mm;column-gap:4mm;width:auto;min-height:0;margin:0;box-shadow:none;break-after:page;padding:0;row-gap:6mm}.sheet:last-child{break-after:auto}.resource-header-enabled .sheet{padding:0;row-gap:6mm}.sheet>.page-resource-header{display:none!important}.pdf-margin{position:static;max-height:none;overflow:hidden;margin:0;padding:0;border:0}.task-block,.task-exercise,figure{break-inside:avoid}} +@media print{html,body{background:white}.topbar,.inspector{display:none}.paper{padding:0}.sheet{position:relative;display:block;width:auto;min-height:0;margin:0;box-shadow:none;break-after:page;padding:0}.sheet:last-child{break-after:auto}.resource-header-enabled .sheet{padding:0}.sheet>.page-resource-header,.sheet>.page-resource-footer{display:none!important}.pdf-margin{position:absolute;top:0;width:18mm;max-height:none;overflow:hidden;margin:0;padding:0;border:0}.pdf-margin.left{left:-19.5mm}.pdf-margin.right{right:-19.5mm}.task-block,.task-exercise,figure{break-inside:avoid}} """ - css += page_resource_header_css() + css += page_resource_header_css(data) if use_resource_header: css += page_resource_print_css(data) js = r""" @@ -2974,9 +2988,10 @@ document.addEventListener('click', event => { {front_intro_html} + {render_page_footer_html(data, 1, total_pages) if use_resource_header else ''} {''.join(sections_html)} -
{render_page_header_html(data, dictionary_page, total_pages) if use_resource_header else ''}
{render_running_header_html(card) if not use_resource_header else ''}

Słownik WE/EN/EK/KW

{render_learning_tree_overview_html(data)}
+
{render_page_header_html(data, dictionary_page, total_pages) if use_resource_header else ''}
{render_running_header_html(card) if not use_resource_header else ''}

Słownik WE/EN/EK/KW

{render_learning_tree_overview_html(data)}
{render_page_footer_html(data, dictionary_page, total_pages) if use_resource_header else ''}
{inspector_html} @@ -3084,6 +3099,12 @@ def title_block_fields(data: dict) -> dict[str, str | bool]: } +def resource_header_height_mm(data: dict) -> float: + """Return the configured repeated-header height in millimetres.""" + configured = data.get("title_block") or {} + return float(configured.get("height_cm", 3)) * 10 + + @lru_cache(maxsize=32) def qr_svg(value: str) -> str: """Generate a self-contained QR SVG without a network dependency. @@ -3181,7 +3202,7 @@ def qr_svg_from_tex(value: str) -> str: def render_page_header_html(data: dict, page: int, total: int) -> str: - """Render the canonical 30 mm resource header used by every page wrapper.""" + """Render the configured resource header used by every page wrapper.""" card = data["card"] fields = title_block_fields(data) url = str(fields["url"]) @@ -3212,62 +3233,104 @@ def render_page_header_html(data: dict, page: int, total: int) -> str: else "" ) - def datum(label: str, value: object, class_name: str = "") -> str: - suffix = f" {class_name}" if class_name else "" - return ( - f'
{html_escape(label)}' - f'{html_escape(value or "—")}
' - ) - card_number = f"{card.get('number', '')}/{card.get('count', '')}" sheet_number = f"{page}/{total}" header_class = "page-resource-header page-resource-header--dual" if repository_qr_html else "page-resource-header" - source_link = ( - f'' - f'GITEA{html_escape(source_url)}' - if source_url - else "" + + def inline_fields(items: list[tuple[str, object]], strong_first: bool = False) -> str: + rendered: list[str] = [] + for index, (label, value) in enumerate(items): + if index: + rendered.append('|') + rendered.append(f'{html_escape(label)}') + tag = "strong" if strong_first and index == 0 else "span" + rendered.append(f'<{tag} class="resource-value">{html_escape(value or "—")}') + return '
' + "".join(rendered) + "
" + + primary_rows = [ + inline_fields( + [ + (fields["category"], f"{card_prefix(card)}: {card.get('title', '')}"), + ], + strong_first=True, + ), + inline_fields( + [ + ("Projekt", card.get("project", "")), + ("Seria", card.get("series_title", card.get("series", ""))), + ] + ), + inline_fields( + [ + ("Przedmiot", card.get("subject", "")), + ("Poziom", card.get("level", "")), + ] + ), + inline_fields([("HTML", url or "brak URL")]), + ] + meta_rows = [ + inline_fields( + [("Data", card.get("revision_date", "")), ("Status", card.get("status", ""))] + ), + inline_fields([("Karta", card_number), ("Wersja", card.get("version", ""))]), + inline_fields([("Autor", card.get("author", "")), ("Arkusz", sheet_number)]), + inline_fields([("UUID", card.get("uuid", ""))]), + ] + column_group = ( + '' + if repository_qr_html + else '' ) + rows: list[str] = [] + for index, (primary, meta) in enumerate(zip(primary_rows, meta_rows)): + cells: list[str] = [] + if index == 0 and repository_qr_html: + cells.append(f'{repository_qr_html}') + cells.append(f'{primary}') + cells.append(f'{meta}') + if index == 0: + cells.append(f'{page_qr_html}') + rows.append("" + "".join(cells) + "") return ( f'
' - f'{repository_qr_html}' - '
' - f'
{html_escape(fields["category"])}' - f'{html_escape(card_prefix(card))}: {html_escape(card.get("title", ""))}
' - '
' - f'{datum("Projekt", card.get("project", ""))}' - f'{datum("Seria", card.get("series_title", card.get("series", "")))}' - f'{datum("Przedmiot", card.get("subject", ""))}' - f'{datum("Poziom", card.get("level", ""), "resource-level")}' - '
' - '
' - f'{source_link}' - f'HTML{html_escape(url or "brak URL")}' - '
' - '
' - '
' - f'{datum("Data", card.get("revision_date", ""))}' - f'{datum("Status", card.get("status", ""))}' - f'{datum("Karta", card_number)}' - f'{datum("Wersja", card.get("version", ""))}' - f'{datum("Autor", card.get("author", ""))}' - f'{datum("Arkusz", sheet_number)}' - f'{datum("UUID", card.get("uuid", ""), "resource-uuid")}' - '
' - f'{page_qr_html}' + f'{column_group}' + f'{"".join(rows)}
' '
' ) -def page_resource_header_css() -> str: +def render_page_footer_html(data: dict, page: int, total: int) -> str: + """Render the compact per-sheet footer used by the HTML A4 preview.""" + card = data["card"] + title_block = data.get("title_block") or {} + doc_uuid = str(title_block.get("doc_uuid", "")) + source_url = repository_url(data) + card_number = f"{card.get('number', '')}/{card.get('count', '')}" + return ( + '
' + '' + f'' + f'' + f'' + f'' + '' + f'' + f'' + f'' + '
KARTA {html_escape(card_number)}UUID {html_escape(card.get("uuid", ""))}DOKUMENT {html_escape(doc_uuid)}
WERSJA {html_escape(card.get("version", ""))} · {html_escape(card.get("revision_date", ""))}GITEA {html_escape(source_url)}
' + ) + + +def page_resource_header_css(data: dict) -> str: return r""" -.page-resource-header{grid-column:1/-1;width:100%;height:30mm;min-height:30mm;max-height:30mm;border:1px solid #1c1c1a;display:grid;grid-template-columns:minmax(0,1fr) 47mm 24mm;background:#fff;color:#111;overflow:hidden;font-family:system-ui,-apple-system,"Segoe UI",sans-serif;line-height:1.12}.page-resource-header--dual{grid-template-columns:24mm minmax(0,1fr) 39mm 24mm} -.resource-qr{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.35mm;border-right:1px solid #aaa;padding:.65mm .8mm;color:#555;text-decoration:none;font-size:7px}.resource-qr-page{border-right:0}.resource-qr .title-qr{display:block;width:22mm;height:22mm;max-width:22mm;max-height:22mm;flex:none}.resource-qr-label{font:700 5px/1 ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.08em;color:#444}.resource-qr-empty{text-align:center}.resource-qr-empty span,.resource-qr-empty strong{display:block;font:600 5.5px/1.15 ui-monospace,SFMono-Regular,Consolas,monospace} -.resource-primary{min-width:0;display:flex;flex-direction:column;justify-content:center;padding:1mm;border-right:1px solid #aaa;font:600 5.3px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace}.resource-title{display:flex;flex-direction:column;min-width:0}.resource-title span,.resource-title strong{font:inherit;color:#111;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.resource-details{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.72fr);column-gap:1mm;min-width:0}.resource-datum{display:flex;gap:.7mm;align-items:baseline;min-width:0}.resource-datum span,.resource-datum strong{font:inherit;color:#111;white-space:nowrap}.resource-datum span{text-transform:uppercase}.resource-datum strong{overflow:hidden;text-overflow:ellipsis}.resource-details .resource-datum:nth-child(2)::before,.resource-meta .resource-datum:nth-child(2)::before,.resource-meta .resource-datum:nth-child(4)::before{content:"·"}.resource-details .resource-datum:nth-child(3),.resource-level{grid-column:1/-1}.resource-urls{min-width:0}.resource-url{display:block;font:inherit;color:#111;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.resource-url span{display:inline-block;width:8mm;font-weight:inherit}.resource-meta{padding:1mm;border-right:1px solid #aaa;display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:.7mm;align-content:center;min-width:0;font:600 5.3px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace}.resource-meta .resource-datum{font:inherit}.resource-meta .resource-datum:nth-child(n+5){grid-column:1/-1}.resource-meta .resource-uuid strong{font:inherit;overflow:visible;text-overflow:clip}.resource-repository-url{min-width:0} -""" +.page-resource-header{width:100%;height:__RESOURCE_HEADER_HEIGHT__;min-height:__RESOURCE_HEADER_HEIGHT__;max-height:__RESOURCE_HEADER_HEIGHT__;background:#fff;color:#111;overflow:hidden;font-family:ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace;line-height:1.08} +.resource-header-table{width:100%;height:100%;border-collapse:collapse;table-layout:fixed;border:1px solid #111;font:600 5pt/1.05 ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace}.resource-header-table tr{height:25%}.resource-header-cell{padding:.25mm .65mm;border-right:1px solid #777;border-bottom:1px solid #aaa;vertical-align:middle;overflow:hidden}.resource-header-table tr:last-child .resource-header-cell{border-bottom:0}.resource-header-cell--meta{border-right:1px solid #777}.resource-inline{display:flex;align-items:baseline;gap:.55mm;min-width:0;white-space:nowrap;overflow:hidden}.resource-label{font-weight:700;text-transform:uppercase;flex:none}.resource-value{font:inherit;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.resource-header-table tr:first-child .resource-header-cell--primary .resource-value{font-weight:800;font-size:6pt}.resource-separator{color:#999;flex:none} +.resource-qr-cell{padding:0;vertical-align:middle;overflow:hidden}.resource-qr-cell--left{border-right:1px solid #777}.resource-qr-cell--right{border-left:1px solid #777}.resource-qr{box-sizing:border-box;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.25mm;padding:.45mm;color:#333;text-decoration:none}.resource-qr .title-qr{display:block;width:22mm;height:22mm;max-width:22mm;max-height:22mm;flex:none}.resource-qr-label{font:700 4.5pt/1 ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace;letter-spacing:.08em;color:#333}.resource-qr-empty{text-align:center}.resource-qr-empty span,.resource-qr-empty strong{display:block;font:600 5pt/1.15 ui-monospace,SFMono-Regular,Consolas,monospace} +.page-resource-footer{position:absolute;left:21.5mm;right:21.5mm;bottom:5mm;border-top:1px solid #111;padding-top:.6mm;font:500 5pt/1.1 ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace;color:#222;overflow:hidden}.page-resource-footer table{width:100%;border-collapse:collapse;table-layout:fixed;font:inherit}.page-resource-footer td{width:25%;padding:.25mm .6mm;border:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.page-resource-footer td+td{border-left:1px solid #ddd}.page-resource-footer .footer-right{text-align:right} +""".replace("__RESOURCE_HEADER_HEIGHT__", f"{resource_header_height_mm(data):g}mm") def css_paged_string(value: object) -> str: @@ -3281,9 +3344,11 @@ def css_paged_string(value: object) -> str: def page_resource_print_css(data: dict) -> str: - """Render a real 30 mm repeated header in CSS paged-media margin boxes.""" + """Render the repeated header in CSS paged-media margin boxes.""" card = data["card"] fields = title_block_fields(data) + header_height_mm = resource_header_height_mm(data) + page_top_margin_mm = header_height_mm + 5 url = str(fields["url"]) source_url = str(fields["repository_url"]) @@ -3297,6 +3362,16 @@ def page_resource_print_css(data: dict) -> str: page_qr_background = qr_background(url, bool(fields["show_qr"])) repository_qr_enabled = bool(fields["show_repository_qr"]) repository_qr_background = qr_background(source_url, repository_qr_enabled) + center_grid_svg = ( + '' + '' + '' + '' + ) + center_grid_data = base64.b64encode(center_grid_svg.encode("utf-8")).decode("ascii") + center_grid_background = f'url("data:image/svg+xml;base64,{center_grid_data}")' middle = "\n".join( [ f"{fields['category']} | {card_prefix(card)}: {card.get('title', '')}", @@ -3320,7 +3395,7 @@ def page_resource_print_css(data: dict) -> str: ] ) qr_box = ( - "height:30mm;box-sizing:border-box;padding:24mm .8mm .5mm;" + f"height:{header_height_mm:g}mm;box-sizing:border-box;padding:24mm .8mm .5mm;" "border:1px solid #111;background-color:#fff;background-repeat:no-repeat;" "background-position:center 1mm;background-size:22mm 22mm;" "vertical-align:middle;text-align:center;" @@ -3336,55 +3411,60 @@ def page_resource_print_css(data: dict) -> str: center_before_page = "\n".join( [ header_row( - fields["category"], - f"DATA {card.get('revision_date', '')} · STATUS {card.get('status', '')}", + f"{fields['category']} | {card_prefix(card)}: {card.get('title', '')}", + f"DATA {card.get('revision_date', '')} | STATUS {card.get('status', '')}", ), header_row( - f"{card_prefix(card)}: {card.get('title', '')}", - f"KARTA {card.get('number', '')}/{card.get('count', '')} · WERSJA {card.get('version', '')}", + f"PROJEKT {card.get('project', '')} | SERIA {card.get('series_title', card.get('series', ''))}", + f"KARTA {card.get('number', '')}/{card.get('count', '')} | WERSJA {card.get('version', '')}", ), header_row( - f"PROJEKT {card.get('project', '')} · SERIA {card.get('series_title', card.get('series', ''))}", - f"AUTOR {card.get('author', '')}", + f"PRZEDMIOT {card.get('subject', '')} | POZIOM {card.get('level', '')}", + f"AUTOR {card.get('author', '')} | ARKUSZ ", ), - header_row(f"PRZEDMIOT {card.get('subject', '')}", "ARKUSZ "), ] ) - center_after_page = "\n" + "\n".join( - [ - header_row( - f"POZIOM {card.get('level', '')}", - f"UUID {card.get('uuid', '')}", - ), - header_row(f"GITEA {source_url}"), - header_row(f"HTML {url}"), - ] + center_after_page = "\n" + header_row( + f"HTML {url}", + f"UUID {card.get('uuid', '')}", ) + footer_left = ( + f"KARTA {card.get('number', '')}/{card.get('count', '')}\n" + f"WERSJA {card.get('version', '')} · {card.get('revision_date', '')}" + ) + footer_center = f"UUID {card.get('uuid', '')}\nGITEA {source_url}" + footer_right_before_page = "ARKUSZ " return f""" @page{{ size:A4; - margin:35mm 21.5mm 15mm; + margin:{page_top_margin_mm:g}mm 21.5mm 15mm; @top-left{{content:"GITEA";width:24mm;{qr_box};background-image:{repository_qr_background}}} - @top-center{{content:"{css_paged_string(center_before_page)}" counter(page) "/" counter(pages) "{css_paged_string(center_after_page)}";width:119mm;height:30mm;box-sizing:border-box;padding:1mm;border-block:1px solid #111;background-color:#fff;background-image:linear-gradient(#aaa,#aaa);background-repeat:no-repeat;background-position:80mm 0;background-size:1px 100%;white-space:pre;vertical-align:middle;text-align:left;font:600 5.3px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace;color:#111;overflow:hidden}} + @top-center{{content:"{css_paged_string(center_before_page)}" counter(page) "/" counter(pages) "{css_paged_string(center_after_page)}";width:119mm;height:{header_height_mm:g}mm;box-sizing:border-box;padding:1mm;border-block:1px solid #111;background-color:#fff;background-image:{center_grid_background};background-repeat:no-repeat;background-position:center;background-size:100% 100%;white-space:pre;vertical-align:middle;text-align:left;font:600 5.3px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace;color:#111;overflow:hidden}} @top-right{{content:"HTML";width:24mm;{qr_box};background-image:{page_qr_background}}} + @bottom-left{{content:"{css_paged_string(footer_left)}";width:45mm;height:7mm;box-sizing:border-box;padding:.7mm 1mm 0;border-top:1px solid #111;white-space:pre;text-align:left;font:500 4.8px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace}} + @bottom-center{{content:"{css_paged_string(footer_center)}";width:90mm;height:7mm;box-sizing:border-box;padding:.7mm 1mm 0;border-top:1px solid #111;white-space:pre;text-align:left;font:500 4.8px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace;overflow:hidden}} + @bottom-right{{content:"{css_paged_string(footer_right_before_page)}" counter(page) "/" counter(pages) "{css_paged_string(chr(10) + 'AUTOR ' + str(card.get('author', '')))}";width:32mm;height:7mm;box-sizing:border-box;padding:.7mm 1mm 0;border-top:1px solid #111;white-space:pre;text-align:right;font:500 4.8px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace}} }} """ return f""" @page{{ size:A4; - margin:35mm 21.5mm 15mm; - @top-left{{content:"{css_paged_string(meta_before_page)}" counter(page) "/" counter(pages) "{css_paged_string(meta_after_page)}";width:47mm;height:30mm;box-sizing:border-box;padding:1.5mm 1.7mm;border:1px solid #111;white-space:pre-wrap;vertical-align:middle;text-align:left;font:600 5.7px/1.3 ui-monospace,SFMono-Regular,Consolas,monospace;color:#111}} - @top-center{{content:"{css_paged_string(middle)}";width:96mm;height:30mm;box-sizing:border-box;padding:1.5mm 2mm;border-block:1px solid #111;white-space:pre-wrap;vertical-align:middle;text-align:left;font:600 6.2px/1.25 ui-monospace,SFMono-Regular,Consolas,monospace;color:#111}} + margin:{page_top_margin_mm:g}mm 21.5mm 15mm; + @top-left{{content:"{css_paged_string(meta_before_page)}" counter(page) "/" counter(pages) "{css_paged_string(meta_after_page)}";width:47mm;height:{header_height_mm:g}mm;box-sizing:border-box;padding:1.5mm 1.7mm;border:1px solid #111;white-space:pre-wrap;vertical-align:middle;text-align:left;font:600 5.7px/1.3 ui-monospace,SFMono-Regular,Consolas,monospace;color:#111}} + @top-center{{content:"{css_paged_string(middle)}";width:96mm;height:{header_height_mm:g}mm;box-sizing:border-box;padding:1.5mm 2mm;border-block:1px solid #111;white-space:pre-wrap;vertical-align:middle;text-align:left;font:600 6.2px/1.25 ui-monospace,SFMono-Regular,Consolas,monospace;color:#111}} @top-right{{content:"HTML";width:24mm;{qr_box};background-image:{page_qr_background}}} }} """ def render_page_header_tex_macro(data: dict) -> list[str]: - """Define the repeated 30 mm TeX header for the classic emitter.""" + """Define the repeated TeX header for the classic emitter.""" card = data["card"] fields = title_block_fields(data) + header_height_mm = resource_header_height_mm(data) + outer_height_mm = header_height_mm - 0.5 + inner_height_mm = header_height_mm - 1 url = str(fields["url"]) source_url = str(fields["repository_url"]) dual_qr = bool(fields["show_repository_qr"]) @@ -3407,14 +3487,43 @@ def render_page_header_tex_macro(data: dict) -> list[str]: card_number = f"{card.get('number', '')}/{card.get('count', '')}" primary_width = "76mm" if dual_qr else "89mm" meta_width = "39mm" if dual_qr else "47mm" - repository_meta_line = ( - rf" {{\fontsize{{3.8}}{{4.3}}\selectfont\ttfamily GITEA: \href{{{source_url}}}{{\nolinkurl{{{source_url}}}}}}}%" - if source_url - else "" - ) + row_height_mm = (inner_height_mm - 0.5) / 4 + + def header_rows_tex(rows: list[str]) -> list[str]: + output: list[str] = [] + for index, row in enumerate(rows): + output.append( + " \\parbox[c][" + + f"{row_height_mm:g}mm" + + "][c]{\\linewidth}{\\hspace{0.65mm}" + + row + + "\\hspace{0.65mm}}%" + ) + if index != len(rows) - 1: + output.extend( + [ + r" \par\nointerlineskip%", + r" \hrule height0.35pt%", + r" \nointerlineskip%", + ] + ) + return output + + primary_rows = [ + rf"{{\fontsize{{5.0}}{{5.4}}\selectfont\ttfamily {tex_escape(fields['category'])} | \textbf{{{tex_escape(title)}}}}}", + rf"{{\fontsize{{4.8}}{{5.2}}\selectfont\ttfamily \textbf{{PROJEKT}} {tex_escape(card.get('project', ''))} | \textbf{{SERIA}} {tex_escape(card.get('series_title', card.get('series', '')))}}}", + rf"{{\fontsize{{4.5}}{{5.0}}\selectfont\ttfamily \textbf{{PRZEDMIOT}} {tex_escape(card.get('subject', ''))} | \textbf{{POZIOM}} {tex_escape(card.get('level', ''))}}}", + rf"{{\fontsize{{3.8}}{{4.2}}\selectfont\ttfamily \textbf{{HTML}} \href{{{url}}}{{\nolinkurl{{{url}}}}}}}", + ] + meta_rows = [ + rf"{{\fontsize{{4.6}}{{5.0}}\selectfont\ttfamily \textbf{{DATA}} {tex_escape(card.get('revision_date', ''))} | \textbf{{STATUS}} {tex_escape(card.get('status', ''))}}}", + rf"{{\fontsize{{4.6}}{{5.0}}\selectfont\ttfamily \textbf{{KARTA}} {tex_escape(card_number)} | \textbf{{WERSJA}} {tex_escape(card.get('version', ''))}}}", + rf"{{\fontsize{{4.6}}{{5.0}}\selectfont\ttfamily \textbf{{AUTOR}} {tex_escape(card.get('author', ''))} | \textbf{{ARKUSZ}} \thepage/\pageref{{LastPage}}}}", + rf"{{\fontsize{{3.8}}{{4.2}}\selectfont\ttfamily \textbf{{UUID}} {tex_escape(card.get('uuid', ''))}}}", + ] source_cell = ( [ - r" \begin{minipage}[c][29mm][c]{24mm}\centering%", + rf" \begin{{minipage}}[c][{inner_height_mm:g}mm][c]{{24mm}}\centering%", " " + source_qr + r"\par%", r" \vspace{0.2mm}{\fontsize{4.5}{4.8}\selectfont\bfseries\ttfamily GITEA}%", r" \end{minipage}%", @@ -3430,29 +3539,17 @@ def render_page_header_tex_macro(data: dict) -> list[str]: r" \setlength{\fboxsep}{0pt}%", r" \setlength{\fboxrule}{0.35pt}%", r" \noindent\fbox{%", - r" \begin{minipage}[c][29.5mm][c]{\dimexpr\textwidth-2\fboxrule\relax}%", + rf" \begin{{minipage}}[c][{outer_height_mm:g}mm][c]{{\dimexpr\textwidth-2\fboxrule\relax}}%", *source_cell, - rf" \begin{{minipage}}[c][29mm][c]{{{primary_width}}}%", - rf" {{\fontsize{{5.5}}{{6.2}}\selectfont\ttfamily\textcolor{{black!58}}{{{tex_escape(fields['category'])}}}}}\par%", - rf" {{\fontsize{{9.2}}{{10.1}}\selectfont\bfseries {tex_escape(title)}}}\par%", - r" \vspace{0.1mm}%", - rf" {{\fontsize{{5.6}}{{6.3}}\selectfont Projekt: \texttt{{{tex_escape(card.get('project', ''))}}}\quad Seria: \texttt{{{tex_escape(card.get('series_title', card.get('series', '')))}}}}}\par%", - rf" {{\fontsize{{5.6}}{{6.3}}\selectfont Przedmiot: \texttt{{{tex_escape(card.get('subject', ''))}}}}}\par%", - rf" {{\fontsize{{5.0}}{{5.7}}\selectfont Poziom: \texttt{{{tex_escape(card.get('level', ''))}}}}}\par%", - rf" {{\fontsize{{4.2}}{{4.8}}\selectfont\ttfamily HTML: \href{{{url}}}{{\nolinkurl{{{url}}}}}}}%", + rf" \begin{{minipage}}[c][{inner_height_mm:g}mm][c]{{{primary_width}}}%", + *header_rows_tex(primary_rows), r" \end{minipage}%", r" \hfill\vrule width0.35pt\hspace{1mm}%", - rf" \begin{{minipage}}[c][29mm][c]{{{meta_width}}}%", - rf" {{\fontsize{{5.7}}{{6.5}}\selectfont Data: \texttt{{{tex_escape(card.get('revision_date', ''))}}}\hfill Status: \texttt{{{tex_escape(card.get('status', ''))}}}}}\par%", - rf" {{\fontsize{{5.7}}{{6.5}}\selectfont Karta: \texttt{{{tex_escape(card_number)}}}\hfill Wersja: \texttt{{{tex_escape(card.get('version', ''))}}}}}\par%", - rf" {{\fontsize{{5.7}}{{6.5}}\selectfont Autor: \texttt{{{tex_escape(card.get('author', ''))}}}}}\par%", - r" {\fontsize{5.7}{6.5}\selectfont Arkusz: \texttt{\thepage/\pageref{LastPage}}}\par%", - r" \vspace{0.5mm}\hrule\vspace{0.5mm}%", - rf" {{\fontsize{{4.7}}{{5.3}}\selectfont\ttfamily UUID: {tex_escape(card.get('uuid', ''))}}}\par%", - repository_meta_line, + rf" \begin{{minipage}}[c][{inner_height_mm:g}mm][c]{{{meta_width}}}%", + *header_rows_tex(meta_rows), r" \end{minipage}%", r" \hfill\vrule width0.35pt\hspace{1mm}%", - r" \begin{minipage}[c][29mm][c]{24mm}\centering%", + rf" \begin{{minipage}}[c][{inner_height_mm:g}mm][c]{{24mm}}\centering%", " " + page_qr + r"\par%", r" \vspace{0.2mm}{\fontsize{4.5}{4.8}\selectfont\bfseries\ttfamily HTML}%", r" \end{minipage}%", @@ -4333,7 +4430,7 @@ body{margin:0;background:#eceae4;display:flex;flex-direction:column;align-items: .page-footer{display:flex;justify-content:space-between;border-top:1.5px solid #1c1c1a;padding-top:8px;margin-top:14px;font:400 9.5px 'JetBrains Mono',monospace;color:#8a887f;gap:16px} @media print{@page{size:A4 landscape;margin:0}body{background:#fff;padding:0;gap:0}.page{box-shadow:none!important;border:none!important;break-after:page}} """ - css += page_resource_header_css() + css += page_resource_header_css(data) card = data["card"] return f""" @@ -4514,7 +4611,7 @@ def render_5b_document_page_html(data: dict, page_data: dict, page: int, total: return render_5b_page_html(data, center, page, total) -def render_main_html_5b_css(template: dict | None = None) -> str: +def render_main_html_5b_css(data: dict, template: dict | None = None) -> str: css = r""" *{-webkit-print-color-adjust:exact;print-color-adjust:exact;box-sizing:border-box} body{margin:0;background:#eceae4;display:flex;flex-direction:column;align-items:center;gap:24px;padding:24px} @@ -4547,7 +4644,7 @@ body{margin:0;background:#eceae4;display:flex;flex-direction:column;align-items: .page-footer{position:absolute;left:var(--page-rule-left);bottom:95px;width:var(--page-rule-width);display:flex;justify-content:space-between;border-top:0;padding-top:6px;margin-top:0;font:400 8.2px 'JetBrains Mono',monospace;color:#8a887f;gap:10px}.page-footer::before{content:"";position:absolute;left:0;right:0;top:0;border-top:1.4px solid #1c1c1a} @media print{@page{size:A4 portrait;margin:0}body{background:#fff;padding:0;gap:0}.page{box-shadow:none!important;border:none!important;break-after:page}} """ - return css + page_resource_header_css() + return css + page_resource_header_css(data) def render_main_html_5b(data: dict, template: dict) -> str: @@ -5740,7 +5837,7 @@ def render_html_output(data: dict, template: dict) -> str: def render_html_css_output(data: dict, template: dict) -> str: if template_emitter(template, "html") == "5b": - return render_main_html_5b_css(template) + return render_main_html_5b_css(data, template) return ""