blob: 6cdc7cb2ced77ebce5f48bf982f55b16c20c0555 [file] [log] [blame]
Marc Kupietzce1aa0c2023-06-15 07:50:23 +02001#!/bin/sh -eu
Marc Kupietzfcb7d472025-06-17 18:07:57 +02002in=$1
3
Marc Kupietzd13544d2026-07-17 11:04:45 +02004# Optional second argument: explicit output file. Made absolute here because we
5# may cd into the input's directory below. Further arguments are ignored.
6out_arg=""
7if [ $# -ge 2 ]; then
8 out_arg=", output='$(realpath -m "$2")'"
9fi
10
11doc=$in
12backup_file=""
13poster_lang=en
14
15# For local files (not URLs): temporarily inject CSS that forces exact A0
16# dimensions and prevents Chrome's print shrinking, and work from the file's
17# directory so relative links (e.g. skeleton_files/) resolve.
18if [ -f "$in" ]; then
19 abs_in=$(realpath "$in")
20 work_dir=$(dirname "$abs_in")
21
22 # Language the poster is written in, from <html lang="…"> (posterdown copies
23 # it from the Rmd `lang:` field). Used for the hyphenation check below.
24 poster_lang=$(sed -n 's/.*<html[^>]*[[:space:]]lang="\([A-Za-z-]*\)".*/\1/p' "$in" | head -n1)
25 poster_lang=${poster_lang:-en}
26
27 backup_file="${abs_in}.backup"
28 cp "$abs_in" "$backup_file"
29 # Restore the original file when we exit, also on failure.
30 trap '[ -n "$backup_file" ] && mv "$backup_file" "$abs_in"' EXIT
31
32 sed -i '/<\/head>/i\
33<style>\
34@page { size: 841mm 1189mm !important; margin: 0mm 0mm 20mm 0mm !important; }\
35html { width: 841mm !important; height: 1189mm !important; transform-origin: 0 0; }\
36body { width: 841mm !important; height: 1189mm !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; line-height: 1.2 !important; }\
37.poster { width: 841mm !important; height: 1189mm !important; }\
38* { line-height: 1.2 !important; }\
39p, div, span, li, ul, ol, td, th, a, strong, em, blockquote, caption, h1, h2, h3, h4, h5, h6 { line-height: 1.2 !important; }\
40</style>' "$abs_in"
41
42 cd "$work_dir"
Marc Kupietzbbd089c2026-08-04 11:48:16 +020043 # pagedown accepts absolute local paths consistently across Chrome/container
44 # versions; a bare basename can be rejected as an invalid URL.
45 doc=$abs_in
Marc Kupietzd13544d2026-07-17 11:04:45 +020046fi
47
48# --- Automatic hyphenation ---------------------------------------------------
49# The template HTML carries `hyphens: auto` plus an <html lang="…"> attribute.
50# Whether Chrome actually hyphenates depends on where it finds its hyphenation
51# dictionaries:
52#
53# * chrome-headless-shell (used in CI) BUNDLES the dictionaries in a
54# `hyphen-data` folder next to the binary and loads them automatically, so
55# hyphenation already works there without any help from us.
56# * full google-chrome (typical locally) ships the dictionaries as a
57# *component* living in the normal browser profile
58# (~/.config/google-chrome/hyphen-data). pagedown::chrome_print() launches
59# Chrome with a throwaway --user-data-dir that has no such component, so
60# hyphenation silently does nothing.
61#
62# For the local case we seed a persistent profile with that component and point
63# Chrome at it: extra_args are appended after pagedown's own --user-data-dir and
64# Chrome honours the last value of a repeated switch. We symlink the whole
65# hyphen-data tree (all languages), so whatever `lang:` the poster declares is
66# respected -- nothing here is English-specific.
67lang_base=${poster_lang%%-*}
68
69chrome_profile="${CHROME_PROFILE_DIR:-$HOME/.cache/pagedown-chrome-profile}"
70user_data_dir_arg=""
71for hyphen_src in \
72 "$HOME/.config/google-chrome/hyphen-data" \
73 "$HOME/.config/google-chrome-beta/hyphen-data" \
74 "$HOME/.config/chromium/hyphen-data"; do
75 [ -d "$hyphen_src" ] || continue
76 mkdir -p "$chrome_profile"
77 ln -sfn "$hyphen_src" "$chrome_profile/hyphen-data"
78 # Only override the profile when we have something to seed; otherwise leave
79 # pagedown's default (throwaway) profile so CI's bundled dictionaries load.
80 user_data_dir_arg=",'--user-data-dir=$chrome_profile'"
81 break
82done
83
84# Warn at build time if no dictionary for the poster's language is reachable,
85# either from the seeded profile or bundled next to the Chrome binary pagedown
86# will launch. Better a clear warning than a silently un-hyphenated poster.
87chrome_bin=$(R --slave -e 'cat(pagedown::find_chrome())' 2>/dev/null || true)
88bundled_dir="/nonexistent"
89[ -n "$chrome_bin" ] && bundled_dir="$(dirname "$(readlink -f "$chrome_bin")")/hyphen-data"
90have_dict=false
91for dict in "$chrome_profile"/hyphen-data/*/hyph-"$lang_base"*.hyb \
92 "$bundled_dir"/hyph-"$lang_base"*.hyb; do
93 # Unmatched globs stay literal, so the file test simply fails for them.
94 [ -e "$dict" ] && { have_dict=true; break; }
95done
96if $have_dict; then
97 echo "html2pdf: hyphenation enabled for lang='$poster_lang'." >&2
98else
99 echo "html2pdf: WARNING - no hyphenation dictionary for lang='$poster_lang'; the PDF will not be hyphenated." >&2
100fi
101
102R -e "pagedown::chrome_print('$doc'$out_arg,
Marc Kupietzfcb7d472025-06-17 18:07:57 +0200103 options=list(
104 pageRanges='1',
105 paperWidth=33.11,
106 paperHeight=46.81,
107 marginTop=0,
108 marginBottom=0,
109 marginLeft=0,
110 marginRight=0,
111 printBackground=TRUE,
112 scale=0.98, # Compensate for ~2% print scaling
113 displayHeaderFooter=FALSE,
114 preferCSSPageSize=TRUE
115 ),
116 extra_args = c(
117 '--disable-gpu',
118 '--no-sandbox',
119 '--disable-dev-shm-usage',
120 '--force-device-scale-factor=1',
121 '--disable-print-preview',
122 '--disable-background-timer-throttling',
123 '--allow-file-access-from-files',
124 '--force-color-profile=srgb',
125 '--disable-lcd-text',
126 '--disable-font-subpixel-positioning',
Marc Kupietzd13544d2026-07-17 11:04:45 +0200127 '--run-all-compositor-stages-before-draw'$user_data_dir_arg
Marc Kupietzfcb7d472025-06-17 18:07:57 +0200128 )
129)"