Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: wrong text baseline calculation in drawText and measureText #606

Merged
merged 1 commit into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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