0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

resvgの使い方

Posted at

resvgはRustで書かれたSVG描画ライブラリです。公式ドキュメントによると、規格への遵守度がlibrsvgはおろかChromeやFirefoxを抑えて1位とのことです。ただし、アニメーションやスクリプトなどには対応していません。

この記事では、resvgを使ってSVG文書をラスター化する方法を紹介します。ラスター化の基本的な流れは次の通りです。

  • SVG文書データからusvg::Treeオブジェクトを作成する
  • tiny_skia::PixmapMutオブジェクトを作成する
  • resvg::renderを使って描画処理を実行する
  • 描画結果を利用する

Tree

TreeのコンストラクタはSVG文書データとusvg::Optionsオブジェクトを受け取ります。SVG文書データは&[u8]&strroxmltree::Documentのいずれかです。OptionsオブジェクトはSVG文書を処理する方法をカスタマイズします。

Optionsの大体のフィールドの意味はドキュメントを読めばわかると思いますが、dpiについては注意が必要です。このフィールドは物理DPIではなく論理DPIを指定します。論理DPIはSVG生成ソフトが意図した値に設定しなければなりません。SVG 2の場合は必ず96.0です。

// SVG文書には "# が頻出することに注意(fill="#eee"など)
let svg_str = r##"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <defs>
    <clipPath id="clip-1">
      <rect x="0" y="0" width="60" height="60"/>
      <rect x="40" y="40" width="60" height="60"/>
    </clipPath>
    <linearGradient id="grad-1" y2="1">
      <stop offset="0" stop-color="red"/>
      <stop offset="1" stop-color="yellow"/>
    </linearGradient>
  </defs>
  <circle cx="50" cy="50" r="50" fill="url(#grad-1)" clip-path="url(#clip-1)"/>
</svg>
"##;
let tree = Tree::from_str(svg_str, &Options::default()).unwrap();

PixmapMut

PixmapRefPixmapMutPixmapはいずれもバッファのラッパーです。これらのオブジェクトはas_refas_mutto_ownedによって相互変換することができます。

PixmapRefはバッファを不変借用し、バッファの情報を利用するメソッドを提供します。

PixmapMutはバッファを可変借用し、バッファに対して描画操作を実施するメソッドを提供します。

Pixmapはバッファを所有します。PixmapMutとは異なり、アロケーションやムーブを伴うコンストラクタが提供されます。

どの型を使用する場合でもバッファの横幅と縦幅を整数で指定する必要があります。この値はSVG文書の横幅と縦幅に物理ピクセル比を乗じた値(を丸めた値)です。物理ピクセル比は物理DPI÷論理DPIで求められます。

let physical_dpi = 120.0;
let physical_pixel_ratio = physical_dpi / 96.0;

let tree_size = tree.size();
let pixmap_width = (tree_size.width() * physical_pixel_ratio).round() as u32;
let pixmap_height = (tree_size.height() * physical_pixel_ratio).round() as u32;
let mut pixmap = Pixmap::new(pixmap_width, pixmap_height).unwrap();

render

renderTreePixmapMutのほかにSVG文書全体に適用されるusvg::Transformオブジェクトを受け取ります。これを使って論理ピクセル座標を物理ピクセル座標に変換します。

let transform = Transform::from_scale(physical_pixel_ratio, physical_pixel_ratio);
resvg::render(&tree, transform, &mut pixmap.as_mut());

これでSVG文書の内容がPixmapに書き込まれました。あとはバッファの内容を好きに利用するだけです。今回はPNGとして保存します。

pixmap.save_png("image.png").unwrap();

結果

以上のプログラムによって作成された画像とInkscapeによって作成された画像を並べます。

image.png

image-inkscape.png

見た目は全く変わりませんが、ファイルサイズが倍以上違います。tiny-skiaでは圧縮時間を優先するのか、圧縮率を優先するかなどの設定ができない上、解像度などのメタデータを含めることができないので、真面目なコードでは別のエンコーダを使用したほうがいいのかもしれません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?