blob: 864f3720b35ff52553dde31b4cf2cc9d261459f0 [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"
43 doc=$(basename "$abs_in")
44fi
45
46# --- Automatic hyphenation ---------------------------------------------------
47# The template HTML carries `hyphens: auto` plus an <html lang="…"> attribute.
48# Whether Chrome actually hyphenates depends on where it finds its hyphenation
49# dictionaries:
50#
51# * chrome-headless-shell (used in CI) BUNDLES the dictionaries in a
52# `hyphen-data` folder next to the binary and loads them automatically, so
53# hyphenation already works there without any help from us.
54# * full google-chrome (typical locally) ships the dictionaries as a
55# *component* living in the normal browser profile
56# (~/.config/google-chrome/hyphen-data). pagedown::chrome_print() launches
57# Chrome with a throwaway --user-data-dir that has no such component, so
58# hyphenation silently does nothing.
59#
60# For the local case we seed a persistent profile with that component and point
61# Chrome at it: extra_args are appended after pagedown's own --user-data-dir and
62# Chrome honours the last value of a repeated switch. We symlink the whole
63# hyphen-data tree (all languages), so whatever `lang:` the poster declares is
64# respected -- nothing here is English-specific.
65lang_base=${poster_lang%%-*}
66
67chrome_profile="${CHROME_PROFILE_DIR:-$HOME/.cache/pagedown-chrome-profile}"
68user_data_dir_arg=""
69for hyphen_src in \
70 "$HOME/.config/google-chrome/hyphen-data" \
71 "$HOME/.config/google-chrome-beta/hyphen-data" \
72 "$HOME/.config/chromium/hyphen-data"; do
73 [ -d "$hyphen_src" ] || continue
74 mkdir -p "$chrome_profile"
75 ln -sfn "$hyphen_src" "$chrome_profile/hyphen-data"
76 # Only override the profile when we have something to seed; otherwise leave
77 # pagedown's default (throwaway) profile so CI's bundled dictionaries load.
78 user_data_dir_arg=",'--user-data-dir=$chrome_profile'"
79 break
80done
81
82# Warn at build time if no dictionary for the poster's language is reachable,
83# either from the seeded profile or bundled next to the Chrome binary pagedown
84# will launch. Better a clear warning than a silently un-hyphenated poster.
85chrome_bin=$(R --slave -e 'cat(pagedown::find_chrome())' 2>/dev/null || true)
86bundled_dir="/nonexistent"
87[ -n "$chrome_bin" ] && bundled_dir="$(dirname "$(readlink -f "$chrome_bin")")/hyphen-data"
88have_dict=false
89for dict in "$chrome_profile"/hyphen-data/*/hyph-"$lang_base"*.hyb \
90 "$bundled_dir"/hyph-"$lang_base"*.hyb; do
91 # Unmatched globs stay literal, so the file test simply fails for them.
92 [ -e "$dict" ] && { have_dict=true; break; }
93done
94if $have_dict; then
95 echo "html2pdf: hyphenation enabled for lang='$poster_lang'." >&2
96else
97 echo "html2pdf: WARNING - no hyphenation dictionary for lang='$poster_lang'; the PDF will not be hyphenated." >&2
98fi
99
100R -e "pagedown::chrome_print('$doc'$out_arg,
Marc Kupietzfcb7d472025-06-17 18:07:57 +0200101 options=list(
102 pageRanges='1',
103 paperWidth=33.11,
104 paperHeight=46.81,
105 marginTop=0,
106 marginBottom=0,
107 marginLeft=0,
108 marginRight=0,
109 printBackground=TRUE,
110 scale=0.98, # Compensate for ~2% print scaling
111 displayHeaderFooter=FALSE,
112 preferCSSPageSize=TRUE
113 ),
114 extra_args = c(
115 '--disable-gpu',
116 '--no-sandbox',
117 '--disable-dev-shm-usage',
118 '--force-device-scale-factor=1',
119 '--disable-print-preview',
120 '--disable-background-timer-throttling',
121 '--allow-file-access-from-files',
122 '--force-color-profile=srgb',
123 '--disable-lcd-text',
124 '--disable-font-subpixel-positioning',
Marc Kupietzd13544d2026-07-17 11:04:45 +0200125 '--run-all-compositor-stages-before-draw'$user_data_dir_arg
Marc Kupietzfcb7d472025-06-17 18:07:57 +0200126 )
127)"