PerfectVector
著者: Irene Kim2 分で読める

WeasyPrintのSVG画像:PDFで消えるロゴを直す

正しいベースURLと明示的なCSSサイズで、WeasyPrintのPDFから消えるSVGロゴを直します。保存したPDFのベクターパス、比率、図形を確認します。

目次

WeasyPrintはHTMLの画像要素を通じてSVGをPDFに配置できます。HTMLを文字列として渡すとロゴが消える場合は、まず画像URLを確認してください。相対パスにはベースURLが必要です。ファイルが読み込めたらCSSで印刷サイズを設定し、保存したPDFを確認します。現在のWeasyPrint APIリファレンスでは、この base_url の要件を説明しています。

以下の例では、オリジナルの枠形のロゴを使って画像が欠ける問題を再現します。修正後のPDFが、指定したサイズのベクター描画形状を含むことも確認します。すでにSVGがあるなら、そのファイルから始めてください。この手順でベクター化が必要なのは、元の図形がピクセルでしか存在しない場合だけです。

図形を変える前に読み込みを確認する

ロゴが欠けていてもPDFは作成できます。リソースの警告を確認し、画像パスを解決し、同じSVGを再描画してから、形状の編集や別形式への変換を検討してください。

小さな文書でSVGが欠ける問題を再現する

Python 3.13.9とWeasyPrint 70.0で検証しました。プロジェクトの環境を使い、固定したバージョンをインストールします。

python -m pip install "weasyprint==70.0"
python -m weasyprint --info

WeasyPrintにはPangoなどのネイティブライブラリーも必要です。読み込みやバージョン確認コマンドが失敗したら、OS別のインストール手順に従ってください。ネイティブライブラリーの不足は、ロゴの欠落とは別の問題です。

次のフォルダー構成を作ります。

example/
  render.py
  assets/
    logo.svg

次のオリジナルのデモ図形を assets/logo.svg として保存します。中央の開口部は、塗りつぶした長方形より確認に役立ちます。手書きした形状であり、PerfectVectorの変換結果ではありません。

<svg xmlns="http://www.w3.org/2000/svg"
     width="120" height="60" viewBox="0 0 120 60">
  <path fill="#087f8c" fill-rule="evenodd"
        d="M0 0H120V60H0Z M20 15V45H100V15Z"/>
</svg>

render.py に次を記述します。

from pathlib import Path
import logging
from weasyprint import HTML
 
logging.basicConfig(level=logging.WARNING)
root = Path(__file__).resolve().parent
 
html = """<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    @page { size: A4; margin: 20mm; }
    body { margin: 0; }
    .logo { display: block; width: 60mm; height: auto; }
  </style>
</head>
<body>
  <img class="logo" src="assets/logo.svg" alt="Frame logo">
</body>
</html>
"""
 
HTML(string=html).write_pdf(root / "missing-base.pdf")
 
HTML(
    string=html,
    base_url=root.as_uri() + "/",
).write_pdf(root / "resolved.pdf")

python render.py を実行します。最初のPDFでは枠がなく、画像の代替テキストが表示されました。ログには次の記述がありました。

Relative URI reference without a base URI: assets/logo.svg

2つ目のPDFでは青緑色の枠が表示され、開口部も維持されました。どちらの呼び出しもファイルを作成するため、PDFが存在することだけでは成功を確認できません。

相対パスに安定した基準フォルダーを与える

assets/logo.svg は、何かを基準にした相対位置でファイルを示します。2つ目の呼び出しの base_url がその開始位置を与えます。root.as_uri() は絶対的なfile URIを作り、末尾のスラッシュでディレクトリーを基準にします。ロゴは同じ assets フォルダーに残り、SVGの内容も変わりません。

Path(__file__) を使うことで、別の作業ディレクトリーから起動しても、例はスクリプトのフォルダーに紐づきます。ノートブックでは通常 __file__ が使えないので、代わりにプロジェクトのフォルダーを明示してください。

フォルダー内のSVG図形と、文書ページ上の同じ図形を矢印でつないだイラスト
リソース解決のイラスト。図形を配置する前に、文書からSVGファイルへ到達する経路が必要です。

もう1つ、有用な対照テストがあります。HTMLを render.py と同じ場所の invoice.html として保存し、ファイル自体を読み込みます。

HTML(filename=str(root / "invoice.html")).write_pdf(
    root / "from-file.pdf"
)

WeasyPrintは入力ファイル名やURLから基準を推定できます。ファイルを使うテストでは、ベースを明示した場合と同じ枠が得られました。HTML文字列には、その推定に使う対応したファイル名がありません。これらの入力方式と基準解決の規則は、HTMLコンストラクターに記載されています。

ログイン後にしか取得できない画像では、正しいURLでも不十分な場合があります。URLフェッチャーの説明によると、既定のHTTPクライアントはCookieや認証を提供しません。アプリで承認されたリソース取得方法を使い、PDFを生成する環境でテストしてください。ログイン済みブラウザーで見えるロゴに、PDFのプロセスもアクセスできるとは限りません。

画像サイズとPDFのページサイズを別々に設定する

この例では @page でA4用紙と余白を設定し、.logo で画像の幅を60 mmにしています。元の比率は2:1なので、height: auto で高さは30 mmになります。保存したPDFでは枠が60 × 30 mmで、ページの上端と左端から20 mmの位置にありました。

