"""いじめ重大事態対応フロー図の印刷用PDFを、SVGからベクターで生成する。 以前は 2.5 倍のラスタPNGを埋め込んだPDF(1ページ版 約2MB / 6ページ版 約4MB)を 使っていたが、同じレイアウトのままベクターで組み直し、大幅に軽量化する (各ファイル約200KB。文字は拡大しても綺麗)。 - A4縦・タイル分割版(2列×3行=6ページ、のりしろ重複あり) - A4縦・全体像1ページ版(縮小・見取り図) レイアウトは旧ラスタ版と同一(A4 3100x4382px 相当、余白100px、紙面2900x4182px、 タイル 1160x1672.8、列 x=[0,660]、行 y=[0, base/2, base])。 base は SVG の元の高さ(リンク行を追記する前の値)。SVG の末尾に追記した 解説ページへのリンク行は、PDFではフッターのリンク注釈として付ける。 使い方: python scripts/make-flow-pdfs.py """ import pathlib import sys import pymupdf sys.stdout.reconfigure(encoding="utf-8") IMAGES = pathlib.Path(__file__).resolve().parent.parent / "public" / "ijime-judai-jitai" / "images" URL = "https://yasutakeyohei.com/ijime-judai-jitai/flow/" TITLE = "いじめの重大事態の対応フロー(ロール別)" # 旧ラスタ版と同じジオメトリ(A4 3100x4382px 相当) A4W = pymupdf.paper_rect("a4").width A4H = pymupdf.paper_rect("a4").height PX2PT = A4W / 3100.0 MARGIN = 100 * PX2PT DEST = pymupdf.Rect(MARGIN, MARGIN, MARGIN + 2900 * PX2PT, MARGIN + 4182 * PX2PT) TILE_W1 = 1160.0 # タイル幅(SVG座標=1x基準) TILE_H1 = 1672.8 # タイル高さ FOOT_SIZE = 50 * PX2PT # 旧: int(20*2.5)=50px FOOT_TOP = (4382 - 100 + 15) * PX2PT FOOT_COLOR = (0x45 / 255, 0x5A / 255, 0x64 / 255) FOOT_6P = TITLE + " {n}/6 (全体の一部・のりしろ重複あり)" FOOT_1P = TITLE + " 全体像(縮小のため文字は小さめ。読みやすい印刷は6ページ版を推奨)" # (SVGファイル名, 出力の6ページ版, 出力の1ページ版, SVGの元の高さ) JOBS = [ ("ijime-judai-flow.svg", "ijime-judai-flow-a4-6p.pdf", "ijime-judai-flow-a4-1p.pdf", 3890), ("ijime-judai-flow-private.svg", "ijime-judai-flow-private-a4-6p.pdf", "ijime-judai-flow-private-a4-1p.pdf", 3624), ] def one_page_rect(base_h): """1ページ版の配置(旧スクリプトの縮小計算を再現)。""" sc = min((2900 / 2.5) / 1820, ((4182 / 2.5) - 30) / base_h) w = 1820 * sc * 2.5 * PX2PT h = base_h * sc * 2.5 * PX2PT x = (3100 - 1820 * sc * 2.5) * PX2PT / 2 return pymupdf.Rect(x, MARGIN, x + w, MARGIN + h) def add_footer(page, text): page.insert_text( pymupdf.Point(MARGIN, FOOT_TOP + FOOT_SIZE * 0.8), text, fontsize=FOOT_SIZE, fontname="japan", color=FOOT_COLOR, ) page.insert_link({ "kind": pymupdf.LINK_URI, "from": pymupdf.Rect(0, FOOT_TOP - 5, A4W, A4H), "uri": URL, }) def build(svg_name, out_6p, out_1p, base_h): svg = pymupdf.open(IMAGES / svg_name) # show_pdf_page は PDF しか受け付けないため、いったんベクターPDFに変換する src = pymupdf.open("pdf", svg.tobytes(garbage=4, deflate=True)) svg.close() xs = [0.0, 660.0] span = base_h - TILE_H1 ys = [0.0, float(round(span / 2)), float(span)] tiles = [] n = 0 for y in ys: for x in xs: n += 1 tiles.append((pymupdf.Rect(x, y, x + TILE_W1, y + TILE_H1), FOOT_6P.format(n=n))) tiles.append((pymupdf.Rect(0, 0, 1820, base_h), FOOT_1P)) def make(pages, path): out = pymupdf.open() for i, (clip, footer) in enumerate(pages): page = out.new_page(width=A4W, height=A4H) dest = DEST if i < 6 else one_page_rect(base_h) page.show_pdf_page(dest, src, 0, clip=clip) add_footer(page, footer) out.subset_fonts() tmp = path.with_suffix(".tmp.pdf") out.save(tmp, garbage=4, deflate=True, clean=True) out.close() tmp.replace(path) print(f"{path.name}: {path.stat().st_size:,} bytes") make(tiles[:6], IMAGES / out_6p) make(tiles[6:], IMAGES / out_1p) src.close() for svg_name, out_6p, out_1p, base_h in JOBS: build(svg_name, out_6p, out_1p, base_h)