Skip to content

Commit

Permalink
fix: wrong text baseline calculation in drawText and measureText (#606)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn authored Jan 12, 2023
1 parent 68a10d3 commit bba0296
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 42 deletions.
3 changes: 3 additions & 0 deletions __test__/draw.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,9 @@ test('measureText with empty string should not throw', (t) => {
actualBoundingBoxRight: 0,
fontBoundingBoxAscent: 0,
fontBoundingBoxDescent: 0,
alphabeticBaseline: 0,
emHeightAscent: 0,
emHeightDescent: 0,
width: 0,
})
})
Expand Down
Binary file modified __test__/snapshots/draw-text-maxWidth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/text-baseline-all.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified __test__/snapshots/text-baseline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 20 additions & 2 deletions __test__/text.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,26 @@ test('text-baseline', async (t) => {
const { ctx } = t.context
ctx.font = '48px Iosevka Slab'
ctx.textBaseline = 'bottom'
ctx.fillText('abcdef', 50, 50)
ctx.fillText('abcdefg', 50, 50)
ctx.fillText('abcdef', 50, 100)
ctx.fillText('abcdefg', 50, 100)
await snapshotImage(t)
})

test('text-baseline-all', async (t) => {
const { ctx } = t.context
const baselines = ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'] as const
ctx.font = '36px Iosevka Slab'
ctx.strokeStyle = 'red'

baselines.forEach((baseline, index) => {
ctx.textBaseline = baseline
const y = 75 + index * 75
ctx.beginPath()
ctx.moveTo(0, y + 0.5)
ctx.lineTo(550, y + 0.5)
ctx.stroke()
ctx.fillText(`Abcdefghijklmnop (${baseline})`, 0, y)
})
await snapshotImage(t)
})

Expand Down
82 changes: 42 additions & 40 deletions skia-c/skia_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,15 +446,15 @@ extern "C"
switch (css_baseline)
{
case CssBaseline::Top:
baseline_offset = -alphabetic_baseline - font_metrics.fAscent;
baseline_offset = -alphabetic_baseline - font_metrics.fAscent - font_metrics.fUnderlinePosition - font_metrics.fUnderlineThickness;
break;
case CssBaseline::Hanging:
// https://github.com/chromium/chromium/blob/104.0.5092.1/third_party/blink/renderer/core/html/canvas/text_metrics.cc#L21-L25
// According to
// http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
// "FOP (Formatting Objects Processor) puts the hanging baseline at 80% of
// the ascender height"
baseline_offset = -alphabetic_baseline - (font_metrics.fAscent - font_metrics.fDescent) * HANGING_AS_PERCENT_OF_ASCENT / 100.0;
baseline_offset = -alphabetic_baseline - font_metrics.fAscent * HANGING_AS_PERCENT_OF_ASCENT / 100.0;
break;
case CssBaseline::Middle:
baseline_offset = -paragraph->getHeight() / 2;
Expand All @@ -466,50 +466,51 @@ extern "C"
baseline_offset = -paragraph->getIdeographicBaseline();
break;
case CssBaseline::Bottom:
baseline_offset = font_metrics.fStrikeoutPosition;
baseline_offset = -alphabetic_baseline + font_metrics.fStrikeoutPosition + font_metrics.fStrikeoutThickness;
break;
};

if (c_canvas)
auto line_center = line_width / 2.0f;
float paint_x;
switch ((TextAlign)align)
{
auto line_center = line_width / 2.0f;
float paint_x;
switch ((TextAlign)align)
case TextAlign::kLeft:
paint_x = x;
break;
case TextAlign::kCenter:
paint_x = x - line_center;
break;
case TextAlign::kRight:
paint_x = x - line_width;
break;
// Unreachable
case TextAlign::kJustify:
paint_x = x;
break;
case TextAlign::kStart:
if (text_direction == TextDirection::kLtr)
{
case TextAlign::kLeft:
paint_x = x;
break;
case TextAlign::kCenter:
paint_x = x - line_center;
break;
case TextAlign::kRight:
}
else
{
paint_x = x - line_width;
break;
// Unreachable
case TextAlign::kJustify:
}
break;
case TextAlign::kEnd:
if (text_direction == TextDirection::kRtl)
{
paint_x = x;
break;
case TextAlign::kStart:
if (text_direction == TextDirection::kLtr)
{
paint_x = x;
}
else
{
paint_x = x - line_width;
}
break;
case TextAlign::kEnd:
if (text_direction == TextDirection::kRtl)
{
paint_x = x;
}
else
{
paint_x = x - line_width;
}
break;
};
}
else
{
paint_x = x - line_width;
}
break;
};

if (c_canvas)
{
auto need_scale = line_width > max_width;
float ratio = need_scale ? max_width / line_width : 1.0;
if (need_scale)
Expand All @@ -529,11 +530,12 @@ extern "C"
auto offset = -baseline_offset - alphabetic_baseline;
c_line_metrics->ascent = -ascent + offset;
c_line_metrics->descent = descent - offset;
c_line_metrics->left = line_metrics.fLeft - first_char_bounds.fLeft;
c_line_metrics->right = last_char_pos_x + last_char_bounds.fRight;
c_line_metrics->left = -paint_x + line_metrics.fLeft - first_char_bounds.fLeft;
c_line_metrics->right = paint_x + last_char_pos_x + last_char_bounds.fRight;
c_line_metrics->width = line_width;
c_line_metrics->font_ascent = -font_metrics.fAscent + offset;
c_line_metrics->font_descent = font_metrics.fDescent - offset;
c_line_metrics->alphabetic_baseline = -font_metrics.fAscent + offset;
}
delete paragraph;
}
Expand Down
1 change: 1 addition & 0 deletions skia-c/skia_c.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ struct skiac_line_metrics
float width;
float font_ascent;
float font_descent;
float alphabetic_baseline;
};

struct skiac_rect
Expand Down
9 changes: 9 additions & 0 deletions src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,9 @@ impl CanvasRenderingContext2D {
actual_bounding_box_right: 0.0,
font_bounding_box_ascent: 0.0,
font_bounding_box_descent: 0.0,
alphabetic_baseline: 0.0,
em_height_ascent: 0.0,
em_height_descent: 0.0,
width: 0.0,
});
}
Expand All @@ -1589,6 +1592,9 @@ impl CanvasRenderingContext2D {
actual_bounding_box_right: metrics.0.right as f64,
font_bounding_box_ascent: metrics.0.font_ascent as f64,
font_bounding_box_descent: metrics.0.font_descent as f64,
alphabetic_baseline: metrics.0.alphabetic_baseline as f64,
em_height_ascent: metrics.0.font_ascent as f64,
em_height_descent: metrics.0.font_descent as f64,
width: metrics.0.width as f64,
})
}
Expand Down Expand Up @@ -1893,6 +1899,9 @@ pub struct TextMetrics {
pub actual_bounding_box_right: f64,
pub font_bounding_box_ascent: f64,
pub font_bounding_box_descent: f64,
pub alphabetic_baseline: f64,
pub em_height_ascent: f64,
pub em_height_descent: f64,
pub width: f64,
}

Expand Down
3 changes: 3 additions & 0 deletions src/sk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ pub mod ffi {
pub width: f32,
pub font_ascent: f32,
pub font_descent: f32,
pub alphabetic_baseline: f32,
pub em_height_ascent: f32,
pub em_height_descent: f32,
}

#[repr(C)]
Expand Down

0 comments on commit bba0296

Please sign in to comment.