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分類です。
| バリアント | 終了コード | 例 |
|---|---|---|
Usage | 1 | 知らないオプション、値の形式違い、実装しないオプション |
Input | 2 | ファイルがない、フォントを読めない、書き込みに失敗した |
Render | 3 | エンジンの上限(入れ子の深さなど)を超えた |
Timeout | 4 | 制限時間を超えた(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バインディングのために公開しているだけで、どのリリースでも変わることがあります。