ここでは図形が viewBox 全体を埋めています。自分のロゴでは、その長方形の中に余白があるかもしれません。その場合、60 mmの画像枠の中で実際の記号はそれより狭く見えます。CSSの幅を適当に大きくして補う前に、SVGのキャンバスを確認してください。SVGの読み込みサイズガイドでは、この違いを詳しく説明しています。

物理寸法を確認するときは、write_pdf() を既定の zoom=1 に保ちます。WeasyPrintのPDF出力リファレンスは、zoom を変えると物理的なCSS単位や名前付きページサイズも拡大縮小されると注意しています。

見える症状最初の確認次の工程
ロゴの代わりに代替テキストが表示される画像URLとリソースのログ有効なベースURLか到達可能な絶対URLを指定する
ロゴのサイズが違うCSSの幅と元の縦横比物理的な幅を設定し、SVGのキャンバスを確認する
図形の一部が欠ける元の境界と該当するSVG機能小さな失敗例に絞り、書き出したPDFを比較する
ローカルでは動くがサーバーでは失敗する利用可能なファイル、フォント、URLアクセス配備先のプロセスのリソースでテストする

PDFの内容と見た目を確認する

現在のWeasyPrintの形式に関する説明では、SVG画像はPDF出力でベクターとして描画されるとしています。ただし、ファイル名が .svg なら図形全体がベクターという意味ではありません。SVGにはビットマップを埋め込めます。結果がまだピクセル状に見える場合は、埋め込み画像の診断で確認してください。

この単純な例では、保存したPDFをPyMuPDF 1.26.7で調べました。

入力の条件描画レコード埋め込み画像見える枠
ベースなしのHTML文字列00欠落し、代替テキストが表示された
フォルダーのベース付きHTML文字列1060 × 30 mm、開口部を保持
ファイル名で読み込んだHTML10解決済み文字列と同じ結果

この結果は提示した枠に対するもので、すべてのSVG機能に当てはまるわけではありません。複合パス1つが見える形状1つを意味するわけでもありません。内側のサブパスが開口部を定義しています。

resolved.pdf を調べるには pymupdf==1.26.7 をインストールし、次を実行します。

import pymupdf
 
with pymupdf.open("resolved.pdf") as document:
    page = document[0]
    print("Drawing records:", len(page.get_drawings()))
    print("Embedded images:", len(page.get_images()))
    for drawing in page.get_drawings():
        box = drawing["rect"]
        print("Bounds in mm:", box.width * 25.4 / 72,
              box.height * 25.4 / 72)

PyMuPDFのpageリファレンスには、描画と画像の調査メソッドが記載されています。この数は診断情報で、忠実さの証明書ではありません。PDFも実際に開き、納品サイズで開口部、色、比率、文字を確認してください。複雑な図形は機能ごとに確認し、レンダラーを更新したら再検証します。

先に新しいSVGを準備する場合

利用できるロゴがPNGやJPGだけなら、HTML文書に置く前にPerfectVectorのロゴベクター化ツールでSVG候補を作れます。編集ソフトで輪郭、小さな開口部、文字を確認し、同じWeasyPrintの処理を通してSVGをテストします。画像ベクター化ガイドでは、ピクセルがパスになると何が変わるかを説明しています。

編集可能な元のSVGがあるなら保持してください。再びトレースするのではなく、ファイル参照やレイアウトを直します。写真はラスター画像のままで構いません。正確なブランド文字には元のフォントや手動の描き直しが必要な場合があります。ベクター化はサーバーのファイル不足、認証、非対応の描画機能を修復しません。

SVGを独立したPDFページに変換するだけなら、CairoSVGの書き出し手順がその小さな作業を扱っています。WeasyPrintは、図形が他の内容とページを共有するHTML文書の場合に適しています。

よくある質問

WeasyPrintがSVGロゴなしのPDFを作るのはなぜですか? 画像の読み込みが失敗しても、文書の他の部分は描画できる場合があります。リソースのログと画像URLを確認してください。このテストでは、相対画像パスを使うHTML文字列に、明示的なベースURLが必要でした。

先にSVGをPNGに変換する必要がありますか? いいえ。WeasyPrintはSVG画像を受け付けます。ベクター出力が目的なら対応する図形をSVGとして保ち、保存したPDFを確認してください。ベクター形状より見た目を優先すると意図的に決めた場合には、ラスターの書き出しも選択肢です。

base_urlはロゴの印刷サイズを変えますか? いいえ。相対リソースURLを解決するための基準を与えます。画像の配置サイズはCSSで設定し、SVGの寸法とviewBoxは別途確認してください。

参考資料

  1. WeasyPrint APIリファレンス — HTML入力、ベースURL、SVG出力、PDFのzoom動作を説明しています。
  2. WeasyPrintのはじめの手順 — インストールと、認証の制限を含むリソース取得を扱っています。
  3. PyMuPDFのPageリファレンス — この例で使った描画と埋め込み画像の調査メソッドを説明しています。

ラスターのロゴしかない場合は、PerfectVectorでSVGを作成し、形状と文字を確認してからHTMLに配置してください。その後、保存したPDFで見た目と物理サイズを検証します。

ブログのその他の記事

編集しやすい
よりきれいなSVGから始めましょう