2021-01-17 15:53:22 +03:00
use std ::cell ::RefCell ;
2020-08-02 22:17:42 +03:00
use std ::env ;
use std ::ffi ::OsStr ;
2020-12-11 00:44:35 +03:00
use std ::fs ;
2020-08-17 17:18:55 +03:00
use std ::path ::Path ;
2021-01-17 15:53:22 +03:00
use std ::rc ::Rc ;
2020-08-02 22:17:42 +03:00
2021-01-15 18:53:02 +03:00
use fontdock ::fs ::FsIndex ;
2020-12-01 00:07:08 +03:00
use image ::{ GenericImageView , Rgba } ;
2020-11-25 20:46:47 +03:00
use tiny_skia ::{
Canvas , Color , ColorU8 , FillRule , FilterQuality , Paint , PathBuilder , Pattern , Pixmap ,
Rect , SpreadMode , Transform ,
} ;
2020-08-02 22:17:42 +03:00
use ttf_parser ::OutlineBuilder ;
2021-01-14 18:47:29 +03:00
use walkdir ::WalkDir ;
2020-08-02 22:17:42 +03:00
2021-02-11 21:26:47 +03:00
use typst ::diag ::{ Diag , DiagSet , Level , Pass } ;
2021-03-19 19:57:31 +03:00
use typst ::env ::{ Env , FsIndexExt , ImageResource , ResourceLoader } ;
use typst ::eval ::{ EvalContext , FuncArgs , FuncValue , Scope , Value } ;
2021-02-09 21:46:57 +03:00
use typst ::exec ::State ;
2020-10-13 12:47:29 +03:00
use typst ::export ::pdf ;
2021-03-11 12:48:29 +03:00
use typst ::geom ::{ Length , Point , Sides , Size } ;
2021-03-19 19:57:31 +03:00
use typst ::layout ::{ Element , Fill , Frame , Geometry , Image , Shape , Shaped } ;
2021-01-15 18:53:02 +03:00
use typst ::library ;
2020-12-11 00:44:35 +03:00
use typst ::parse ::{ LineMap , Scanner } ;
2021-03-19 19:57:31 +03:00
use typst ::pretty ::pretty ;
2021-02-11 21:26:47 +03:00
use typst ::syntax ::{ Location , Pos } ;
2020-10-13 12:47:29 +03:00
use typst ::typeset ;
2020-08-02 22:17:42 +03:00
2021-02-20 19:53:40 +03:00
const TYP_DIR : & str = " ./typ " ;
const REF_DIR : & str = " ./ref " ;
const PNG_DIR : & str = " ./png " ;
const PDF_DIR : & str = " ./pdf " ;
2020-12-11 00:44:35 +03:00
const FONT_DIR : & str = " ../fonts " ;
2020-08-02 22:17:42 +03:00
fn main ( ) {
2020-11-20 18:36:22 +03:00
env ::set_current_dir ( env ::current_dir ( ) . unwrap ( ) . join ( " tests " ) ) . unwrap ( ) ;
2021-03-13 16:18:31 +03:00
let args = Args ::new ( env ::args ( ) . skip ( 1 ) ) ;
2020-08-02 22:17:42 +03:00
let mut filtered = Vec ::new ( ) ;
2021-01-16 17:28:03 +03:00
for entry in WalkDir ::new ( " . " ) . into_iter ( ) {
2021-01-14 18:47:29 +03:00
let entry = entry . unwrap ( ) ;
2021-01-16 17:28:03 +03:00
if entry . depth ( ) < = 1 {
continue ;
}
2021-01-14 18:47:29 +03:00
let src_path = entry . into_path ( ) ;
2020-10-13 13:34:11 +03:00
if src_path . extension ( ) ! = Some ( OsStr ::new ( " typ " ) ) {
2020-08-02 22:17:42 +03:00
continue ;
}
2021-03-13 16:18:31 +03:00
if args . matches ( & src_path . to_string_lossy ( ) ) {
2021-01-14 18:47:29 +03:00
filtered . push ( src_path ) ;
2020-08-02 22:17:42 +03:00
}
}
let len = filtered . len ( ) ;
2021-01-13 23:33:22 +03:00
if len = = 1 {
2020-08-02 22:17:42 +03:00
println! ( " Running test ... " ) ;
2021-01-13 23:33:22 +03:00
} else if len > 1 {
2020-08-02 22:17:42 +03:00
println! ( " Running {} tests " , len ) ;
}
let mut index = FsIndex ::new ( ) ;
index . search_dir ( FONT_DIR ) ;
2021-01-15 18:53:02 +03:00
let mut env = Env {
fonts : index . into_dynamic_loader ( ) ,
2020-11-28 00:35:42 +03:00
resources : ResourceLoader ::new ( ) ,
2021-01-15 18:53:02 +03:00
} ;
2020-08-02 22:17:42 +03:00
2021-01-13 23:33:22 +03:00
let mut ok = true ;
2021-01-14 18:47:29 +03:00
for src_path in filtered {
2021-03-13 16:18:31 +03:00
let path = src_path . strip_prefix ( TYP_DIR ) . unwrap ( ) ;
let png_path = Path ::new ( PNG_DIR ) . join ( path ) . with_extension ( " png " ) ;
let ref_path = Path ::new ( REF_DIR ) . join ( path ) . with_extension ( " png " ) ;
let pdf_path =
args . pdf . then ( | | Path ::new ( PDF_DIR ) . join ( path ) . with_extension ( " pdf " ) ) ;
ok & = test (
& mut env ,
& src_path ,
& png_path ,
& ref_path ,
pdf_path . as_deref ( ) ,
) ;
2021-01-13 16:07:38 +03:00
}
2020-10-13 13:34:11 +03:00
if ! ok {
std ::process ::exit ( 1 ) ;
}
}
2020-08-02 22:17:42 +03:00
2021-03-13 16:18:31 +03:00
struct Args {
2020-08-02 22:17:42 +03:00
filter : Vec < String > ,
2021-03-13 16:18:31 +03:00
pdf : bool ,
2020-08-02 22:17:42 +03:00
perfect : bool ,
}
2021-03-13 16:18:31 +03:00
impl Args {
2020-08-03 17:01:23 +03:00
fn new ( args : impl Iterator < Item = String > ) -> Self {
2020-08-02 22:17:42 +03:00
let mut filter = Vec ::new ( ) ;
let mut perfect = false ;
2021-03-13 16:18:31 +03:00
let mut pdf = false ;
2020-08-02 22:17:42 +03:00
for arg in args {
match arg . as_str ( ) {
2020-08-03 17:01:23 +03:00
" --nocapture " = > { }
2021-03-13 16:18:31 +03:00
" --pdf " = > pdf = true ,
2020-08-02 22:17:42 +03:00
" = " = > perfect = true ,
_ = > filter . push ( arg ) ,
}
}
2021-03-13 16:18:31 +03:00
Self { filter , pdf , perfect }
2020-08-02 22:17:42 +03:00
}
fn matches ( & self , name : & str ) -> bool {
if self . perfect {
self . filter . iter ( ) . any ( | p | name = = p )
} else {
2020-08-30 23:18:55 +03:00
self . filter . is_empty ( ) | | self . filter . iter ( ) . any ( | p | name . contains ( p ) )
2020-08-02 22:17:42 +03:00
}
}
}
2020-12-11 00:44:35 +03:00
fn test (
2021-03-13 16:18:31 +03:00
env : & mut Env ,
2020-12-11 00:44:35 +03:00
src_path : & Path ,
png_path : & Path ,
2021-02-20 19:53:40 +03:00
ref_path : & Path ,
2021-03-13 16:18:31 +03:00
pdf_path : Option < & Path > ,
2020-12-11 00:44:35 +03:00
) -> bool {
2021-01-14 18:47:29 +03:00
let name = src_path . strip_prefix ( TYP_DIR ) . unwrap_or ( src_path ) ;
println! ( " Testing {} " , name . display ( ) ) ;
2020-12-11 00:44:35 +03:00
let src = fs ::read_to_string ( src_path ) . unwrap ( ) ;
2021-01-13 16:07:38 +03:00
let mut ok = true ;
let mut frames = vec! [ ] ;
2021-01-22 19:16:42 +03:00
let mut lines = 0 ;
2021-02-01 00:43:11 +03:00
let mut compare_ref = true ;
2021-03-12 15:57:47 +03:00
let mut compare_ever = false ;
2021-02-01 00:43:11 +03:00
let parts : Vec < _ > = src . split ( " --- " ) . collect ( ) ;
for ( i , part ) in parts . iter ( ) . enumerate ( ) {
let is_header = i = = 0
& & parts . len ( ) > 1
& & part
. lines ( )
. all ( | s | s . starts_with ( " // " ) | | s . chars ( ) . all ( | c | c . is_whitespace ( ) ) ) ;
if is_header {
for line in part . lines ( ) {
if line . starts_with ( " // Ref: false " ) {
compare_ref = false ;
}
}
} else {
2021-03-12 15:57:47 +03:00
let ( part_ok , compare_here , part_frames ) =
2021-03-13 16:18:31 +03:00
test_part ( env , part , i , compare_ref , lines ) ;
2021-02-01 00:43:11 +03:00
ok & = part_ok ;
2021-03-12 15:57:47 +03:00
compare_ever | = compare_here ;
2021-02-01 00:43:11 +03:00
frames . extend ( part_frames ) ;
}
2021-01-22 19:16:42 +03:00
lines + = part . lines ( ) . count ( ) as u32 ;
2021-01-13 16:07:38 +03:00
}
2021-03-12 15:57:47 +03:00
if compare_ever {
2021-03-13 16:18:31 +03:00
if let Some ( pdf_path ) = pdf_path {
let pdf_data = pdf ::export ( & env , & frames ) ;
fs ::create_dir_all ( & pdf_path . parent ( ) . unwrap ( ) ) . unwrap ( ) ;
fs ::write ( pdf_path , pdf_data ) . unwrap ( ) ;
}
2021-01-13 16:07:38 +03:00
2021-02-11 23:22:06 +03:00
let canvas = draw ( & env , & frames , 2.0 ) ;
2021-01-14 18:47:29 +03:00
fs ::create_dir_all ( & png_path . parent ( ) . unwrap ( ) ) . unwrap ( ) ;
2021-01-14 01:19:44 +03:00
canvas . pixmap . save_png ( png_path ) . unwrap ( ) ;
2021-02-20 19:53:40 +03:00
if let Ok ( ref_pixmap ) = Pixmap ::load_png ( ref_path ) {
if canvas . pixmap ! = ref_pixmap {
println! ( " Does not match reference image. ❌ " ) ;
2021-01-13 16:07:38 +03:00
ok = false ;
}
2021-02-20 19:53:40 +03:00
} else {
println! ( " Failed to open reference image. ❌ " ) ;
ok = false ;
2021-01-13 16:07:38 +03:00
}
}
if ok {
2021-01-14 18:47:29 +03:00
println! ( " \x1b [1ATesting {} ✔ " , name . display ( ) ) ;
2021-01-13 16:07:38 +03:00
}
ok
}
2021-02-01 00:43:11 +03:00
fn test_part (
2021-03-13 16:18:31 +03:00
env : & mut Env ,
2021-02-01 00:43:11 +03:00
src : & str ,
i : usize ,
compare_ref : bool ,
lines : u32 ,
2021-03-12 15:57:47 +03:00
) -> ( bool , bool , Vec < Frame > ) {
2021-01-13 19:22:33 +03:00
let map = LineMap ::new ( src ) ;
2021-02-01 00:43:11 +03:00
let ( local_compare_ref , ref_diags ) = parse_metadata ( src , & map ) ;
let compare_ref = local_compare_ref . unwrap_or ( compare_ref ) ;
2020-12-11 00:44:35 +03:00
2021-01-15 18:53:02 +03:00
let mut scope = library ::new ( ) ;
2021-01-26 22:49:45 +03:00
let panics = Rc ::new ( RefCell ::new ( vec! [ ] ) ) ;
register_helpers ( & mut scope , Rc ::clone ( & panics ) ) ;
2021-01-14 01:19:44 +03:00
// We want to have "unbounded" pages, so we allow them to be infinitely
// large and fit them to match their content.
2021-01-15 18:53:02 +03:00
let mut state = State ::default ( ) ;
2021-01-14 01:19:44 +03:00
state . page . size = Size ::new ( Length ::pt ( 120.0 ) , Length ::raw ( f64 ::INFINITY ) ) ;
2020-12-11 00:44:35 +03:00
state . page . margins = Sides ::uniform ( Some ( Length ::pt ( 10.0 ) . into ( ) ) ) ;
2021-01-13 17:44:41 +03:00
2021-02-11 21:26:47 +03:00
let Pass { output : mut frames , diags } = typeset ( env , & src , & scope , state ) ;
2021-01-13 16:07:38 +03:00
if ! compare_ref {
frames . clear ( ) ;
}
2020-12-11 00:44:35 +03:00
let mut ok = true ;
2021-01-26 22:49:45 +03:00
for panic in & * panics . borrow ( ) {
let line = map . location ( panic . pos ) . unwrap ( ) . line ;
println! ( " Assertion failed in line {} ❌ " , lines + line ) ;
if let ( Some ( lhs ) , Some ( rhs ) ) = ( & panic . lhs , & panic . rhs ) {
println! ( " Left: {:?} " , lhs ) ;
println! ( " Right: {:?} " , rhs ) ;
} else {
println! ( " Missing argument. " ) ;
}
2021-01-17 15:53:22 +03:00
ok = false ;
}
2020-12-11 00:44:35 +03:00
if diags ! = ref_diags {
2021-01-13 16:07:38 +03:00
println! ( " Subtest {} does not match expected diagnostics. ❌ " , i ) ;
2020-12-11 00:44:35 +03:00
ok = false ;
for diag in & diags {
2021-01-13 18:37:18 +03:00
if ! ref_diags . contains ( diag ) {
2021-01-14 19:41:13 +03:00
print! ( " Not annotated | " ) ;
2021-01-22 19:16:42 +03:00
print_diag ( diag , & map , lines ) ;
2020-12-11 00:44:35 +03:00
}
}
for diag in & ref_diags {
2021-01-13 18:37:18 +03:00
if ! diags . contains ( diag ) {
2021-01-14 19:41:13 +03:00
print! ( " Not emitted | " ) ;
2021-01-22 19:16:42 +03:00
print_diag ( diag , & map , lines ) ;
2020-12-11 00:44:35 +03:00
}
}
}
2021-03-12 15:57:47 +03:00
( ok , compare_ref , frames )
2020-12-11 00:44:35 +03:00
}
2021-02-11 21:26:47 +03:00
fn parse_metadata ( src : & str , map : & LineMap ) -> ( Option < bool > , DiagSet ) {
let mut diags = DiagSet ::new ( ) ;
2021-02-01 00:43:11 +03:00
let mut compare_ref = None ;
2020-12-11 00:44:35 +03:00
2021-01-13 19:22:33 +03:00
for ( i , line ) in src . lines ( ) . enumerate ( ) {
2021-02-01 00:43:11 +03:00
let line = line . trim ( ) ;
if line . starts_with ( " // Ref: false " ) {
compare_ref = Some ( false ) ;
}
if line . starts_with ( " // Ref: true " ) {
compare_ref = Some ( true ) ;
}
2021-01-13 16:07:38 +03:00
2021-01-13 19:22:33 +03:00
let ( level , rest ) = if let Some ( rest ) = line . strip_prefix ( " // Warning: " ) {
2020-12-11 00:44:35 +03:00
( Level ::Warning , rest )
2021-01-13 19:22:33 +03:00
} else if let Some ( rest ) = line . strip_prefix ( " // Error: " ) {
2021-01-13 16:07:38 +03:00
( Level ::Error , rest )
2020-12-11 00:44:35 +03:00
} else {
continue ;
} ;
2021-01-13 19:22:33 +03:00
fn num ( s : & mut Scanner ) -> u32 {
s . eat_while ( | c | c . is_numeric ( ) ) . parse ( ) . unwrap ( )
}
2020-12-11 00:44:35 +03:00
2021-01-13 19:22:33 +03:00
let pos = | s : & mut Scanner | -> Pos {
2021-01-30 18:46:16 +03:00
let first = num ( s ) ;
let ( delta , column ) =
if s . eat_if ( ':' ) { ( first , num ( s ) ) } else { ( 1 , first ) } ;
2021-01-13 19:22:33 +03:00
let line = i as u32 + 1 + delta ;
2021-01-26 15:49:04 +03:00
map . pos ( Location ::new ( line , column ) ) . unwrap ( )
2021-01-13 19:22:33 +03:00
} ;
2021-01-13 16:07:38 +03:00
2021-01-13 19:22:33 +03:00
let mut s = Scanner ::new ( rest ) ;
2021-02-17 23:30:20 +03:00
let start = pos ( & mut s ) ;
let end = if s . eat_if ( '-' ) { pos ( & mut s ) } else { start } ;
2021-02-11 21:26:47 +03:00
diags . insert ( Diag ::new ( start .. end , level , s . rest ( ) . trim ( ) ) ) ;
2021-01-13 19:22:33 +03:00
}
2020-12-11 00:44:35 +03:00
2021-01-13 19:22:33 +03:00
( compare_ref , diags )
2020-12-11 00:44:35 +03:00
}
2021-01-26 22:49:45 +03:00
struct Panic {
pos : Pos ,
lhs : Option < Value > ,
rhs : Option < Value > ,
}
fn register_helpers ( scope : & mut Scope , panics : Rc < RefCell < Vec < Panic > > > ) {
2021-03-19 19:57:31 +03:00
pub fn args ( _ : & mut EvalContext , args : & mut FuncArgs ) -> Value {
let repr = pretty ( args ) ;
2021-02-10 00:56:44 +03:00
args . items . clear ( ) ;
2021-03-19 19:57:31 +03:00
Value ::template ( " args " , move | ctx | {
let snapshot = ctx . state . clone ( ) ;
ctx . set_monospace ( ) ;
ctx . push_text ( & repr ) ;
ctx . state = snapshot ;
} )
2021-01-14 19:28:03 +03:00
}
2021-03-19 19:57:31 +03:00
let test = move | ctx : & mut EvalContext , args : & mut FuncArgs | -> Value {
2021-01-17 15:53:22 +03:00
let lhs = args . require ::< Value > ( ctx , " left-hand side " ) ;
let rhs = args . require ::< Value > ( ctx , " right-hand side " ) ;
if lhs ! = rhs {
2021-01-26 22:49:45 +03:00
panics . borrow_mut ( ) . push ( Panic { pos : args . span . start , lhs , rhs } ) ;
2021-01-17 15:53:22 +03:00
Value ::Str ( format! ( " (panic) " ) )
} else {
Value ::None
}
} ;
2021-03-03 19:53:40 +03:00
scope . def_const ( " error " , Value ::Error ) ;
2021-03-19 19:57:31 +03:00
scope . def_const ( " args " , FuncValue ::new ( Some ( " args " . into ( ) ) , args ) ) ;
scope . def_const ( " test " , FuncValue ::new ( Some ( " test " . into ( ) ) , test ) ) ;
2021-01-14 19:28:03 +03:00
}
2021-02-11 21:26:47 +03:00
fn print_diag ( diag : & Diag , map : & LineMap , lines : u32 ) {
2021-01-22 19:16:42 +03:00
let mut start = map . location ( diag . span . start ) . unwrap ( ) ;
let mut end = map . location ( diag . span . end ) . unwrap ( ) ;
start . line + = lines ;
end . line + = lines ;
2021-02-11 21:26:47 +03:00
println! ( " {} : {} - {} : {} " , diag . level , start , end , diag . message ) ;
2020-12-11 00:44:35 +03:00
}
2020-08-02 22:17:42 +03:00
2021-02-11 23:22:06 +03:00
fn draw ( env : & Env , frames : & [ Frame ] , pixel_per_pt : f32 ) -> Canvas {
2020-11-25 20:46:47 +03:00
let pad = Length ::pt ( 5.0 ) ;
2021-01-03 02:12:09 +03:00
let height = pad + frames . iter ( ) . map ( | l | l . size . height + pad ) . sum ::< Length > ( ) ;
2020-08-30 23:18:55 +03:00
let width = 2.0 * pad
2021-01-03 02:12:09 +03:00
+ frames
2020-08-30 23:18:55 +03:00
. iter ( )
2020-11-25 20:46:47 +03:00
. map ( | l | l . size . width )
2020-08-30 23:18:55 +03:00
. max_by ( | a , b | a . partial_cmp ( & b ) . unwrap ( ) )
2021-03-12 15:57:47 +03:00
. unwrap_or_default ( ) ;
2020-08-30 23:18:55 +03:00
2020-11-25 20:46:47 +03:00
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 ;
2021-01-14 01:19:44 +03:00
if pixel_width > 4000 | | pixel_height > 4000 {
2021-03-12 15:57:47 +03:00
panic! (
" overlarge image: {} by {} ({} x {}) " ,
pixel_width , pixel_height , width , height ,
) ;
2021-01-14 01:19:44 +03:00
}
2020-11-25 20:46:47 +03:00
let mut canvas = Canvas ::new ( pixel_width , pixel_height ) . unwrap ( ) ;
canvas . scale ( pixel_per_pt , pixel_per_pt ) ;
canvas . pixmap . fill ( Color ::BLACK ) ;
2020-08-02 22:17:42 +03:00
2020-11-25 20:46:47 +03:00
let mut origin = Point ::new ( pad , pad ) ;
2021-01-03 02:12:09 +03:00
for frame in frames {
2020-11-25 20:46:47 +03:00
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 ,
2021-01-03 02:12:09 +03:00
frame . size . width . to_pt ( ) as f32 ,
frame . size . height . to_pt ( ) as f32 ,
2020-11-25 20:46:47 +03:00
)
. unwrap ( ) ,
& paint ,
2020-08-02 22:17:42 +03:00
) ;
2021-01-03 02:12:09 +03:00
for & ( pos , ref element ) in & frame . elements {
2020-11-25 20:46:47 +03:00
let pos = origin + pos ;
2020-08-02 22:17:42 +03:00
match element {
2021-01-03 02:12:09 +03:00
Element ::Text ( shaped ) = > {
2021-02-11 23:22:06 +03:00
draw_text ( env , & mut canvas , pos , shaped ) ;
2020-11-28 00:35:42 +03:00
}
2021-01-03 02:12:09 +03:00
Element ::Image ( image ) = > {
2021-02-11 23:22:06 +03:00
draw_image ( env , & mut canvas , pos , image ) ;
2020-11-20 18:36:22 +03:00
}
2021-02-04 23:30:18 +03:00
Element ::Geometry ( geom ) = > {
2021-02-11 23:22:06 +03:00
draw_geometry ( env , & mut canvas , pos , geom ) ;
2021-02-04 23:30:18 +03:00
}
2020-08-02 22:17:42 +03:00
}
}
2021-01-03 02:12:09 +03:00
origin . y + = frame . size . height + pad ;
2020-08-02 22:17:42 +03:00
}
2020-11-25 20:46:47 +03:00
canvas
2020-08-02 22:17:42 +03:00
}
2021-02-11 23:22:06 +03:00
fn draw_text ( env : & Env , canvas : & mut Canvas , pos : Point , shaped : & Shaped ) {
2020-12-17 17:43:30 +03:00
let face = env . fonts . face ( shaped . face ) . get ( ) ;
2020-08-02 22:17:42 +03:00
for ( & glyph , & offset ) in shaped . glyphs . iter ( ) . zip ( & shaped . offsets ) {
2020-11-25 20:46:47 +03:00
let units_per_em = face . units_per_em ( ) . unwrap_or ( 1000 ) ;
let x = ( pos . x + offset ) . to_pt ( ) as f32 ;
2021-03-10 12:20:01 +03:00
let y = pos . y . to_pt ( ) as f32 ;
2020-11-25 20:46:47 +03:00
let scale = ( shaped . font_size / units_per_em as f64 ) . to_pt ( ) as f32 ;
2020-08-02 22:17:42 +03:00
let mut builder = WrappedPathBuilder ( PathBuilder ::new ( ) ) ;
face . outline_glyph ( glyph , & mut builder ) ;
2020-12-17 14:16:17 +03:00
if let Some ( path ) = builder . 0. finish ( ) {
let placed = path
. transform ( & Transform ::from_row ( scale , 0.0 , 0.0 , - scale , x , y ) . unwrap ( ) )
. unwrap ( ) ;
2020-11-25 20:46:47 +03:00
2021-03-20 00:36:13 +03:00
let mut paint = paint_from_fill ( shaped . color ) ;
2020-12-17 14:16:17 +03:00
paint . anti_alias = true ;
2020-11-25 20:46:47 +03:00
2020-12-17 14:16:17 +03:00
canvas . fill_path ( & placed , & paint , FillRule ::default ( ) ) ;
}
2020-08-02 22:17:42 +03:00
}
}
2021-02-11 23:22:06 +03:00
fn draw_geometry ( _ : & Env , canvas : & mut Canvas , pos : Point , element : & Geometry ) {
2021-02-04 23:30:18 +03:00
let x = pos . x . to_pt ( ) as f32 ;
let y = pos . y . to_pt ( ) as f32 ;
2021-03-20 00:36:13 +03:00
let paint = paint_from_fill ( element . fill ) ;
2021-02-04 23:30:18 +03:00
2021-02-06 14:30:44 +03:00
match & element . shape {
Shape ::Rect ( s ) = > {
let ( w , h ) = ( s . width . to_pt ( ) as f32 , s . height . to_pt ( ) as f32 ) ;
canvas . fill_rect ( Rect ::from_xywh ( x , y , w , h ) . unwrap ( ) , & paint ) ;
2021-02-06 14:54:44 +03:00
}
2021-02-06 14:30:44 +03:00
} ;
2021-02-04 23:30:18 +03:00
}
2021-03-20 00:36:13 +03:00
fn paint_from_fill ( fill : Fill ) -> Paint < 'static > {
let mut paint = Paint ::default ( ) ;
match fill {
Fill ::Color ( c ) = > match c {
typst ::color ::Color ::Rgba ( c ) = > paint . set_color_rgba8 ( c . r , c . g , c . b , c . a ) ,
} ,
Fill ::Image ( _ ) = > todo! ( ) ,
}
paint
}
2021-02-11 23:22:06 +03:00
fn draw_image ( env : & Env , canvas : & mut Canvas , pos : Point , element : & Image ) {
2021-01-01 19:54:31 +03:00
let img = & env . resources . loaded ::< ImageResource > ( element . res ) ;
2020-11-28 00:35:42 +03:00
2021-01-01 19:54:31 +03:00
let mut pixmap = Pixmap ::new ( img . buf . width ( ) , img . buf . height ( ) ) . unwrap ( ) ;
for ( ( _ , _ , src ) , dest ) in img . buf . pixels ( ) . zip ( pixmap . pixels_mut ( ) ) {
2020-11-28 00:35:42 +03:00
let Rgba ( [ r , g , b , a ] ) = src ;
* dest = ColorU8 ::from_rgba ( r , g , b , a ) . premultiply ( ) ;
2020-11-20 18:36:22 +03:00
}
2021-01-01 19:54:31 +03:00
let view_width = element . size . width . to_pt ( ) as f32 ;
let view_height = element . size . height . to_pt ( ) as f32 ;
2020-11-25 20:46:47 +03:00
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 ,
2020-11-20 18:36:22 +03:00
) ;
}
2020-08-02 22:17:42 +03:00
struct WrappedPathBuilder ( PathBuilder ) ;
impl OutlineBuilder for WrappedPathBuilder {
fn move_to ( & mut self , x : f32 , y : f32 ) {
self . 0. move_to ( x , y ) ;
}
fn line_to ( & mut self , x : f32 , y : f32 ) {
self . 0. line_to ( x , y ) ;
}
fn quad_to ( & mut self , x1 : f32 , y1 : f32 , x : f32 , y : f32 ) {
self . 0. quad_to ( x1 , y1 , x , y ) ;
}
fn curve_to ( & mut self , x1 : f32 , y1 : f32 , x2 : f32 , y2 : f32 , x : f32 , y : f32 ) {
self . 0. cubic_to ( x1 , y1 , x2 , y2 , x , y ) ;
}
fn close ( & mut self ) {
self . 0. close ( ) ;
}
}