はんけトケ

地図の印刷

無視される印刷

ウェブ制作において、印刷(PDF含むプリンターデバイスへの出力)はないがしろにされがちな要素ですカツカツの制作期間と予算でそこまで手が回らない、といった場合ももちろんあるでしょうしかし大抵は単なる認識不足です。

たとえばサイトをレスポンシブに作ったなら、用紙にフィットさせたり、最低でもスクリーンと同じに印刷されるよう調整するくらいたやすいはずにもかかわらず崩れて印刷されてしまう、もったいないサイトは大手でもめずらしくありません制作工程に印刷の確認が含まれてないのは明らかです制作者も注文主も、サイトを印刷する需要があるとは考えていないのでしょう。

MapLibreの印刷不具合

いっぽう地図は、印刷される機会が少しはあると考えられますウェブGISの開発者もその点けっこう気にしているのではと想像しますが、探しても情報がろくに見つからず困りましたはんけトケが地図表示に使っている、MapLibre GL JSの印刷についてです。

わたしたちが地図をブラウザーで見ている間、MapLibreは地図をウインドウやディスプレーに合わせて描いていますこれをそのまま印刷しても、紙の形に合いませんはみ出したり余白が多すぎたりして体裁がとても悪い。

ならば、印刷時に印刷用スタイル(@media printのCSS)を適用すればよい常識ではそうですね。 ところが、MapLibreの地図にはなぜかそれが効かないのです。

前提条件

この現象は、これを書いている時点(2025年1月)で最新のMapLibre GL JS v.5とウインドウズ用Chrome 132で確認しました。

現在のブラウザーのシェアは、Edge含むChromium系が圧倒的で計約7割、次いでモバイルSafariが約1割強個人的な好みはさておいて、その他のブラウザーの対応を特に求められないかぎり、わたしはこの2者をウェブ開発のターゲットとしています。

モバイルSafari、数的にほぼiPadではなくiPhoneと見なせますが、スマートフォンで印刷する状況は少なそうしたがって、Chromeで正常に印刷できればまず問題ないでしょう逆にいえば、少なくともChromeではちゃんと印刷できるよう努めるべきです。

対策は?

話を戻しますこの問題はなぜ生じて、いかに解決するかあれだけ普及しているMapLibreなのだから、議論や対策はネット上にあふれていそうなものですが、まったく見つかりません探し方が悪いのか、だれも印刷に興味がないのか。

ともあれ仕組みの面から素朴に考えれば、印刷が要求されたら用紙のサイズで地図を描き直せばいいだけのはず印刷用CSSが適用されない理由はわかりませんが、ならば代わりに、地図を明示的・強制的にリサイズして描き直すようプログラムすればいいのでは。

印刷処理の流れ

印刷を要求すると、ブラウザーは「これから印刷するよ」とJavaScriptに通知してくれます(beforeprintイベント)JavaScript側では、その通知を受け取るイベントリスナー内でHTML要素などをいじることができ、それを抜けると印刷(正確には印刷プレビュー)が始まります。

ここに地図を描き直すチャンスがありますが、イベントリスナー内でMapLibreにリサイズや再描画を指示してみても変化なし関係ありそうなメソッドをいろいろ試しましたが、いずれも効果ありませんでした。

ひょっとすると、地図はワーカーやフレームアニメーション要求などで非同期的に再描画されるが、ブラウザーがその完了を待たずに印刷してしまうのかもしれませんというのは臆測にすぎませんが、印刷開始に非同期待ち合わせの仕組みがないのは確かで、不備ではないかと前々から建議はされていた模様いずれにせよ、挙動の詳細はわからず、わかったところでアプリケーション側ではこれ以上手の打ちようがなさそうです。

代替手段

らちが明かないので、あきらめてアプローチを変え、「印刷ビュー」モードをこしらえることにしました。

つまり用紙サイズの地図を、印刷する前にスクリーン上で描いてしまうのです印刷プレビューが二重化するようで冗長ですが、判型やら表示項目やらの選択肢を設けて存在意義を強調します。

もともと作ってある印刷用CSSを、ボタンが押されたらスクリーンに適用すればいいだけで、さほど手数はかかりませんでした。

それにしても、地図印刷の需要はあるはずなのに本当にこんな泥臭い手段しかないのか、いまだに釈然としません……(ちなみにMapLibre用印刷プラグインは存在するものの、こうした実装の手間が省けるだけで根本解決ではない)。

余談:ChromeとFirefoxのバグ

印刷の話題ついでに。

Chromeには、ウェブページを開いて初回の印刷内容がなぜか空白になってしまうバグがありますだいぶ昔から報告され続けているが放置されている、もはや仕様のような現象です実は、上記印刷ビューの開発中にもこのバグに遭遇しましたもしこれが起きたら、印刷ダイアログを閉じてもう一度開くとか、出力先デバイスを変えるとか、何かしら印刷内容が再描画されるよう仕向けると直るようです。

また、シェアは少ないものの主要ブラウザーのひとつであるFirefoxには、MapLibreの地図をまったく印刷できないバグがあります(おかしな挙動は他にもある)MapLibreの前身Mapbox時代からの現象で、対処法は一応判明しているものの、パフォーマンスが低下する副作用があるため採用しかねます。

