Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rustから

エンジンはcrates.ioにsghtmltopdfとして公開しています。 CLIの実行ファイルを作っているのと同じクレートです。 APIの一覧はdocs.rsを参照してください。

cargo add sghtmltopdf

Converter

ConverterはCLIと同じオプション列を受け取ります。 オプションリファレンスにあるものは、同じ名前・同じ意味のまま使えます。

use sghtmltopdf::{with_render_stack, Converter};

let converter = Converter::from_args(["--page-size", "A4", "--margin-top", "20mm"])?;
let html = std::fs::File::open("invoice.html")?;
let pdf = with_render_stack(|| converter.render_to_vec(html))?;
std::fs::write("invoice.pdf", pdf)?;

オプションはfrom_argsの時点で一度だけ検証します。 作ったConverterは何度でも使い回せ、Sendなので別スレッドへ渡せます。

入力パス・--output・serverサブコマンドは指定できません。 HTMLはrenderに渡すReaderから読み、PDFはrenderに渡すSinkへ書くためです。

文字コードはブラウザと同じ順(BOM、<meta charset>、UTF-8)で判定します。 --encodingを指定した場合はそちらを使います。

スタックサイズ

レンダリングは文書の入れ子の深さだけ再帰します。 スレッドの既定のスタックでは深い文書で落ちることがあるため、with_render_stackを通して十分なスタック(16MiB)を持つスレッドで実行してください。 クロージャの戻り値はそのまま返り、panicも呼び出し元へ伝わります。

エラー

ConvertErrorはCLIの終了コードと同じ4分類です。

バリアント終了コード例
Usage1知らないオプション、値の形式違い、実装しないオプション
Input2ファイルがない、フォントを読めない、書き込みに失敗した
Render3エンジンの上限(入れ子の深さなど)を超えた
Timeout4制限時間を超えた(HTTPサーバモードのみ)

ConvertErrorは#[non_exhaustive]なので、matchには_の腕が必要です。

ページが確定したそばから書き出す

render_to_vecはPDF全体をメモリに集めてから返します。 renderに自前のSinkを渡すと、ページのレイアウトが確定するたびにそのバイト列を受け取れます。

エンジンはwriteを先頭から順に(ページが確定するたびに1回以上)呼び、最後にfinishを1回だけ呼びます。 finishの戻り値がrenderの戻り値になります。

use std::io::{self, Write};
use sghtmltopdf::{with_render_stack, Converter, Sink};

struct WriterSink<W: Write>(W);

impl<W: Write> Sink for WriterSink<W> {
    type Output = W;
    type Error = io::Error;

    fn write(&mut self, bytes: &[u8]) -> io::Result<()> {
        self.0.write_all(bytes)
    }

    fn finish(mut self) -> io::Result<W> {
        self.0.flush()?;
        Ok(self.0)
    }
}

let converter = Converter::from_args(["--page-size", "A4"])?;
let html = std::fs::File::open("invoice.html")?;
let socket = std::net::TcpStream::connect("127.0.0.1:9000")?;
with_render_stack(|| converter.render(html, WriterSink(socket)))?;

Converter::renderに渡すSinkは、Errorがio::Errorである必要があります。

次のSinkは最初から用意しています。

Sink書き出し先
MemorySinkメモリ。finishでバイト列を返す
FileSinkファイル。一時ファイルに書き、成功したときだけ出力パスへrenameする
StdoutSink標準出力
BufferedSink指定したバイト数ごとにコールバックへ渡す。S3のマルチパートアップロード(最後以外は5MB以上)向け

ストリーミングモード(--streaming)と組み合わせると、ページを書き出した後にそのメモリを解放しながら読み進めます。 使える機能の制約はストリーミングモードを参照してください。

Engine

Engineはより低レベルなAPIです。 EngineOptionsを組み立て、HTMLをチャンクごとにfeedし、最後にfinishします。

use sghtmltopdf::{Engine, EngineOptions, FontSpec, MemorySink, Mode, PageSize};

let mut options = EngineOptions::default();
options.mode = Mode::Streaming;
options.settings.size = PageSize::A4;
options.fonts = vec![FontSpec { path: "fonts/NotoSansJP-Regular.ttf".into(), index: 0 }];

let mut engine = Engine::new(options, MemorySink::new());
engine.feed(b"<!DOCTYPE html><p>Hello</p>")?;
let pdf: Vec<u8> = engine.finish()?;

EngineOptionsは#[non_exhaustive]なので、クレートの外では構造体リテラルで書けません。 EngineOptions::default()で作ってからフィールドを書き換えてください。

文字コードの判定、--header-centerなどの簡易オプションから@pageへの変換、ヘッダー/フッターHTMLの読み込みはConverterの側で行っています。 Engineは受け取ったバイト列をUTF-8として扱います。

ローカルファイルへのアクセスも既定が異なります。 EngineOptionsの既定は制限なしで、CLIのように基準ディレクトリの外を拒否しません。 信頼できないHTMLを扱う場合はlocal_accessを設定してください。

特に理由がなければConverterを使ってください。

feature

feature既定内容
cli有効sghtmltopdfコマンドとConverter(clap)
server有効sghtmltopdf server(tiny_http)
svg有効SVG画像をベクタのまま埋め込む(svg2pdf)
svg-text無効SVG画像の中の<text>

ライブラリとして使う場合は、HTTPサーバを外すと依存が減ります。

[dependencies]
sghtmltopdf = { version = "0.5", default-features = false, features = ["cli", "svg"] }

安定性

semverの対象は、クレートのルートから使える型だけです。 sghtmltopdf::layoutのようなモジュールはドキュメントに出していません。 テストやRubyバインディングのために公開しているだけで、どのリリースでも変わることがあります。