summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml4
-rw-r--r--src/library/insert.rs8
-rw-r--r--tests/ref/coma.pngbin77574 -> 79107 bytes
-rw-r--r--tests/ref/image.pngbin957679 -> 952426 bytes
-rw-r--r--tests/typeset.rs149
5 files changed, 85 insertions, 76 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 11a2ed57..f92114d8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,12 +18,14 @@ unicode-xid = "0.2"
# feature = "serde"
serde = { version = "1", features = ["derive"], optional = true }
+
+# for the CLI
anyhow = { version = "1", optional = true }
[dev-dependencies]
criterion = "0.3"
memmap = "0.7"
-raqote = { version = "0.8", default-features = false }
+tiny-skia = "0.2"
[profile.dev]
opt-level = 2
diff --git a/src/library/insert.rs b/src/library/insert.rs
index db1e9e17..9fe719c7 100644
--- a/src/library/insert.rs
+++ b/src/library/insert.rs
@@ -12,6 +12,8 @@ use crate::prelude::*;
///
/// # Positional arguments
/// - The path to the image (string)
+///
+/// Supports PNG and JPEG files.
pub fn image(mut args: Args, ctx: &mut EvalContext) -> Value {
let path = args.need::<_, Spanned<String>>(ctx, 0, "path");
let width = args.get::<_, Linear>(ctx, "width");
@@ -92,7 +94,11 @@ impl Layout for Image {
impl Debug for Image {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad("Image")
+ f.debug_struct("Image")
+ .field("width", &self.width)
+ .field("height", &self.height)
+ .field("align", &self.align)
+ .finish()
}
}
diff --git a/tests/ref/coma.png b/tests/ref/coma.png
index f84423ee..d0c524ec 100644
--- a/tests/ref/coma.png
+++ b/tests/ref/coma.png
Binary files differ
diff --git a/tests/ref/image.png b/tests/ref/image.png
index d532d4e9..932f0c8b 100644
--- a/tests/ref/image.png
+++ b/tests/ref/image.png
Binary files differ
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 7c628879..38651766 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -7,7 +7,10 @@ use std::rc::Rc;
use fontdock::fs::{FsIndex, FsSource};
use memmap::Mmap;
-use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
+use tiny_skia::{
+ Canvas, Color, ColorU8, FillRule, FilterQuality, Paint, PathBuilder, Pattern, Pixmap,
+ Rect, SpreadMode, Transform,
+};
use ttf_parser::OutlineBuilder;
use typst::diag::{Feedback, Pass};
@@ -26,9 +29,6 @@ const PDF_DIR: &str = "pdf";
const PNG_DIR: &str = "png";
const REF_DIR: &str = "ref";
-const BLACK: SolidSource = SolidSource { r: 0, g: 0, b: 0, a: 255 };
-const WHITE: SolidSource = SolidSource { r: 255, g: 255, b: 255, a: 255 };
-
fn main() {
env::set_current_dir(env::current_dir().unwrap().join("tests")).unwrap();
@@ -133,8 +133,8 @@ fn test(src_path: &Path, pdf_path: &Path, png_path: &Path, loader: &SharedFontLo
let loader = loader.borrow();
- let surface = render(&layouts, &loader, 2.0);
- surface.write_png(png_path).unwrap();
+ let canvas = draw(&layouts, &loader, 2.0);
+ canvas.pixmap.save_png(png_path).unwrap();
let pdf_data = pdf::export(&layouts, &loader);
fs::write(pdf_path, pdf_data).unwrap();
@@ -170,106 +170,107 @@ impl TestFilter {
}
}
-fn render(layouts: &[BoxLayout], loader: &FontLoader, scale: f64) -> DrawTarget {
- let pad = Length::pt(scale * 10.0);
+fn draw(layouts: &[BoxLayout], loader: &FontLoader, pixel_per_pt: f32) -> Canvas {
+ let pad = Length::pt(5.0);
+
+ let height = pad + layouts.iter().map(|l| l.size.height + pad).sum::<Length>();
let width = 2.0 * pad
+ layouts
.iter()
- .map(|l| scale * l.size.width)
+ .map(|l| l.size.width)
.max_by(|a, b| a.partial_cmp(&b).unwrap())
.unwrap();
- let height =
- pad + layouts.iter().map(|l| scale * l.size.height + pad).sum::<Length>();
+ let pixel_width = (pixel_per_pt * width.to_pt() as f32) as u32;
+ let pixel_height = (pixel_per_pt * height.to_pt() as f32) as u32;
+ let mut canvas = Canvas::new(pixel_width, pixel_height).unwrap();
+ canvas.scale(pixel_per_pt, pixel_per_pt);
+ canvas.pixmap.fill(Color::BLACK);
- let int_width = width.to_pt().round() as i32;
- let int_height = height.to_pt().round() as i32;
- let mut surface = DrawTarget::new(int_width, int_height);
- surface.clear(BLACK);
-
- let mut offset = Point::new(pad, pad);
+ let mut origin = Point::new(pad, pad);
for layout in layouts {
- surface.fill_rect(
- offset.x.to_pt() as f32,
- offset.y.to_pt() as f32,
- (scale * layout.size.width).to_pt() as f32,
- (scale * layout.size.height).to_pt() as f32,
- &Source::Solid(WHITE),
- &Default::default(),
+ let mut paint = Paint::default();
+ paint.set_color(Color::WHITE);
+
+ canvas.fill_rect(
+ Rect::from_xywh(
+ origin.x.to_pt() as f32,
+ origin.y.to_pt() as f32,
+ layout.size.width.to_pt() as f32,
+ layout.size.height.to_pt() as f32,
+ )
+ .unwrap(),
+ &paint,
);
for &(pos, ref element) in &layout.elements {
- let pos = scale * pos + offset;
-
+ let pos = origin + pos;
match element {
LayoutElement::Text(shaped) => {
- render_shaped(&mut surface, loader, shaped, pos, scale)
- }
- LayoutElement::Image(image) => {
- render_image(&mut surface, image, pos, scale)
+ draw_text(&mut canvas, loader, shaped, pos)
}
+ LayoutElement::Image(image) => draw_image(&mut canvas, image, pos),
}
}
- offset.y += scale * layout.size.height + pad;
+ origin.y += layout.size.height + pad;
}
- surface
+ canvas
}
-fn render_shaped(
- surface: &mut DrawTarget,
- loader: &FontLoader,
- shaped: &Shaped,
- pos: Point,
- scale: f64,
-) {
+fn draw_text(canvas: &mut Canvas, loader: &FontLoader, shaped: &Shaped, pos: Point) {
let face = loader.get_loaded(shaped.face).get();
for (&glyph, &offset) in shaped.glyphs.iter().zip(&shaped.offsets) {
+ let units_per_em = face.units_per_em().unwrap_or(1000);
+
+ let x = (pos.x + offset).to_pt() as f32;
+ let y = (pos.y + shaped.font_size).to_pt() as f32;
+ let scale = (shaped.font_size / units_per_em as f64).to_pt() as f32;
+
let mut builder = WrappedPathBuilder(PathBuilder::new());
face.outline_glyph(glyph, &mut builder);
- let path = builder.0.finish();
- let units_per_em = face.units_per_em().unwrap_or(1000);
- let s = scale * (shaped.font_size / units_per_em as f64);
- let x = pos.x + scale * offset;
- let y = pos.y + scale * shaped.font_size;
-
- let t = Transform::create_scale(s.to_pt() as f32, -s.to_pt() as f32)
- .post_translate(Vector::new(x.to_pt() as f32, y.to_pt() as f32));
-
- surface.fill(
- &path.transform(&t),
- &Source::Solid(SolidSource { r: 0, g: 0, b: 0, a: 255 }),
- &Default::default(),
- )
+ let path = builder.0.finish().unwrap();
+ let placed = path
+ .transform(&Transform::from_row(scale, 0.0, 0.0, -scale, x, y).unwrap())
+ .unwrap();
+
+ let mut paint = Paint::default();
+ paint.anti_alias = true;
+
+ canvas.fill_path(&placed, &paint, FillRule::default());
}
}
-fn render_image(surface: &mut DrawTarget, image: &ImageElement, pos: Point, scale: f64) {
- let mut data = vec![];
- for pixel in image.buf.pixels() {
- let [r, g, b, a] = pixel.0;
- data.push(
- ((a as u32) << 24)
- | ((r as u32) << 16)
- | ((g as u32) << 8)
- | ((b as u32) << 0),
- );
+fn draw_image(canvas: &mut Canvas, image: &ImageElement, pos: Point) {
+ let mut pixmap = Pixmap::new(image.buf.width(), image.buf.height()).unwrap();
+ for (src, dest) in image.buf.pixels().zip(pixmap.pixels_mut()) {
+ let [r, g, b, a] = src.0;
+ *dest = ColorU8::from_rgba(r, g, b, a).premultiply();
}
- surface.draw_image_with_size_at(
- (scale * image.size.width.to_pt()) as f32,
- (scale * image.size.height.to_pt()) as f32,
- pos.x.to_pt() as f32,
- pos.y.to_pt() as f32,
- &raqote::Image {
- width: image.buf.dimensions().0 as i32,
- height: image.buf.dimensions().1 as i32,
- data: &data,
- },
- &Default::default(),
+ let view_width = image.size.width.to_pt() as f32;
+ let view_height = image.size.height.to_pt() as f32;
+
+ let x = pos.x.to_pt() as f32;
+ let y = pos.y.to_pt() as f32;
+ let scale_x = view_width as f32 / pixmap.width() as f32;
+ let scale_y = view_height as f32 / pixmap.height() as f32;
+
+ let mut paint = Paint::default();
+ paint.shader = Pattern::new(
+ &pixmap,
+ SpreadMode::Pad,
+ FilterQuality::Bilinear,
+ 1.0,
+ Transform::from_row(scale_x, 0.0, 0.0, scale_y, x, y).unwrap(),
+ );
+
+ canvas.fill_rect(
+ Rect::from_xywh(x, y, view_width, view_height).unwrap(),
+ &paint,
);
}