mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Refactor SVG export a bit
This commit is contained in:
parent
61e4ad6bba
commit
2ea451b83b
@ -95,19 +95,14 @@ pub fn compile_once(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
enum ExportImageFormat {
|
||||
Png,
|
||||
Svg,
|
||||
}
|
||||
|
||||
/// Export into the target format.
|
||||
fn export(document: &Document, command: &CompileCommand) -> StrResult<()> {
|
||||
match command.output().extension() {
|
||||
Some(ext) if ext.eq_ignore_ascii_case("png") => {
|
||||
export_image(document, command, ExportImageFormat::Png)
|
||||
export_image(document, command, ImageExportFormat::Png)
|
||||
}
|
||||
Some(ext) if ext.eq_ignore_ascii_case("svg") => {
|
||||
export_image(document, command, ExportImageFormat::Svg)
|
||||
export_image(document, command, ImageExportFormat::Svg)
|
||||
}
|
||||
_ => export_pdf(document, command),
|
||||
}
|
||||
@ -121,18 +116,24 @@ fn export_pdf(document: &Document, command: &CompileCommand) -> StrResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An image format to export in.
|
||||
enum ImageExportFormat {
|
||||
Png,
|
||||
Svg,
|
||||
}
|
||||
|
||||
/// Export to one or multiple PNGs.
|
||||
fn export_image(
|
||||
document: &Document,
|
||||
command: &CompileCommand,
|
||||
fmt: ExportImageFormat,
|
||||
fmt: ImageExportFormat,
|
||||
) -> StrResult<()> {
|
||||
// Determine whether we have a `{n}` numbering.
|
||||
let output = command.output();
|
||||
let string = output.to_str().unwrap_or_default();
|
||||
let numbered = string.contains("{n}");
|
||||
if !numbered && document.pages.len() > 1 {
|
||||
bail!("cannot export multiple PNGs without `{{n}}` in output path");
|
||||
bail!("cannot export multiple images without `{{n}}` in output path");
|
||||
}
|
||||
|
||||
// Find a number width that accommodates all pages. For instance, the
|
||||
@ -149,13 +150,13 @@ fn export_image(
|
||||
output.as_path()
|
||||
};
|
||||
match fmt {
|
||||
ExportImageFormat::Png => {
|
||||
ImageExportFormat::Png => {
|
||||
let pixmap =
|
||||
typst::export::render(frame, command.ppi / 72.0, Color::WHITE);
|
||||
pixmap.save_png(path).map_err(|_| "failed to write PNG file")?;
|
||||
}
|
||||
ExportImageFormat::Svg => {
|
||||
let svg = typst::export::svg_frame(frame);
|
||||
ImageExportFormat::Svg => {
|
||||
let svg = typst::export::svg(frame);
|
||||
fs::write(path, svg).map_err(|_| "failed to write SVG file")?;
|
||||
}
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ mod render;
|
||||
mod svg;
|
||||
|
||||
pub use self::pdf::pdf;
|
||||
pub use self::render::render;
|
||||
pub use self::svg::{svg, svg_frame};
|
||||
pub use self::render::{render, render_merged};
|
||||
pub use self::svg::{svg, svg_merged};
|
||||
|
@ -37,6 +37,47 @@ pub fn render(frame: &Frame, pixel_per_pt: f32, fill: Color) -> sk::Pixmap {
|
||||
canvas
|
||||
}
|
||||
|
||||
/// Export multiple frames into a single raster image.
|
||||
///
|
||||
/// The padding will be added around and between the individual frames.
|
||||
pub fn render_merged(
|
||||
frames: &[Frame],
|
||||
pixel_per_pt: f32,
|
||||
frame_fill: Color,
|
||||
padding: Abs,
|
||||
padding_fill: Color,
|
||||
) -> sk::Pixmap {
|
||||
let pixmaps: Vec<_> = frames
|
||||
.iter()
|
||||
.map(|frame| typst::export::render(frame, pixel_per_pt, frame_fill))
|
||||
.collect();
|
||||
|
||||
let padding = (pixel_per_pt * padding.to_f32()).round() as u32;
|
||||
let pxw =
|
||||
2 * padding + pixmaps.iter().map(sk::Pixmap::width).max().unwrap_or_default();
|
||||
let pxh =
|
||||
padding + pixmaps.iter().map(|pixmap| pixmap.height() + padding).sum::<u32>();
|
||||
|
||||
let mut canvas = sk::Pixmap::new(pxw, pxh).unwrap();
|
||||
canvas.fill(padding_fill.into());
|
||||
|
||||
let [x, mut y] = [padding; 2];
|
||||
for pixmap in pixmaps {
|
||||
canvas.draw_pixmap(
|
||||
x as i32,
|
||||
y as i32,
|
||||
pixmap.as_ref(),
|
||||
&sk::PixmapPaint::default(),
|
||||
sk::Transform::identity(),
|
||||
None,
|
||||
);
|
||||
|
||||
y += pixmap.height() + padding;
|
||||
}
|
||||
|
||||
canvas
|
||||
}
|
||||
|
||||
/// Render a frame into the canvas.
|
||||
fn render_frame(
|
||||
canvas: &mut sk::Pixmap,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -429,7 +429,7 @@ fn test(
|
||||
fs::create_dir_all(png_path.parent().unwrap()).unwrap();
|
||||
canvas.save_png(png_path).unwrap();
|
||||
|
||||
let svg = typst::export::svg(&document);
|
||||
let svg = typst::export::svg_merged(&document.pages, Abs::pt(5.0));
|
||||
fs::create_dir_all(svg_path.parent().unwrap()).unwrap();
|
||||
std::fs::write(svg_path, svg).unwrap();
|
||||
|
||||
@ -898,42 +898,33 @@ fn test_spans_impl(output: &mut String, node: &SyntaxNode, within: Range<u64>) -
|
||||
/// Draw all frames into one image with padding in between.
|
||||
fn render(frames: &[Frame]) -> sk::Pixmap {
|
||||
let pixel_per_pt = 2.0;
|
||||
let pixmaps: Vec<_> = frames
|
||||
.iter()
|
||||
.map(|frame| {
|
||||
let limit = Abs::cm(100.0);
|
||||
if frame.width() > limit || frame.height() > limit {
|
||||
panic!("overlarge frame: {:?}", frame.size());
|
||||
}
|
||||
typst::export::render(frame, pixel_per_pt, Color::WHITE)
|
||||
})
|
||||
.collect();
|
||||
let padding = Abs::pt(5.0);
|
||||
|
||||
let pad = (5.0 * pixel_per_pt).round() as u32;
|
||||
let pxw = 2 * pad + pixmaps.iter().map(sk::Pixmap::width).max().unwrap_or_default();
|
||||
let pxh = pad + pixmaps.iter().map(|pixmap| pixmap.height() + pad).sum::<u32>();
|
||||
|
||||
let mut canvas = sk::Pixmap::new(pxw, pxh).unwrap();
|
||||
canvas.fill(sk::Color::BLACK);
|
||||
|
||||
let [x, mut y] = [pad; 2];
|
||||
for (frame, mut pixmap) in frames.iter().zip(pixmaps) {
|
||||
let ts = sk::Transform::from_scale(pixel_per_pt, pixel_per_pt);
|
||||
render_links(&mut pixmap, ts, frame);
|
||||
|
||||
canvas.draw_pixmap(
|
||||
x as i32,
|
||||
y as i32,
|
||||
pixmap.as_ref(),
|
||||
&sk::PixmapPaint::default(),
|
||||
sk::Transform::identity(),
|
||||
None,
|
||||
);
|
||||
|
||||
y += pixmap.height() + pad;
|
||||
for frame in frames {
|
||||
let limit = Abs::cm(100.0);
|
||||
if frame.width() > limit || frame.height() > limit {
|
||||
panic!("overlarge frame: {:?}", frame.size());
|
||||
}
|
||||
}
|
||||
|
||||
canvas
|
||||
let mut pixmap = typst::export::render_merged(
|
||||
frames,
|
||||
pixel_per_pt,
|
||||
Color::WHITE,
|
||||
padding,
|
||||
Color::BLACK,
|
||||
);
|
||||
|
||||
let padding = (pixel_per_pt * padding.to_pt() as f32).round();
|
||||
let [x, mut y] = [padding; 2];
|
||||
for frame in frames {
|
||||
let ts =
|
||||
sk::Transform::from_scale(pixel_per_pt, pixel_per_pt).post_translate(x, y);
|
||||
render_links(&mut pixmap, ts, frame);
|
||||
y += (pixel_per_pt * frame.height().to_pt() as f32).round().max(1.0) + padding;
|
||||
}
|
||||
|
||||
pixmap
|
||||
}
|
||||
|
||||
/// Draw extra boxes for links so we can see whether they are there.
|
||||
|
Loading…
x
Reference in New Issue
Block a user