fix: match classic HTML header to PDF geometry

This commit is contained in:
user
2026-07-16 08:03:49 +02:00
parent 01a5f18414
commit 28a6715280
2 changed files with 44 additions and 30 deletions
+3 -3
View File
@@ -29,9 +29,9 @@ kontynuacji. Tabliczka zastępuje dużą tabelę metadanych strony tytułowej.
Zawiera kategorię, tytuł, metadane karty, numer strony oraz widoczny, Zawiera kategorię, tytuł, metadane karty, numer strony oraz widoczny,
kanoniczny URL. Ten sam URL jest kodowany lokalnie w QR: jako SVG w HTML i 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 przez pakiet `qrcode` w TeX. Opcjonalny `repository_url` dodaje drugi QR do
repozytorium. Przy dwóch kodach komórki mają po 27 mm: Gitea znajduje się po repozytorium. W layoutach klasycznym HTML i PDF komórki QR mają po 24 mm,
lewej, a wyrenderowany zasób po prawej. Jedna zamknięta ramka biegnie dokładnie a kody po 22 mm: Gitea znajduje się po lewej, a wyrenderowany zasób po prawej.
między marginesami strony. W HTML widok ekranowy ma rozmiar A4 (210 × 297 mm) i Ramka ma 167 mm i biegnie między marginesami bocznymi po 21,5 mm. W HTML widok ekranowy ma rozmiar A4 (210 × 297 mm) i
nagłówek na każdej logicznej karcie, a wydruk używa `@page { size: A4; }` oraz nagłówek na każdej logicznej karcie, a wydruk używa `@page { size: A4; }` oraz
pól marginesu, dlatego nagłówek powtarza się również po automatycznym podziale pól marginesu, dlatego nagłówek powtarza się również po automatycznym podziale
długiej sekcji. długiej sekcji.
+41 -27
View File
@@ -2885,7 +2885,7 @@ def render_main_html(data: dict) -> str:
*{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:Georgia,"Times New Roman",serif;line-height:1.42}
.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{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} .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 9mm 11mm}.resource-header-enabled .sheet{padding-top:9mm;row-gap:6mm} .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:9mm;row-gap:6mm}
.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} .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}
.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} .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} .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}
@@ -3223,7 +3223,7 @@ def render_page_header_html(data: dict, page: int, total: int) -> str:
sheet_number = f"{page}/{total}" sheet_number = f"{page}/{total}"
header_class = "page-resource-header page-resource-header--dual" if repository_qr_html else "page-resource-header" header_class = "page-resource-header page-resource-header--dual" if repository_qr_html else "page-resource-header"
source_link = ( source_link = (
f'<a class="resource-url" href="{html_escape(source_url)}">' f'<a class="resource-url resource-repository-url" href="{html_escape(source_url)}">'
f'<span>GITEA</span>{html_escape(source_url)}</a>' f'<span>GITEA</span>{html_escape(source_url)}</a>'
if source_url if source_url
else "" else ""
@@ -3244,7 +3244,6 @@ def render_page_header_html(data: dict, page: int, total: int) -> str:
f'{datum("Poziom", card.get("level", ""), "resource-level")}' f'{datum("Poziom", card.get("level", ""), "resource-level")}'
'</div>' '</div>'
'<div class="resource-urls">' '<div class="resource-urls">'
f'{source_link}'
f'<a class="resource-url" href="{html_escape(url)}"><span>HTML</span>{html_escape(url or "brak URL")}</a>' f'<a class="resource-url" href="{html_escape(url)}"><span>HTML</span>{html_escape(url or "brak URL")}</a>'
'</div>' '</div>'
'</div>' '</div>'
@@ -3256,6 +3255,7 @@ def render_page_header_html(data: dict, page: int, total: int) -> str:
f'{datum("Arkusz", sheet_number)}' f'{datum("Arkusz", sheet_number)}'
f'{datum("Autor", card.get("author", ""))}' f'{datum("Autor", card.get("author", ""))}'
f'{datum("UUID", card.get("uuid", ""), "resource-uuid")}' f'{datum("UUID", card.get("uuid", ""), "resource-uuid")}'
f'{source_link}'
'</div>' '</div>'
f'{page_qr_html}' f'{page_qr_html}'
'</header>' '</header>'
@@ -3264,11 +3264,9 @@ def render_page_header_html(data: dict, page: int, total: int) -> str:
def page_resource_header_css() -> str: def page_resource_header_css() -> str:
return r""" 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) 49mm 27mm;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:27mm minmax(0,1fr) 49mm 27mm} .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:24mm;height:24mm;max-width:24mm;max-height:24mm;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-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:grid;grid-template-rows:auto 1fr auto;padding:1.4mm 2mm;border-right:1px solid #aaa}.resource-title{display:flex;align-items:baseline;justify-content:space-between;gap:3mm;border-bottom:1px solid #ddd;padding-bottom:.8mm;min-width:0}.resource-title span{font:600 6.5px/1.1 ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.06em;color:#555;white-space:nowrap}.resource-title strong{font:700 12px/1.1 Georgia,"Times New Roman",serif;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right} .resource-primary{min-width:0;display:grid;grid-template-rows:auto 1fr auto;padding:.9mm 1.5mm;border-right:1px solid #aaa}.resource-title{display:grid;grid-template-rows:auto auto;border-bottom:1px solid #ddd;padding-bottom:.45mm;min-width:0}.resource-title span{font:600 5.2px/1.05 ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.06em;color:#555;white-space:nowrap}.resource-title strong{font:700 10px/1.05 Georgia,"Times New Roman",serif;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}.resource-details{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-content:center;column-gap:2mm;row-gap:.3mm;min-width:0}.resource-datum{display:grid;grid-template-columns:auto minmax(0,1fr);gap:.8mm;align-items:baseline;min-width:0}.resource-datum span{font:500 5.5px/1.05 ui-monospace,SFMono-Regular,Consolas,monospace;text-transform:uppercase;color:#666;white-space:nowrap}.resource-datum strong{font:600 6.8px/1.08 ui-monospace,SFMono-Regular,Consolas,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.resource-level{grid-column:1/-1}.resource-url{font:500 5.2px/1.05 ui-monospace,SFMono-Regular,Consolas,monospace;color:#222;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-top:1px solid #eee;padding-top:.4mm}.resource-urls{min-width:0}.resource-url{display:block}.resource-url span{display:inline-block;width:8mm;font-weight:700;color:#666}.resource-meta{padding:.8mm 1.1mm;border-right:1px solid #aaa;display:grid;grid-template-columns:1fr 1fr;gap:.3mm 1mm;align-content:center;min-width:0}.resource-meta .resource-datum{display:block}.resource-meta .resource-datum span,.resource-meta .resource-datum strong{display:block}.resource-meta .resource-datum strong{font-size:6.2px}.resource-meta .resource-uuid{grid-column:1/-1;border-top:1px solid #ddd;padding-top:.35mm}.resource-meta .resource-uuid strong{font-size:4.9px;overflow-wrap:anywhere;white-space:normal}.resource-meta .resource-repository-url{grid-column:1/-1;font-size:4.2px;line-height:1.05;white-space:normal;overflow-wrap:anywhere;max-height:2.4em}.resource-meta .resource-repository-url span{width:7mm}
.resource-details{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-content:center;column-gap:3mm;row-gap:.6mm;min-width:0}.resource-datum{display:grid;grid-template-columns:auto minmax(0,1fr);gap:1mm;align-items:baseline;min-width:0}.resource-datum span{font:500 6px/1.1 ui-monospace,SFMono-Regular,Consolas,monospace;text-transform:uppercase;color:#666;white-space:nowrap}.resource-datum strong{font:600 7.5px/1.12 ui-monospace,SFMono-Regular,Consolas,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.resource-level{grid-column:1/-1}.resource-url{font:500 6.5px/1.1 ui-monospace,SFMono-Regular,Consolas,monospace;color:#222;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-top:1px solid #eee;padding-top:.7mm}
.resource-urls{min-width:0}.resource-url{display:block}.resource-url span{display:inline-block;width:9mm;font-weight:700;color:#666}.resource-url+.resource-url{padding-top:.35mm}.resource-meta{padding:1.4mm 1.7mm;border-right:1px solid #aaa;display:grid;grid-template-columns:1fr 1fr;gap:.65mm 1.6mm;align-content:center;min-width:0}.resource-meta .resource-datum{display:block}.resource-meta .resource-datum span,.resource-meta .resource-datum strong{display:block}.resource-meta .resource-datum strong{font-size:7px}.resource-meta .resource-uuid{grid-column:1/-1;border-top:1px solid #ddd;padding-top:.7mm}.resource-meta .resource-uuid strong{font-size:5.8px;overflow-wrap:anywhere;white-space:normal}
""" """
@@ -3322,47 +3320,63 @@ def page_resource_print_css(data: dict) -> str:
] ]
) )
qr_box = ( qr_box = (
"height:30mm;box-sizing:border-box;padding:25.3mm .8mm .5mm;" "height:30mm;box-sizing:border-box;padding:24mm .8mm .5mm;"
"border:1px solid #111;background-color:#fff;background-repeat:no-repeat;" "border:1px solid #111;background-color:#fff;background-repeat:no-repeat;"
"background-position:center .7mm;background-size:24mm 24mm;" "background-position:center 1mm;background-size:22mm 22mm;"
"vertical-align:middle;text-align:center;" "vertical-align:middle;text-align:center;"
"font:700 5px/1 ui-monospace,SFMono-Regular,Consolas,monospace;" "font:700 5px/1 ui-monospace,SFMono-Regular,Consolas,monospace;"
"letter-spacing:.08em;color:#444" "letter-spacing:.08em;color:#444"
) )
if repository_qr_enabled: if repository_qr_enabled:
def header_row(primary: object = "", meta: object = "") -> str:
left = str(primary).replace("\n", " ")[:92]
right = str(meta).replace("\n", " ")
return f"{left:<92} {right}"
center_before_page = "\n".join( center_before_page = "\n".join(
[ [
f"{fields['category']} | {card_prefix(card)}: {card.get('title', '')}", header_row(
f"PROJEKT {card.get('project', '')} · SERIA {card.get('series_title', card.get('series', ''))} · PRZEDMIOT {card.get('subject', '')}", fields["category"],
f"POZIOM {card.get('level', '')}", f"DATA {card.get('revision_date', '')} · STATUS {card.get('status', '')}",
f"DATA {card.get('revision_date', '')} · STATUS {card.get('status', '')} · KARTA {card.get('number', '')}/{card.get('count', '')} · WERSJA {card.get('version', '')}", ),
"STRONA ", header_row(
f"{card_prefix(card)}: {card.get('title', '')}",
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', '')}",
),
header_row(f"PRZEDMIOT {card.get('subject', '')}", "ARKUSZ "),
] ]
) )
center_after_page = "\n".join( center_after_page = "\n" + "\n".join(
[ [
f" · AUTOR {card.get('author', '')} · UUID {card.get('uuid', '')}", header_row(
f"GITEA {source_url}", f"POZIOM {card.get('level', '')}",
f"HTML {url}", f"UUID {card.get('uuid', '')}",
),
header_row(f"GITEA {source_url}"),
header_row(f"HTML {url}"),
] ]
) )
return f""" return f"""
@page{{ @page{{
size:A4; size:A4;
margin:35mm 9mm 15mm; margin:35mm 21.5mm 15mm;
@top-left{{content:"GITEA";width:27mm;{qr_box};background-image:{repository_qr_background}}} @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:138mm;height:30mm;box-sizing:border-box;padding:1.2mm 2mm;border-block:1px solid #111;white-space:pre-wrap;vertical-align:middle;text-align:left;font:600 7px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace;color:#111}} @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-right{{content:"HTML";width:27mm;{qr_box};background-image:{page_qr_background}}} @top-right{{content:"HTML";width:24mm;{qr_box};background-image:{page_qr_background}}}
}} }}
""" """
return f""" return f"""
@page{{ @page{{
size:A4; size:A4;
margin:35mm 9mm 15mm; 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:49mm;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-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:116mm;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}} @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}}
@top-right{{content:"HTML";width:27mm;{qr_box};background-image:{page_qr_background}}} @top-right{{content:"HTML";width:24mm;{qr_box};background-image:{page_qr_background}}}
}} }}
""" """