fulgur 0.0.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/Cargo.toml +30 -0
- data/README.md +210 -9
- data/ext/fulgur/Cargo.toml +30 -0
- data/ext/fulgur/extconf.rb +8 -0
- data/lib/fulgur/asset_bundle.rb +11 -0
- data/lib/fulgur/margin.rb +36 -0
- data/lib/fulgur/version.rb +5 -0
- data/lib/fulgur.rb +15 -11
- data/src/asset_bundle.rs +78 -0
- data/src/engine.rs +297 -0
- data/src/error.rs +57 -0
- data/src/gvl.rs +79 -0
- data/src/lib.rs +40 -0
- data/src/margin.rs +76 -0
- data/src/page_size.rs +93 -0
- data/src/pdf.rs +114 -0
- metadata +41 -14
data/src/pdf.rs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//! `Fulgur::Pdf` — `Engine#render_html` が返す結果オブジェクト。
|
|
2
|
+
//!
|
|
3
|
+
//! バイト列を保持し、`to_s` (ASCII-8BIT String), `bytesize`, `to_base64`,
|
|
4
|
+
//! `to_data_uri`, `inspect`, `write_to_path`, `write_to_io` を提供する。
|
|
5
|
+
|
|
6
|
+
use base64::Engine as _;
|
|
7
|
+
use magnus::{Error, Module, RModule, RString, Ruby, TryConvert, Value, method, prelude::*};
|
|
8
|
+
|
|
9
|
+
/// `write_to_io` でチャンク単位に分割するサイズ (64 KiB)。
|
|
10
|
+
const CHUNK_SIZE: usize = 64 * 1024;
|
|
11
|
+
|
|
12
|
+
#[magnus::wrap(class = "Fulgur::Pdf", free_immediately, size)]
|
|
13
|
+
pub struct RbPdf {
|
|
14
|
+
pub(crate) bytes: Vec<u8>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl RbPdf {
|
|
18
|
+
pub(crate) fn new(bytes: Vec<u8>) -> Self {
|
|
19
|
+
Self { bytes }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fn bytesize(&self) -> usize {
|
|
23
|
+
self.bytes.len()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fn to_s(&self) -> RString {
|
|
27
|
+
// `RString::from_slice` は ASCII-8BIT (binary) encoding の Ruby String を返す。
|
|
28
|
+
RString::from_slice(&self.bytes)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fn to_base64(&self) -> String {
|
|
32
|
+
base64::engine::general_purpose::STANDARD.encode(&self.bytes)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn to_data_uri(&self) -> String {
|
|
36
|
+
format!("data:application/pdf;base64,{}", self.to_base64())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fn inspect(&self) -> String {
|
|
40
|
+
format!("#<Fulgur::Pdf bytesize={}>", self.bytes.len())
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// PDF バイト列をファイルパスに書き込む。
|
|
44
|
+
///
|
|
45
|
+
/// `path` は `String` または `to_path` に応答するオブジェクト (`Pathname` など)。
|
|
46
|
+
/// エラーは `fulgur::Error::Io` 経由で `map_fulgur_error` に委譲する
|
|
47
|
+
/// (`NotFound` なら `Errno::ENOENT`、それ以外は `Fulgur::RenderError`)。
|
|
48
|
+
fn write_to_path(&self, path: Value) -> Result<(), Error> {
|
|
49
|
+
let ruby = Ruby::get().expect("ruby vm");
|
|
50
|
+
let path_str = coerce_to_path(path)?;
|
|
51
|
+
std::fs::write(&path_str, &self.bytes).map_err(|e| {
|
|
52
|
+
let fulgur_err = fulgur::Error::Io(e);
|
|
53
|
+
crate::error::map_fulgur_error(&ruby, fulgur_err)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// PDF バイト列を Ruby IO オブジェクトに書き込む。
|
|
58
|
+
///
|
|
59
|
+
/// - `binmode` に応答する場合のみ呼んでエンコーディング変換を防ぐ。独自ダック型
|
|
60
|
+
/// IO ラッパが `binmode` を実装していないケースでも `NoMethodError` を投げない。
|
|
61
|
+
/// - 64 KiB チャンクに分割して `write` を呼ぶ。ピークメモリは `N + CHUNK` に収まる。
|
|
62
|
+
/// - `IO#write` が要求より少ないバイト数を返した場合 (socket / pipe / 独自 IO の短書き込み)
|
|
63
|
+
/// は残りを再送する。戻り値が 0 の場合は無限ループを防ぐため `RuntimeError` を返す。
|
|
64
|
+
fn write_to_io(&self, io: Value) -> Result<(), Error> {
|
|
65
|
+
let responds: bool = io.funcall("respond_to?", (magnus::Symbol::new("binmode"),))?;
|
|
66
|
+
if responds {
|
|
67
|
+
let _: Value = io.funcall("binmode", ())?;
|
|
68
|
+
}
|
|
69
|
+
for chunk in self.bytes.chunks(CHUNK_SIZE) {
|
|
70
|
+
let mut offset = 0;
|
|
71
|
+
while offset < chunk.len() {
|
|
72
|
+
let rb_bytes = RString::from_slice(&chunk[offset..]);
|
|
73
|
+
let written: usize = io.funcall("write", (rb_bytes,))?;
|
|
74
|
+
if written == 0 {
|
|
75
|
+
return Err(Error::new(
|
|
76
|
+
magnus::exception::runtime_error(),
|
|
77
|
+
"IO#write returned 0 bytes; cannot make progress",
|
|
78
|
+
));
|
|
79
|
+
}
|
|
80
|
+
offset += written;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
Ok(())
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Ruby 値をファイルパス文字列に変換する。`Pathname` 等 `to_path` に応答する
|
|
88
|
+
/// オブジェクトを受け入れ、そうでなければ `to_str` で暗黙変換する。
|
|
89
|
+
pub(crate) fn coerce_to_path(value: Value) -> Result<String, Error> {
|
|
90
|
+
let responds: bool = value.funcall("respond_to?", (magnus::Symbol::new("to_path"),))?;
|
|
91
|
+
let converted: Value = if responds {
|
|
92
|
+
value.funcall("to_path", ())?
|
|
93
|
+
} else {
|
|
94
|
+
value
|
|
95
|
+
};
|
|
96
|
+
<String>::try_convert(converted).map_err(|_| {
|
|
97
|
+
Error::new(
|
|
98
|
+
magnus::exception::type_error(),
|
|
99
|
+
"path must be a String or respond to to_path",
|
|
100
|
+
)
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pub fn define(_ruby: &Ruby, fulgur: &RModule) -> Result<(), Error> {
|
|
105
|
+
let class = fulgur.define_class("Pdf", magnus::class::object())?;
|
|
106
|
+
class.define_method("bytesize", method!(RbPdf::bytesize, 0))?;
|
|
107
|
+
class.define_method("to_s", method!(RbPdf::to_s, 0))?;
|
|
108
|
+
class.define_method("to_base64", method!(RbPdf::to_base64, 0))?;
|
|
109
|
+
class.define_method("to_data_uri", method!(RbPdf::to_data_uri, 0))?;
|
|
110
|
+
class.define_method("inspect", method!(RbPdf::inspect, 0))?;
|
|
111
|
+
class.define_method("write_to_path", method!(RbPdf::write_to_path, 1))?;
|
|
112
|
+
class.define_method("write_to_io", method!(RbPdf::write_to_io, 1))?;
|
|
113
|
+
Ok(())
|
|
114
|
+
}
|
metadata
CHANGED
|
@@ -1,35 +1,62 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fulgur
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mitsuru Hayasaka
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
12
|
-
dependencies:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
date: 2026-04-18 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rb_sys
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.9'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.9'
|
|
27
|
+
description: Ruby bindings for fulgur, a deterministic HTML/CSS to PDF rendering engine.
|
|
28
|
+
email:
|
|
29
|
+
- hayasaka.mitsuru@gmail.com
|
|
18
30
|
executables: []
|
|
19
|
-
extensions:
|
|
31
|
+
extensions:
|
|
32
|
+
- ext/fulgur/extconf.rb
|
|
20
33
|
extra_rdoc_files: []
|
|
21
34
|
files:
|
|
35
|
+
- CHANGELOG.md
|
|
36
|
+
- Cargo.toml
|
|
22
37
|
- LICENSE-MIT
|
|
23
38
|
- README.md
|
|
39
|
+
- ext/fulgur/Cargo.toml
|
|
40
|
+
- ext/fulgur/extconf.rb
|
|
24
41
|
- lib/fulgur.rb
|
|
42
|
+
- lib/fulgur/asset_bundle.rb
|
|
43
|
+
- lib/fulgur/margin.rb
|
|
44
|
+
- lib/fulgur/version.rb
|
|
45
|
+
- src/asset_bundle.rs
|
|
46
|
+
- src/engine.rs
|
|
47
|
+
- src/error.rs
|
|
48
|
+
- src/gvl.rs
|
|
49
|
+
- src/lib.rs
|
|
50
|
+
- src/margin.rs
|
|
51
|
+
- src/page_size.rs
|
|
52
|
+
- src/pdf.rs
|
|
25
53
|
homepage: https://github.com/mitsuru/fulgur
|
|
26
54
|
licenses:
|
|
27
|
-
- MIT
|
|
28
55
|
- Apache-2.0
|
|
56
|
+
- MIT
|
|
29
57
|
metadata:
|
|
58
|
+
allowed_push_host: https://rubygems.org
|
|
30
59
|
source_code_uri: https://github.com/mitsuru/fulgur
|
|
31
|
-
changelog_uri: https://github.com/mitsuru/fulgur/blob/main/CHANGELOG.md
|
|
32
|
-
bug_tracker_uri: https://github.com/mitsuru/fulgur/issues
|
|
33
60
|
post_install_message:
|
|
34
61
|
rdoc_options: []
|
|
35
62
|
require_paths:
|
|
@@ -38,7 +65,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
38
65
|
requirements:
|
|
39
66
|
- - ">="
|
|
40
67
|
- !ruby/object:Gem::Version
|
|
41
|
-
version:
|
|
68
|
+
version: 3.1.0
|
|
42
69
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
70
|
requirements:
|
|
44
71
|
- - ">="
|
|
@@ -48,5 +75,5 @@ requirements: []
|
|
|
48
75
|
rubygems_version: 3.5.22
|
|
49
76
|
signing_key:
|
|
50
77
|
specification_version: 4
|
|
51
|
-
summary:
|
|
78
|
+
summary: Offline HTML/CSS → PDF conversion
|
|
52
79
|
test_files: []
|