いずれもこれを書いている現在の情報です将来解消されるかもしれません。

【追記】 印刷方法が判明

MapLibre GL JS v.5とウインドウズ用Chrome 132において、地図をスクリーンサイズから用紙サイズに直接描き直して印刷する方法がわかったので追記します。

beforeprintイベントリスナー内で、地図のコンテナー要素のwidth・heightスタイルを指定すれば良いようですサイズが親要素から自然に決まる構成だったとしても、必ずこのコンテナー要素に明示的にサイズ指定します。

コード例を示します。

<html>
<head>
<title>MapLibre GL JS v.5 & Chrome 132 地図の簡単な印刷処理例</title>
<script src="https://unpkg.com/maplibre-gl@5.0.0/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@5.0.0/dist/maplibre-gl.css" rel="stylesheet"/>
<style>
#map { width: 100%; height: 100% }

/* この印刷用スタイルがなぜか効かない!? */
@media print {
	#map {
		width: 210mm !important;
		height: 297mm !important;
	}
}
</style>
</head>
<body>
<div id="map"></div>
<script>
const	map = new maplibregl.Map({
	container: 'map',
	style: 'https://demotiles.maplibre.org/style.json',
	center: [139.73879, 35.65811],
	zoom: 4
});

// 印刷開始イベントリスナー
window.addEventListener('beforeprint', (e) => {
	// 地図コンテナー要素を用紙サイズに変更(ここでは仮にA4縦)
	const	container = map.getContainer();
	container.style.width  = '210mm';
	container.style.height = '297mm';
});

// 印刷終了イベントリスナー
window.addEventListener('afterprint', (e) => {
	// 地図コンテナー要素のサイズを元に戻す(ここでは仮に100%)
	const	container = map.getContainer();
	container.style.width  = '100%';
	container.style.height = '100%';
});
</script>
</body>
</html>

印刷用CSSで同様の指定をしても効果がなかったため、印刷要求時にはこの要素のリサイズや、それに伴うMapLibreの再描画はされないものと理解していましたが、ふと思い立ってこれを試してみたら効いたのです。

わかってしまえば簡単なことでしたしかし直感的にも論理的にも不可解な、これは現Chromeへの対症療法にすぎませんこんな方法はいずれ不要になるかもしれないし、しても効かなくなるかもしれません。

別の問題

ちなみに、こうして用紙サイズへ直接印刷できるようになって気づきましたが、地図タイルが一部欠けて印刷されてしまうことがあります。

スクリーンでは未表示だった地図の領域が用紙サイズでは表示される場合、タイルのロードが間に合わないためです印刷開始にはやはり非同期待ち合わせの仕組みが必要そうですね。

結局、印刷ビューを経て印刷するのが最も無難なようですあれの実装が無駄にならずに済み喜ぶべきでしょうか。

余談:モバイルSafariのバグ

ついでにモバイルSafariについて。

前提条件に述べたとおり、モバイルSafariの印刷機能は無視してもいいのですが、なにしろ日本ではシェアが大きく、不具合があると気になります。

ただ、ウェブ開発者ならご存じのとおりSafariは固有の挙動が多く、かつてのIE対応のごとき不毛さがあり深入りしたくありませんわたしの方針としては、あいまいな線引きながら「“簡単”にできる」ところまではとりあえず対処することにしています。

詳しくはいちいち書きませんが、今回もやはりモバイルSafari固有の問題がいくつか生じ、そのためだけにCSS構成を変えるくらいまでは手をかけました。

しかしまだおかしいが、アップルの尻拭いに無償奉仕する義理はこれ以上なく、あとはSafariの責任と割り切って放置しますそのうち自然に直るかもしれないし、もっと悪化するかもしれません。

印刷は鬼門

スクリーンや印刷シミュレーターではちゃんと効く、標準準拠かつありふれたスタイル指定が効かない時点で、印刷レンダラーの不具合・瑕疵は明らかです長々書きましたが、要はその瑕疵の回避に苦労した、というだけの記事でした。

冒頭の話に改めて戻ると、地図だのWebGLだの使わない一般サイトではここまで酷くないながら、ヤブヘビなので印刷に触れたくない気持ちも理解できますそうしてだれも触れないため改善も進まない、悪循環ですね。

beforeprint・afterprintイベントのタイミングはブラウザーによりまちまちとか聞くし、Firefoxはいまだにちゃんと印刷してくれないし、印刷機能は不遇すぎて鬼門と化していますブラウザーの印刷周りの開発ペースはかなり遅く、今回調べたことが今日明日役に立たなくなってしまうことはないと思いますそれでも本来無用なはずのことに労力を費やしたわけで、まさにIE対応のごとき徒労感を覚えた一件でした。

用語・注釈

イベント
イベント駆動型プログラミング(発生した事象に応じて動く受動的作法)において、ある事象の発生を知らせる信号。
イベントリスナー
イベントを寝て待つ関数あらかじめ登録しておくと、イベントが発生したときに呼び出される。
ワーカー(ウェブワーカー)
JavaScriptにおけるバックグラウンド実行スレッド。
IE対応
非標準の仕様・挙動への対処をやむなく迫られること。