LoginSignup
2
4

More than 3 years have passed since last update.

Rustのcairoバインディングを使う

Posted at

はじめに

Rustからベクターグラフィックスライブラリcairoを使おうとしたら、ドキュメントが不親切で少しはまりどころがあったのでここにメモを残しておきます。
今回はRustのcairoバインディングとしてcairo-rsクレートを使用します。
使用したバージョンは0.7.0です。今後APIが変更される可能性があります。

PDFで出力する

それではcairo-rsを使って何らかの描画結果をPDFに出力してみましょう。
cargoを使って適当な名前でプロジェクトを作り、Cargo.tomlを編集します。

Cargo.toml
[package]
name = "example-cairo"
version = "0.1.0"
authors = ["Your Name"]
edition = "2018"

[dependencies]
cairo-rs = { version = "0.7.0", features = ["pdf"] }

ここで気をつけておきたい点として、[dependencies]の記述においてcairo-rsのpdf featureを有効にしておくことです。
これを書いておかないとcairo-rsのpdfモジュールを利用できません。

ところでcrates.ioに登録されているRustのクレートのドキュメントはDocs.rsで閲覧することができますが、Docs.rsはクレートがfeaturesとして提供している機能のドキュメントはデフォルトでは生成しないようです。
Cargo.toml[package.metadata.docs.rs]テーブルに適切な記述をすればfeaturesのドキュメントも生成されるのですが、2019年6月26日時点でcairo-rsはそれをしていません。

一方、Gtk-rsのサイトに設置されているcairo-rsのドキュメントではfeaturesのドキュメントも生成されていますからこちらを参照しましょう。

Context構造体の作成

Cairoを使ってグラフィックの描画をする際にはContext構造体に対する操作を通して描画を行います。
ドキュメントのContext構造体のページを見ましょう。
Context構造体を作るにはnewメソッドを呼び出せば良いので、まずはContext::newメソッドの宣言確認しておきます。

pub fn new(target: &Surface) -> Context

上記のようにSurface構造体の参照を受け取ってContext構造体を返します。
Surface構造体が必要そうなのでドキュメントのSurface構造体のページを参照したいのですが、こちらから探すとわかりづらいので先に答えを言ってしまいましょう。

PDFを出力するにはcairo::PdfSurface構造体を使います。このPdfSurface構造体のnewメソッドの宣言は

pub fn new<P: AsRef<Path>>(width: f64, height: f64, path: P) -> Self

となっているので、適当なサイズとファイルパスを渡して呼び出してPdfSurface構造体を得ます。
この構造体はDeref<Target=Surface>トレイトを実装しているので、その参照をContext::newの引数として渡すことができます。

ここまでに述べたことをコードにしておきましょう。src/main.rsは次のようになります:

src/main.rs
use cairo::*;

fn main() {
    let surface = PdfSurface::new(600.0, 600.0, "test.pdf");
    let context = Context::new(&surface);
}

このコードをビルドして実行すると、test.pdfに空白のPDFファイルができているはずです。

パスの描画

ここからはcairoに固有の知識が必要になります。
Cairoでパス(線)を描画するためには、Contextの内部で保持されている点をmove_toline_toなどのメソッドを使って動かしてから、strokeメソッドを呼び出す必要があります。
また、パスを塗りつぶすにはset_sourceメソッドでPatternを設定してからfillメソッドを呼ぶ必要があります。

細かな説明は省略しますが雰囲気を掴むために例を以下に書いておきます。
ソースコードは以下のURLのgithubリポジトリにも置いておきます。
https://github.com/ShotaroTsuji/example-cairo

src/main.rs
use cairo::*;

fn draw_example(context: &Context) {
    context.move_to(100.0, 100.0);
    context.line_to(100.0, 150.0);
    context.line_to(150.0, 100.0);
    context.stroke();

    context.move_to(300.0, 300.0);
    context.rel_line_to(-100.0, 0.0);
    context.rel_line_to(0.0, -100.0);
    context.rel_line_to(100.0, 100.0);
    context.close_path();
    context.stroke();

    context.set_dash(&[5.0, 2.5], 0.0);
    context.set_line_width(5.0);
    context.move_to(100.0, 400.0);
    context.rel_curve_to(10.0, 10.0,
                         50.0, 20.0,
                         100.0, 0.0);
    context.rel_curve_to(50.0, -20.0,
                         50.0, -60.0,
                         100.0, 0.0);
    context.stroke();

    let pat = SolidPattern::from_rgb(0.0, 1.0, 0.5);
    context.set_source(&pat);
    context.rectangle(500.0, 100.0, 50.0, 80.0);
    context.fill();

    context.set_font_size(30.0);
    let ext = context.text_extents("Hello, world!");
    let grad = LinearGradient::new(400.0, 400.0,
                                   400.0+ext.width, 400.0+ext.height);
    grad.add_color_stop_rgb(0.0, 1.0, 0.0, 0.0);
    grad.add_color_stop_rgb(0.5, 0.8, 0.8, 0.0);
    grad.add_color_stop_rgb(1.0, 0.0, 1.0, 0.0);
    context.set_source(&grad);
    context.move_to(400.0, 400.0);
    context.show_text("Hello, world!");
}

fn main() {
    let surface = PdfSurface::new(600.0, 600.0, "test.pdf");
    let context = Context::new(&surface);

    draw_example(&context);
}

まとめ

cairo-rsクレートを使ってみたのですが、個人的な感触としては割と生のままのバインディングという感じでRustっぽい感じはしませんでした。
今回作業している途中にcairo-rsのバージョンが0.6.0から0.7.0に上がったのですが、その際にAPIの変更が行われてしまい少し困惑しました。まだ1.0.0になっていないので変更されても仕方がないのですが……

それから、先ほど掲出したリポジトリ( https://github.com/ShotaroTsuji/example-cairo
src/bin/sakura.rsはもう少し面白いサンプルを書きました。

2
4
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
2
4