@@ -189,7 +189,14 @@ impl FontData for FontDataInternal {
189189 let glyph_metrics = font_ref. glyph_metrics ( & [ ] ) . scale ( em) ;
190190 let charmap = font_ref. charmap ( ) ;
191191
192- base_y -= ( 0.24 * em) as i32 ;
192+ // Place the swash pen at the baseline. font-kit rasterized into a
193+ // `size`-square canvas whose top sat at `pos.y - 0.24*em`, then
194+ // applied a `(0, em)` rasterization translation, putting the
195+ // effective baseline at `pos.y + 0.76*em`. Swash places glyphs
196+ // relative to the pen directly, so we shift the pen to that same
197+ // baseline; otherwise glyphs render ~one em above where callers
198+ // expect them.
199+ base_y += ( 0.76 * em) as i32 ;
193200
194201 let mut prev = None ;
195202 let place_holder = glyph_for_char ( & charmap, PLACEHOLDER_CHAR ) ;
@@ -277,13 +284,15 @@ mod test {
277284 }
278285
279286 fn assert_draw_sanity ( family : FontFamily < ' _ > , style : FontStyle ) -> FontResult < ( ) > {
280- let size = 32.0 ;
281- let em = size / 1.24 ;
282- let baseline = ( size as i32 ) - ( 0.24 * em as f32 ) as i32 ;
287+ let size = 32.0_f64 ;
288+ let em = ( size / 1.24 ) as f32 ;
289+ let pos_y = size as i32 ;
290+ // Baseline must match the pen position chosen in `draw`.
291+ let baseline = pos_y + ( 0.76 * em) as i32 ;
283292 let font = FontDataInternal :: new ( family, style) ?;
284293 let mut samples = Vec :: new ( ) ;
285294
286- let draw_result = font. draw ( ( 0 , size as i32 ) , size, "Hg" , |x, y, alpha| {
295+ let draw_result = font. draw ( ( 0 , pos_y ) , size, "Hg" , |x, y, alpha| {
287296 samples. push ( ( x, y, alpha) ) ;
288297 Ok :: < ( ) , ( ) > ( ( ) )
289298 } ) ?;
@@ -311,16 +320,24 @@ mod test {
311320 let min_y = touched. iter ( ) . map ( |( _, y, _) | * y) . min ( ) . unwrap ( ) ;
312321 let max_y = touched. iter ( ) . map ( |( _, y, _) | * y) . max ( ) . unwrap ( ) ;
313322
323+ // Baseline-anchored bounds. The pen is at output y = `baseline`,
324+ // ascenders extend up by ~em and descenders down by ~0.3*em.
314325 assert ! ( min_x >= 0 , "glyphs drifted left: min_x={}" , min_x) ;
315- assert ! ( min_y >= 0 , "glyphs drifted above canvas: min_y={}" , min_y) ;
326+ assert ! (
327+ min_y >= baseline - ( 1.2 * em) as i32 ,
328+ "glyphs drifted too high above baseline {}: min_y={}" ,
329+ baseline,
330+ min_y
331+ ) ;
316332 assert ! (
317333 max_x <= ( 3.0 * em) as i32 ,
318334 "glyphs drifted right: max_x={}" ,
319335 max_x
320336 ) ;
321337 assert ! (
322- max_y <= ( 1.5 * em) as i32 ,
323- "glyphs drifted below canvas: max_y={}" ,
338+ max_y <= baseline + ( 0.6 * em) as i32 ,
339+ "glyphs drifted too far below baseline {}: max_y={}" ,
340+ baseline,
324341 max_y
325342 ) ;
326343
@@ -334,11 +351,19 @@ mod test {
334351 max_y
335352 ) ;
336353
354+ // Cap height should sit above the baseline by a meaningful amount.
355+ assert ! (
356+ min_y < baseline,
357+ "expected glyph tops above baseline {}: min_y={}" ,
358+ baseline,
359+ min_y
360+ ) ;
361+
337362 // The touched bbox should span roughly one em vertically; this
338363 // guards against placement.top being applied with the wrong scale.
339364 let bbox_height = ( max_y - min_y) as f32 ;
340365 assert ! (
341- ( 0.5 * em as f32 ..=1.5 * em as f32 ) . contains( & bbox_height) ,
366+ ( 0.5 * em..=1.5 * em) . contains( & bbox_height) ,
342367 "bbox height {} not within [0.5*em, 1.5*em] (em={})" ,
343368 bbox_height,
344369 em
0 commit comments