Skip to content

Commit 4e4acba

Browse files
committed
Add support for WOFF
1 parent ad8b40a commit 4e4acba

File tree

10 files changed

+135
-7
lines changed

10 files changed

+135
-7
lines changed

.gitmodules

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
[submodule "vendor/woff2/source"]
2-
path = vendor/woff2/source
3-
url = https://github.com/google/woff2.git
41
[submodule "vendor/brotli/source"]
52
path = vendor/brotli/source
63
url = https://github.com/google/brotli
4+
[submodule "vendor/sfnt2woff/source"]
5+
path = vendor/sfnt2woff/source
6+
url = https://github.com/kseo/sfnt2woff.git
7+
[submodule "vendor/woff2/source"]
8+
path = vendor/woff2/source
9+
url = https://github.com/google/woff2.git

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "woff"
3-
version = "0.4.0"
3+
version = "0.5.0"
44
edition = "2021"
55
license = "Apache-2.0/MIT"
66
authors = ["Ivan Ukhov <[email protected]>"]
@@ -32,6 +32,7 @@ required-features = ["binary"]
3232

3333
[dependencies]
3434
arguments = { version = "0.8", optional = true }
35+
libc = { version = "0.2", default-features = false }
3536

3637
[build-dependencies]
3738
cc = "1"

build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
fn main() {
22
let target = std::env::var("TARGET").unwrap();
33

4+
cc::Build::new()
5+
.include("vendor/sfnt2woff/source/woff")
6+
.file("vendor/sfnt2woff/source/woff/woff.c")
7+
.static_flag(true)
8+
.warnings(false)
9+
.compile("libsfnt2woff.a");
10+
411
cc::Build::new()
512
.cpp(true)
613
.flag("-std=c++11")
@@ -71,4 +78,6 @@ fn main() {
7178
println!("cargo:rustc-link-lib=m");
7279
println!("cargo:rustc-link-lib=pthread");
7380
}
81+
82+
println!("cargo:rustc-link-lib=z");
7483
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
//! Compressor for fonts in Web Open Font Format.
22
3+
pub mod version1;
34
pub mod version2;

src/version1/ffi.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
extern "C" {
2+
pub fn woffEncode(
3+
sfntData: *const u8,
4+
sfntLen: u32,
5+
majorVersion: u16,
6+
minorVersion: u16,
7+
woffLen: *mut u32,
8+
status: *mut u32,
9+
) -> *const u8;
10+
11+
pub fn woffDecode(
12+
woffData: *const u8,
13+
woffLen: u32,
14+
sfntLen: *mut u32,
15+
status: *mut u32,
16+
) -> *const u8;
17+
}

src/version1/mod.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//! The [Web Open Font Format][1] of version 1.0.
2+
//!
3+
//! [1]: https://www.w3.org/TR/WOFF/
4+
5+
mod ffi;
6+
7+
use std::io::{Error, ErrorKind, Result};
8+
use std::path::Path;
9+
10+
/// Compress.
11+
pub fn compress(data: &[u8]) -> Option<Vec<u8>> {
12+
let mut size = 0;
13+
let mut status = 0;
14+
let data = unsafe {
15+
ffi::woffEncode(
16+
data.as_ptr() as _,
17+
data.len() as _,
18+
1,
19+
0,
20+
&mut size,
21+
&mut status,
22+
)
23+
};
24+
finalize(data, size, status)
25+
}
26+
27+
/// Decompress.
28+
pub fn decompress(data: &[u8]) -> Option<Vec<u8>> {
29+
let mut size = 0;
30+
let mut status = 0;
31+
let data =
32+
unsafe { ffi::woffDecode(data.as_ptr() as _, data.len() as _, &mut size, &mut status) };
33+
finalize(data, size, status)
34+
}
35+
36+
/// Convert.
37+
pub fn convert<T: AsRef<Path>>(source: T, destination: T) -> Result<()> {
38+
let data = std::fs::read(source)?;
39+
let destination = destination.as_ref();
40+
let data = if destination
41+
.extension()
42+
.and_then(|value| value.to_str())
43+
.map(|value| value == "woff")
44+
.unwrap_or(false)
45+
{
46+
match compress(&data) {
47+
Some(data) => data,
48+
_ => return Err(Error::new(ErrorKind::Other, "failed to compress")),
49+
}
50+
} else {
51+
match decompress(&data) {
52+
Some(data) => data,
53+
_ => return Err(Error::new(ErrorKind::Other, "failed to decompress")),
54+
}
55+
};
56+
std::fs::write(destination, data)
57+
}
58+
59+
fn finalize(data: *const u8, size: u32, status: u32) -> Option<Vec<u8>> {
60+
if !data.is_null() && status == 0 {
61+
let mut buffer = Vec::with_capacity(size as _);
62+
unsafe {
63+
std::ptr::copy_nonoverlapping(data, buffer.as_mut_ptr(), size as _);
64+
buffer.set_len(size as _);
65+
libc::free(data as *mut _);
66+
}
67+
Some(buffer)
68+
} else if !data.is_null() {
69+
unsafe {
70+
libc::free(data as *mut _);
71+
}
72+
None
73+
} else {
74+
None
75+
}
76+
}
77+
78+
#[cfg(test)]
79+
mod tests {
80+
#[test]
81+
fn compress() {
82+
let result = super::convert(
83+
"tests/fixtures/Roboto-Regular.ttf",
84+
"tests/fixtures/Roboto-Regular.woff",
85+
);
86+
assert!(result.is_ok());
87+
}
88+
89+
#[test]
90+
fn decompress() {
91+
let result = super::convert(
92+
"tests/fixtures/Roboto-Regular.woff",
93+
"tests/fixtures/Roboto-Regular.ttf",
94+
);
95+
assert!(result.is_ok());
96+
}
97+
}

src/version2/ffi.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#[allow(deprecated)]
21
extern "C" {
32
pub fn ComputeTTFToWOFF2Size(
43
data: *const u8,

src/version2/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
//!
33
//! [1]: https://www.w3.org/TR/WOFF2/
44
5+
mod ffi;
6+
57
use std::io::{Error, ErrorKind, Result};
68
use std::path::Path;
79

8-
mod ffi;
9-
1010
/// Compress.
1111
pub fn compress(data: &[u8], metadata: String, quality: usize, transform: bool) -> Option<Vec<u8>> {
1212
let metadata_length = metadata.len();

tests/fixtures/Roboto-Regular.woff

86.3 KB
Binary file not shown.

vendor/sfnt2woff/source

Submodule source added at 250a02d

0 commit comments

Comments
 (0)