1
+ use cosmic_text:: { Attrs , Buffer , FontSystem , Metrics , Shaping } ;
2
+ use fontdb:: Source ;
3
+ use image:: { load_from_memory, Rgba } ;
1
4
use krilla:: document:: Document ;
2
5
use krilla:: rgb:: Rgb ;
3
6
use krilla:: serialize:: SerializeSettings ;
7
+ use krilla:: stream:: TestGlyph ;
4
8
use krilla:: { rgb, Fill , LinearGradient , Paint , SpreadMethod , Stop } ;
5
- use sitro:: { render_mupdf, render_pdfbox, render_pdfium, render_pdfjs, render_quartz, RenderOptions , RenderedDocument , Renderer , render_poppler, render_ghostscript} ;
9
+ use sitro:: {
10
+ render_ghostscript, render_mupdf, render_pdfbox, render_pdfium, render_pdfjs, render_poppler,
11
+ render_quartz, RenderOptions , RenderedDocument , Renderer ,
12
+ } ;
13
+ use skrifa:: GlyphId ;
14
+ use std:: cmp:: max;
6
15
use std:: path:: PathBuf ;
16
+ use std:: sync:: Arc ;
7
17
use tiny_skia_path:: { PathBuilder , Rect , Size , Transform } ;
8
18
use usvg:: NormalizedF32 ;
9
19
@@ -17,7 +27,7 @@ pub fn render_doc(doc: &[u8], renderer: &Renderer) -> RenderedDocument {
17
27
Renderer :: Quartz => render_quartz ( doc, & options) . unwrap ( ) ,
18
28
Renderer :: Pdfjs => render_pdfjs ( doc, & options) . unwrap ( ) ,
19
29
Renderer :: Pdfbox => render_pdfbox ( doc, & options) . unwrap ( ) ,
20
- Renderer :: Ghostscript => render_ghostscript ( doc, & options) . unwrap ( )
30
+ Renderer :: Ghostscript => render_ghostscript ( doc, & options) . unwrap ( ) ,
21
31
}
22
32
}
23
33
@@ -32,6 +42,42 @@ pub fn save_refs(name: &str, renderer: &Renderer, document: RenderedDocument) {
32
42
panic ! ( "empty document" ) ;
33
43
} else if document. len ( ) == 1 {
34
44
let ref_path = refs_path. join ( format ! ( "{}.png" , renderer. name( ) ) ) ;
45
+
46
+ let reference = load_from_memory ( & std:: fs:: read ( & ref_path) . unwrap ( ) ) . unwrap ( ) . into_rgba8 ( ) ;
47
+ let actual = load_from_memory ( & document[ 0 ] ) . unwrap ( ) . into_rgba8 ( ) ;
48
+
49
+ let width = max ( reference. width ( ) , actual. width ( ) ) ;
50
+ let height = max ( reference. height ( ) , actual. height ( ) ) ;
51
+
52
+ let mut pixel_diff = 0 ;
53
+
54
+ for x in 0 ..width {
55
+ for y in 0 ..height {
56
+ let actual_pixel = actual. get_pixel_checked ( x, y) ;
57
+ let expected_pixel = reference. get_pixel_checked ( x, y) ;
58
+
59
+
60
+
61
+ match ( actual_pixel, expected_pixel) {
62
+ ( Some ( actual) , Some ( expected) ) => {
63
+ if is_pix_diff ( expected, actual) {
64
+ pixel_diff += 1 ;
65
+ }
66
+ }
67
+ ( Some ( actual) , None ) => {
68
+ pixel_diff += 1 ;
69
+ }
70
+ ( None , Some ( expected) ) => {
71
+ pixel_diff += 1 ;
72
+ }
73
+ _ => unreachable ! ( ) ,
74
+ }
75
+ }
76
+ }
77
+
78
+ assert_eq ! ( pixel_diff, 0 ) ;
79
+
80
+
35
81
std:: fs:: write ( & ref_path, & document[ 0 ] ) . unwrap ( ) ;
36
82
} else {
37
83
for ( index, page) in document. iter ( ) . enumerate ( ) {
@@ -41,6 +87,17 @@ pub fn save_refs(name: &str, renderer: &Renderer, document: RenderedDocument) {
41
87
}
42
88
}
43
89
90
+ fn is_pix_diff ( pixel1 : & Rgba < u8 > , pixel2 : & Rgba < u8 > ) -> bool {
91
+ if pixel1. 0 [ 3 ] == 0 && pixel2. 0 [ 3 ] == 0 {
92
+ return false ;
93
+ }
94
+
95
+ pixel1. 0 [ 0 ] != pixel2. 0 [ 0 ]
96
+ || pixel1. 0 [ 1 ] != pixel2. 0 [ 1 ]
97
+ || pixel1. 0 [ 2 ] != pixel2. 0 [ 2 ]
98
+ || pixel1. 0 [ 3 ] != pixel2. 0 [ 3 ]
99
+ }
100
+
44
101
macro_rules! generate_renderer_tests {
45
102
( $test_name: ident, $test_body: expr) => {
46
103
paste:: item! {
@@ -140,3 +197,50 @@ generate_renderer_tests!(linear_gradient, |renderer| {
140
197
let rendered = render_doc( & pdf, & renderer) ;
141
198
save_refs( stringify!( linear_gradient) , & renderer, rendered) ;
142
199
} ) ;
200
+
201
+ generate_renderer_tests ! ( cosmic_text, |renderer| {
202
+ let mut font_system = FontSystem :: new_with_fonts( [ Source :: Binary ( Arc :: new( include_bytes!(
203
+ "fonts/NotoSans-Regular.ttf"
204
+ ) ) ) ] ) ;
205
+ let metrics = Metrics :: new( 14.0 , 20.0 ) ;
206
+ let mut buffer = Buffer :: new( & mut font_system, metrics) ;
207
+ buffer. set_size( & mut font_system, Some ( 200.0 ) , None ) ;
208
+ let attrs = Attrs :: new( ) ;
209
+ let text = "Some text here. Let's make it a bit longer so that line wrapping kicks in" ;
210
+ buffer. set_text( & mut font_system, text, attrs, Shaping :: Advanced ) ;
211
+ buffer. shape_until_scroll( & mut font_system, false ) ;
212
+
213
+ let page_size = tiny_skia_path:: Size :: from_wh( 200.0 , 400.0 ) . unwrap( ) ;
214
+ let mut document_builder = Document :: new( SerializeSettings :: default ( ) ) ;
215
+ let mut builder = document_builder. start_page( page_size) ;
216
+ let mut surface = builder. surface( ) ;
217
+
218
+ let font_map = surface. convert_fontdb( font_system. db_mut( ) , None ) ;
219
+
220
+ // Inspect the output runs
221
+ for run in buffer. layout_runs( ) {
222
+ let y_offset = run. line_y;
223
+ let iter = run
224
+ . glyphs
225
+ . iter( )
226
+ . map( |g| {
227
+ TestGlyph :: new(
228
+ font_map. get( & g. font_id) . unwrap( ) . clone( ) ,
229
+ GlyphId :: new( g. glyph_id as u32 ) ,
230
+ g. w,
231
+ g. x_offset,
232
+ g. font_size,
233
+ run. text[ g. start..g. end] . to_string( ) ,
234
+ )
235
+ } )
236
+ . peekable( ) ;
237
+ surface. fill_glyph_run( 0.0 , y_offset, Fill :: <Rgb >:: default ( ) , iter) ;
238
+ }
239
+
240
+ surface. finish( ) ;
241
+ builder. finish( ) ;
242
+
243
+ let pdf = document_builder. finish( ) ;
244
+ let rendered = render_doc( & pdf, & renderer) ;
245
+ save_refs( stringify!( text_rendering) , & renderer, rendered) ;
246
+ } ) ;
0 commit comments