CSS段組と印刷の地雷

column-count: 3 等を使ったCSSでの段組をして、印刷物をデザインする際にハマったことを共有します(ある程度段組CSSと印刷用CSSを知っている方を対象に書いています)。

バックグラウンド

行程さん

行程さん

行程さん」という、旅のしおりをウェブ上で簡単に作るサービスを作っています。今回はそのしおりをアナログ回帰させて、簡単に印刷してもらうために印刷専用のプレビュー画面をウェブ上で作ろうとしたのが始まりです。

機能

  • 二つ折り用と三つ折り用
  • 印刷されるものと同じ
印刷プレビューページを作りたい

考慮すること

特定の誰か、というものをあまり考えず、なるべくすべての環境で印刷可能にしたいと考えました。

  • 日本、日本人
  • 年齢
    • 10代〜60代くらい
  • ネットリテラシー
    • ある
    • ない
  • 通信速度
    • 遅い
    • 速い
  • OS、端末
    • Windows
    • macOS
    • Android
    • iOS
  • 印刷するプリンター
    • 所持
    • コンビニ

対応ブラウザの決定打になった地雷たち

すべてのブラウザに対応したかったのですが、以下のような状況によりできず、対応ブラウザをChrome(Windows、macOS、Android)のみにしました。iOS版Chromeは、印刷ダイアログのプレビューやPDF出力時はOS側が描画処理をしているのか、期待する表示にならず。

Chromeに対応しさえすれば、「Puppeteer + ヘッドレスChrome」でサーバー側からPDFが可能です

すべてのブラウザに対応ができない

無理でした。CSSの段組の解釈の違い(バグ?)や、印刷の余白や大きさの制御など、Chrome以外のブラウザではそもそも対応してないものが多かったです。

印刷ダイアログでプレビューを出しにくい

  • Chrome(Windows、macOS):○
  • Firefox(Windows):×
  • Firefox(macOS):△(手動でPDFのプレビュー)
  • IE:△(手動でメニューから選択)
  • Edge:○
  • Safari(macOS):○
  • Chrome(Android):○
  • Chrome(iOS):○
  • Safari(iOS):○

「(プレビューがそもそも)ない」「わかりづらい」「JavaScriptで制御できない」など、理由は様々です。

Edgeに背景を印刷するチェックボックスがない

ないんです。探しました。向かいのホーム、路地裏の窓、こんなとこにいるはずもないのに。でもないんです。

以下の方法を考えましたが、面倒になってやめました。

  • 背景画像をimg要素に変換、白抜き文字は -webkit-background-clip: text; でごにょごにょ?
  • SVGでごにょごにょ?
  • しおりすべてを画像化?

Windowsユーザーがデフォルトで使うであろうEdgeは、Chromiumにはあるようなので、それを待った方が得策と判断。

Edge(Chromium版)印刷プレビュー

出会った地雷たち(ほぼCSSで解決)

エンドユーザーにわざわざ「背景のグラフィック」チェックを入れさせたくない

body {
    -webkit-print-color-adjust: exact;
    color-adjust: exact;
}

で解決。

段組みの要素の周りに印刷余白分のマージンを開けたい

html body 要素の margin padding では解決できないので、

@page {
    size: A4 landscape;
    margin: 15mm;
}

で解決。

文字が途中で切れてしまう

段組みで文字が切れる
  • display: block; コンテンツ幅に合わせて縮小させたいので見送り
.comment-area {
    display: table;
    word-break: break-all;/*Chromeのみのため*/
}

で解決(最適解かどうかは別)。

段組すると縦に長くなる

段組み

こんな感じにしたい(上記はcolumn-count: 3 を指定)。

height を指定で解決。

今度は横長になってしまう

  • 読み込み後、最後の要素のY軸の位置を取得し、何ページになるか計算

  • JavaScriptで要素を下にページ数分コピー

  • 要素ごとに横にずらす

    .classname {
    transform: translateX(-297mm);
    }
    

で解決。

margin-left: -297mm; ではうまくいかない。

heightを指定しても2ページ目以降に文字量が1ページに満たないと、紙上で縦幅が潰れる

段組みでつぶれる

高さいっぱいの要素を用意。

.classname {
    height: 197.5mm;
    margin-top: -197.5mm;
}

で解決。

フチなし印刷がしたい

  • 拡大の率が安定しないためあきらめる
  • そのかわり、フチがあっても問題ないデザインにする

印刷時の紙の余白を、何mmにすればいいかわからない

電気屋とネットで調べました。

現在の家庭用の一般的なプリンターの余白の最小は5mm。印刷両面印刷時には6mmになるプリンターもあるようです。

行程さんでは6mmに設定で解決。

あまりにギリギリにしても、印刷時にどうしてもずれるのでずれたときに目立たせなくするために1mm増やしました。

mmとpxの両方の単位を混合させたい

  • 1インチ = 25.4mm = 96px のため混合させても大丈夫
  • 297mm = 297(mm) * 96/25.4(px/mm) = 1122.519685039px

PDF出力したら、文字が欠けてる

文字が欠ける

以下のときに再現されました。

  • Android端末「HUAWEI P20 lite」
  • Chrome
  • PDF出力
  • ローソン・ファミマ等のマルチプリンターで印刷
  • ウェブフォントを使っていない
  • 同じ条件では同じ文字が欠けるが、ページの構成を変えると別の文字が欠ける

完全な再現方法や仕組みはわからないのですが、プリンターのフォントとの相性な気がします。アウトライン化できれば解決するのですが、どうにかしてChrome上でアウトライン化できればいいなと思って調べましたが無理そうでした。

  • ウェブフォント「Noto Sans CJK JP」を適用(通常の太さと太文字と両方必要)で解決
  • WOFF形式のため、文字詰めをさせていた font-feature-settings が効かなくなるデメリットあり
  • すべてを読み込ませると重いのでサブセット化などで対応

高さにvh、%などの単位を使うと、AndroidのChromeの印刷ダイアログのプレビューで意図しない高さになっている!

vh、%を使うのを辞めることで解決。

サブタイトル(h2、h3)の後、段落(p)の前で段区切りしたくない!!

段区切り

Does not support avoid for page-break-before & page-break-after (only page-break-inside).

[Can I use]

CSSは用意されていますが、現状は無理っぽいです。

.classname {
    break-inside: avoid;
    padding-bottom: 0.6rem;
    margin-bottom: -0.6rem;
}

で解決。

絵文字を増やして、AndroidでPDF出力すると1メガくらい増える!

絵文字を入れると増える

誰か解決方法教えてほしいです。やっぱりサーバー側でPDF出力するのがてっとりばやそうです(現在は解決されているようです)。