summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock22
-rw-r--r--Cargo.toml1
-rw-r--r--crates/typst-cli/Cargo.toml1
-rw-r--r--crates/typst-cli/src/compile.rs2
-rw-r--r--crates/typst-render/Cargo.toml29
-rw-r--r--crates/typst-render/src/lib.rs (renamed from crates/typst/src/export/render.rs)142
-rw-r--r--crates/typst/Cargo.toml3
-rw-r--r--crates/typst/src/export/mod.rs2
-rw-r--r--tests/Cargo.toml1
-rw-r--r--tests/src/benches.rs2
-rw-r--r--tests/src/tests.rs18
11 files changed, 126 insertions, 97 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c8ca8a48..4da1e66d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2908,9 +2908,7 @@ dependencies = [
"once_cell",
"palette",
"pdf-writer",
- "pixglyph",
"regex",
- "resvg",
"roxmltree",
"rustybuzz",
"serde",
@@ -2920,7 +2918,6 @@ dependencies = [
"subsetter",
"svg2pdf",
"time",
- "tiny-skia",
"toml",
"tracing",
"ttf-parser",
@@ -2976,6 +2973,7 @@ dependencies = [
"tracing-subscriber",
"typst",
"typst-library",
+ "typst-render",
"ureq",
"xz2",
"zip",
@@ -3066,6 +3064,23 @@ dependencies = [
]
[[package]]
+name = "typst-render"
+version = "0.9.0"
+dependencies = [
+ "bytemuck",
+ "comemo",
+ "flate2",
+ "image",
+ "pixglyph",
+ "resvg",
+ "roxmltree",
+ "tiny-skia",
+ "ttf-parser",
+ "typst",
+ "usvg",
+]
+
+[[package]]
name = "typst-syntax"
version = "0.9.0"
dependencies = [
@@ -3095,6 +3110,7 @@ dependencies = [
"ttf-parser",
"typst",
"typst-library",
+ "typst-render",
"unscanny",
"walkdir",
]
diff --git a/Cargo.toml b/Cargo.toml
index eb1b5ac1..b88d880b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,7 @@ keywords = ["typst"]
typst = { path = "crates/typst" }
typst-library = { path = "crates/typst-library" }
typst-macros = { path = "crates/typst-macros" }
+typst-render = { path = "crates/typst-render" }
typst-syntax = { path = "crates/typst-syntax" }
az = "1.2"
base64 = "0.21.2"
diff --git a/crates/typst-cli/Cargo.toml b/crates/typst-cli/Cargo.toml
index 6155c046..9a96c6b2 100644
--- a/crates/typst-cli/Cargo.toml
+++ b/crates/typst-cli/Cargo.toml
@@ -22,6 +22,7 @@ doc = false
[dependencies]
typst = { workspace = true }
typst-library = { workspace = true }
+typst-render = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true }
codespan-reporting = { workspace = true }
diff --git a/crates/typst-cli/src/compile.rs b/crates/typst-cli/src/compile.rs
index b5cf0e54..80e19f1b 100644
--- a/crates/typst-cli/src/compile.rs
+++ b/crates/typst-cli/src/compile.rs
@@ -220,7 +220,7 @@ fn export_image(
match fmt {
ImageExportFormat::Png => {
let pixmap =
- typst::export::render(frame, command.ppi / 72.0, Color::WHITE);
+ typst_render::render(frame, command.ppi / 72.0, Color::WHITE);
pixmap
.save_png(path)
.map_err(|err| eco_format!("failed to write PNG file ({err})"))?;
diff --git a/crates/typst-render/Cargo.toml b/crates/typst-render/Cargo.toml
new file mode 100644
index 00000000..0c552cd3
--- /dev/null
+++ b/crates/typst-render/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "typst-render"
+description = "Raster image exporter for Typst."
+version.workspace = true
+rust-version.workspace = true
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+repository.workspace = true
+license.workspace = true
+categories.workspace = true
+keywords.workspace = true
+
+[lib]
+doctest = false
+bench = false
+
+[dependencies]
+typst = { workspace = true }
+bytemuck = { workspace = true }
+comemo = { workspace = true }
+flate2 = { workspace = true }
+image = { workspace = true }
+pixglyph = { workspace = true }
+resvg = { workspace = true }
+roxmltree = { workspace = true }
+tiny-skia = { workspace = true }
+ttf-parser = { workspace = true }
+usvg = { workspace = true }
diff --git a/crates/typst/src/export/render.rs b/crates/typst-render/src/lib.rs
index 7a33849f..4cd00385 100644
--- a/crates/typst/src/export/render.rs
+++ b/crates/typst-render/src/lib.rs
@@ -9,15 +9,14 @@ use pixglyph::Bitmap;
use resvg::tiny_skia::IntRect;
use tiny_skia as sk;
use ttf_parser::{GlyphId, OutlineBuilder};
-use usvg::{NodeExt, TreeParsing};
-
-use crate::doc::{Frame, FrameItem, FrameKind, GroupItem, Meta, TextItem};
-use crate::font::Font;
-use crate::geom::{
+use typst::doc::{Frame, FrameItem, FrameKind, GroupItem, Meta, TextItem};
+use typst::font::Font;
+use typst::geom::{
self, Abs, Axes, Color, FixedStroke, Geometry, Gradient, LineCap, LineJoin, Paint,
PathItem, Point, Ratio, Relative, Shape, Size, Transform,
};
-use crate::image::{Image, ImageKind, RasterFormat};
+use typst::image::{Image, ImageKind, RasterFormat};
+use usvg::{NodeExt, TreeParsing};
/// Export a frame into a raster image.
///
@@ -29,7 +28,7 @@ pub fn render(frame: &Frame, pixel_per_pt: f32, fill: Color) -> sk::Pixmap {
let pxh = (pixel_per_pt * size.y.to_f32()).round().max(1.0) as u32;
let mut canvas = sk::Pixmap::new(pxw, pxh).unwrap();
- canvas.fill(fill.into());
+ canvas.fill(to_sk_color(fill));
let ts = sk::Transform::from_scale(pixel_per_pt, pixel_per_pt);
render_frame(&mut canvas, State::new(size, ts, pixel_per_pt), frame);
@@ -49,7 +48,7 @@ pub fn render_merged(
) -> sk::Pixmap {
let pixmaps: Vec<_> = frames
.iter()
- .map(|frame| typst::export::render(frame, pixel_per_pt, frame_fill))
+ .map(|frame| render(frame, pixel_per_pt, frame_fill))
.collect();
let padding = (pixel_per_pt * padding.to_f32()).round() as u32;
@@ -59,7 +58,7 @@ pub fn render_merged(
padding + pixmaps.iter().map(|pixmap| pixmap.height() + padding).sum::<u32>();
let mut canvas = sk::Pixmap::new(pxw, pxh).unwrap();
- canvas.fill(padding_fill.into());
+ canvas.fill(to_sk_color(padding_fill));
let [x, mut y] = [padding; 2];
for pixmap in pixmaps {
@@ -173,18 +172,19 @@ fn render_frame(canvas: &mut sk::Pixmap, state: State, frame: &Frame) {
/// Render a group frame with optional transform and clipping into the canvas.
fn render_group(canvas: &mut sk::Pixmap, state: State, pos: Point, group: &GroupItem) {
+ let sk_transform = to_sk_transform(&group.transform);
let state = match group.frame.kind() {
- FrameKind::Soft => state.pre_translate(pos).pre_concat(group.transform.into()),
+ FrameKind::Soft => state.pre_translate(pos).pre_concat(sk_transform),
FrameKind::Hard => state
.pre_translate(pos)
- .pre_concat(group.transform.into())
+ .pre_concat(sk_transform)
.pre_concat_container(
state
.transform
.post_concat(state.container_transform.invert().unwrap()),
)
- .pre_concat_container(Transform::translate(pos.x, pos.y).into())
- .pre_concat_container(group.transform.into())
+ .pre_concat_container(to_sk_transform(&Transform::translate(pos.x, pos.y)))
+ .pre_concat_container(sk_transform)
.with_size(group.frame.size()),
};
@@ -456,8 +456,7 @@ fn write_bitmap<S: PaintSampler>(
for x in 0..mw {
for y in 0..mh {
let alpha = bitmap.coverage[(y * mw + x) as usize];
- let color: sk::ColorU8 = sampler.sample((x, y)).into();
-
+ let color = to_sk_color_u8_without_alpha(sampler.sample((x, y)));
pixmap.pixels_mut()[((y + 1) * (mw + 2) + (x + 1)) as usize] =
sk::ColorU8::from_rgba(
color.red(),
@@ -502,8 +501,9 @@ fn write_bitmap<S: PaintSampler>(
continue;
}
- let color: sk::ColorU8 = sampler.sample((x as _, y as _)).into();
- let color = bytemuck::cast(color.premultiply());
+ let color = sampler.sample((x as _, y as _));
+ let color =
+ bytemuck::cast(to_sk_color_u8_without_alpha(color).premultiply());
let pi = (y * cw + x) as usize;
if cov == 255 {
pixels[pi] = color;
@@ -621,8 +621,8 @@ fn render_shape(canvas: &mut sk::Pixmap, state: State, shape: &Shape) -> Option<
);
let stroke = sk::Stroke {
width,
- line_cap: line_cap.into(),
- line_join: line_join.into(),
+ line_cap: to_sk_line_cap(*line_cap),
+ line_join: to_sk_line_join(*line_join),
dash,
miter_limit: miter_limit.get() as f32,
};
@@ -740,34 +740,6 @@ fn scaled_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
Some(Arc::new(pixmap))
}
-impl From<Transform> for sk::Transform {
- fn from(transform: Transform) -> Self {
- let Transform { sx, ky, kx, sy, tx, ty } = transform;
- sk::Transform::from_row(
- sx.get() as _,
- ky.get() as _,
- kx.get() as _,
- sy.get() as _,
- tx.to_f32(),
- ty.to_f32(),
- )
- }
-}
-
-impl From<sk::Transform> for Transform {
- fn from(value: sk::Transform) -> Self {
- let sk::Transform { sx, ky, kx, sy, tx, ty } = value;
- Self {
- sx: Ratio::new(sx as _),
- ky: Ratio::new(ky as _),
- kx: Ratio::new(kx as _),
- sy: Ratio::new(sy as _),
- tx: Abs::raw(tx as _),
- ty: Abs::raw(ty as _),
- }
- }
-}
-
/// Trait for sampling of a paint, used as a generic
/// abstraction over solid colors and gradients.
trait PaintSampler: Copy {
@@ -860,18 +832,16 @@ fn to_sk_paint<'a>(
let mut pixmap = sk::Pixmap::new(width.max(1), height.max(1)).unwrap();
for x in 0..width {
for y in 0..height {
- let color: sk::Color = gradient
- .sample_at(
- (
- (x as f32 + offset.x.to_f32()) * scale.x.get() as f32,
- (y as f32 + offset.y.to_f32()) * scale.y.get() as f32,
- ),
- (width as f32, height as f32),
- )
- .into();
+ let color = gradient.sample_at(
+ (
+ (x as f32 + offset.x.to_f32()) * scale.x.get() as f32,
+ (y as f32 + offset.y.to_f32()) * scale.y.get() as f32,
+ ),
+ (width as f32, height as f32),
+ );
pixmap.pixels_mut()[(y * width + x) as usize] =
- color.premultiply().to_color_u8();
+ to_sk_color(color).premultiply().to_color_u8();
}
}
@@ -881,7 +851,7 @@ fn to_sk_paint<'a>(
let mut sk_paint: sk::Paint<'_> = sk::Paint::default();
match paint {
Paint::Solid(color) => {
- sk_paint.set_color((*color).into());
+ sk_paint.set_color(to_sk_color(*color));
sk_paint.anti_alias = true;
}
Paint::Gradient(gradient) => {
@@ -925,33 +895,44 @@ fn to_sk_paint<'a>(
sk_paint
}
-impl From<Color> for sk::Color {
- fn from(color: Color) -> Self {
- let [r, g, b, a] = color.to_rgba().to_vec4_u8();
- sk::Color::from_rgba8(r, g, b, a)
- }
+fn to_sk_color(color: Color) -> sk::Color {
+ let [r, g, b, a] = color.to_rgba().to_vec4_u8();
+ sk::Color::from_rgba8(r, g, b, a)
}
-impl From<&LineCap> for sk::LineCap {
- fn from(line_cap: &LineCap) -> Self {
- match line_cap {
- LineCap::Butt => sk::LineCap::Butt,
- LineCap::Round => sk::LineCap::Round,
- LineCap::Square => sk::LineCap::Square,
- }
+fn to_sk_color_u8_without_alpha(color: Color) -> sk::ColorU8 {
+ let [r, g, b, _] = color.to_rgba().to_vec4_u8();
+ sk::ColorU8::from_rgba(r, g, b, 255)
+}
+
+fn to_sk_line_cap(cap: LineCap) -> sk::LineCap {
+ match cap {
+ LineCap::Butt => sk::LineCap::Butt,
+ LineCap::Round => sk::LineCap::Round,
+ LineCap::Square => sk::LineCap::Square,
}
}
-impl From<&LineJoin> for sk::LineJoin {
- fn from(line_join: &LineJoin) -> Self {
- match line_join {
- LineJoin::Miter => sk::LineJoin::Miter,
- LineJoin::Round => sk::LineJoin::Round,
- LineJoin::Bevel => sk::LineJoin::Bevel,
- }
+fn to_sk_line_join(join: LineJoin) -> sk::LineJoin {
+ match join {
+ LineJoin::Miter => sk::LineJoin::Miter,
+ LineJoin::Round => sk::LineJoin::Round,
+ LineJoin::Bevel => sk::LineJoin::Bevel,
}
}
+fn to_sk_transform(transform: &Transform) -> sk::Transform {
+ let Transform { sx, ky, kx, sy, tx, ty } = *transform;
+ sk::Transform::from_row(
+ sx.get() as _,
+ ky.get() as _,
+ kx.get() as _,
+ sy.get() as _,
+ tx.to_f32(),
+ ty.to_f32(),
+ )
+}
+
/// Allows to build tiny-skia paths from glyph outlines.
struct WrappedPathBuilder(sk::PathBuilder);
@@ -989,13 +970,6 @@ impl AbsExt for Abs {
}
}
-impl From<Color> for sk::ColorU8 {
- fn from(value: Color) -> Self {
- let [r, g, b, _] = value.to_rgba().to_vec4_u8();
- sk::ColorU8::from_rgba(r, g, b, 255)
- }
-}
-
// Alpha multiplication and blending are ported from:
// https://skia.googlesource.com/skia/+/refs/heads/main/include/core/SkColorPriv.h
diff --git a/crates/typst/Cargo.toml b/crates/typst/Cargo.toml
index 1e820a68..34063979 100644
--- a/crates/typst/Cargo.toml
+++ b/crates/typst/Cargo.toml
@@ -34,9 +34,7 @@ miniz_oxide = { workspace = true }
once_cell = { workspace = true }
palette = { workspace = true }
pdf-writer = { workspace = true }
-pixglyph = { workspace = true }
regex = { workspace = true }
-resvg = { workspace = true }
roxmltree = { workspace = true }
rustybuzz = { workspace = true }
serde = { workspace = true }
@@ -45,7 +43,6 @@ smallvec = { workspace = true }
subsetter = { workspace = true }
svg2pdf = { workspace = true }
time = { workspace = true }
-tiny-skia = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true }
ttf-parser = { workspace = true }
diff --git a/crates/typst/src/export/mod.rs b/crates/typst/src/export/mod.rs
index 4f7f8a70..ef24c97b 100644
--- a/crates/typst/src/export/mod.rs
+++ b/crates/typst/src/export/mod.rs
@@ -1,9 +1,7 @@
//! Exporting into external formats.
mod pdf;
-mod render;
mod svg;
pub use self::pdf::{pdf, PdfPageLabel, PdfPageLabelStyle};
-pub use self::render::{render, render_merged};
pub use self::svg::{svg, svg_merged};
diff --git a/tests/Cargo.toml b/tests/Cargo.toml
index a5a18a5b..023776ea 100644
--- a/tests/Cargo.toml
+++ b/tests/Cargo.toml
@@ -9,6 +9,7 @@ publish = false
[dev-dependencies]
typst = { workspace = true }
typst-library = { workspace = true }
+typst-render = { workspace = true }
clap = { workspace = true }
comemo = { workspace = true }
ecow = { workspace = true }
diff --git a/tests/src/benches.rs b/tests/src/benches.rs
index 8d74ca9c..666bb8a0 100644
--- a/tests/src/benches.rs
+++ b/tests/src/benches.rs
@@ -90,7 +90,7 @@ fn bench_render(iai: &mut Iai) {
let world = BenchWorld::new();
let mut tracer = Tracer::new();
let document = typst::compile(&world, &mut tracer).unwrap();
- iai.run(|| typst::export::render(&document.pages[0], 1.0, Color::WHITE))
+ iai.run(|| typst_render::render(&document.pages[0], 1.0, Color::WHITE))
}
struct BenchWorld {
diff --git a/tests/src/tests.rs b/tests/src/tests.rs
index d974b731..07dba177 100644
--- a/tests/src/tests.rs
+++ b/tests/src/tests.rs
@@ -26,7 +26,7 @@ use typst::eval::{
eco_format, func, Bytes, Datetime, Library, NoneValue, Repr, Tracer, Value,
};
use typst::font::{Font, FontBook};
-use typst::geom::{Abs, Color, Smart};
+use typst::geom::{Abs, Color, Smart, Transform};
use typst::syntax::{FileId, PackageVersion, Source, SyntaxNode, VirtualPath};
use typst::{World, WorldExt};
use typst_library::layout::{Margin, PageElem};
@@ -906,7 +906,7 @@ fn render(frames: &[Frame]) -> sk::Pixmap {
}
}
- let mut pixmap = typst::export::render_merged(
+ let mut pixmap = typst_render::render_merged(
frames,
pixel_per_pt,
Color::WHITE,
@@ -932,7 +932,7 @@ fn render_links(canvas: &mut sk::Pixmap, ts: sk::Transform, frame: &Frame) {
let ts = ts.pre_translate(pos.x.to_pt() as f32, pos.y.to_pt() as f32);
match *item {
FrameItem::Group(ref group) => {
- let ts = ts.pre_concat(group.transform.into());
+ let ts = ts.pre_concat(to_sk_transform(&group.transform));
render_links(canvas, ts, &group.frame);
}
FrameItem::Meta(Meta::Link(_), size) => {
@@ -948,6 +948,18 @@ fn render_links(canvas: &mut sk::Pixmap, ts: sk::Transform, frame: &Frame) {
}
}
+fn to_sk_transform(transform: &Transform) -> sk::Transform {
+ let Transform { sx, ky, kx, sy, tx, ty } = *transform;
+ sk::Transform::from_row(
+ sx.get() as _,
+ ky.get() as _,
+ kx.get() as _,
+ sy.get() as _,
+ tx.to_pt() as f32,
+ ty.to_pt() as f32,
+ )
+}
+
/// A Linear-feedback shift register using XOR as its shifting function.
/// Can be used as PRNG.
struct LinearShift(u64);