mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
🐛 Keep Pinyin readings on one baseline over horizontal text
Each ruby reading attached its lowest ink to the base, which suits kana but not Latin letters: the descender of "y" lifted "yǒu" above "liáo" over 友聊, and under ruby moved with ascenders and tone marks. Western letters in a reading now take the font's descent (over) or cap height (under) instead of their ink, so Pinyin and romaji readings share one baseline and descenders stay clear of the base. Upright glyphs keep the ink edge. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
8f5c66c2d5
commit
01db64abaf
@ -384,11 +384,22 @@ pub(super) fn horizontal_ruby_pieces(
|
||||
/// Ink edge shared by the glyphs of one ruby line: the lowest ink bottom for
|
||||
/// over ruby, the highest ink top for under ruby. One edge for the whole line
|
||||
/// keeps marks whose ink sits mid-height, such as ー and ・, on the line's
|
||||
/// baseline instead of pulling each of them onto the base.
|
||||
/// baseline instead of pulling each of them onto the base. Western letters
|
||||
/// (Pinyin, romaji) take the font's descent and cap height instead of their
|
||||
/// ink, so every reading keeps one baseline whatever its descenders, ascenders
|
||||
/// or tone marks.
|
||||
pub(super) fn ruby_line_ink_edge(shaped: &[ShapedRun], glyphs: &[RubyGlyphRef], over: bool) -> f32 {
|
||||
let edges = glyphs.iter().map(|(run_index, glyph, _, _)| {
|
||||
let edges = glyphs.iter().map(|(run_index, glyph, _, western)| {
|
||||
let run = &shaped[*run_index];
|
||||
let (_, metrics) = run.font.metrics();
|
||||
if *western {
|
||||
if over {
|
||||
return metrics.descent;
|
||||
}
|
||||
if metrics.cap_height > 0.0 {
|
||||
return -metrics.cap_height;
|
||||
}
|
||||
}
|
||||
let fallback = if over {
|
||||
metrics.descent
|
||||
} else {
|
||||
|
||||
@ -1341,6 +1341,70 @@ fn horizontal_ruby_glyphs_share_one_baseline() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn latin_readings_share_one_baseline_whatever_their_letters() {
|
||||
// Pinyin over 友聊: the descender of "yu" lifted it above "lia" when each
|
||||
// reading attached its own ink bottom to the base.
|
||||
use crate::shapes::text_vertical::test_support::{provider, EM};
|
||||
use crate::shapes::{FontFamily, FontStyle};
|
||||
use skia_safe::FontMgr;
|
||||
|
||||
let font_provider = provider(include_bytes!("../../fonts/sourcesanspro-regular.ttf"));
|
||||
let family = format!("{}", FontFamily::new(Uuid::nil(), 400, FontStyle::Normal));
|
||||
let size = EM / 2.0;
|
||||
let shape = |text: &str| {
|
||||
shape_segment_with_fallbacks(
|
||||
text,
|
||||
size,
|
||||
&[family.clone()],
|
||||
&font_provider,
|
||||
false,
|
||||
FontFeatures::None,
|
||||
&FontMgr::from(font_provider.clone()),
|
||||
"zh-Hans",
|
||||
)
|
||||
};
|
||||
let base = skia::Rect::from_xywh(0.0, 0.0, EM, EM);
|
||||
for side in [RubySide::Over, RubySide::Under] {
|
||||
let baseline = |text: &str| {
|
||||
let span = TextSpan {
|
||||
ruby: text.to_string(),
|
||||
ruby_side: side,
|
||||
font_size: EM,
|
||||
..make_span("友", 0.0)
|
||||
};
|
||||
let shaped = shape(text);
|
||||
let glyphs = ruby_glyph_refs(text, &shaped, size);
|
||||
assert!(glyphs.iter().all(|(_, _, _, western)| *western));
|
||||
let line = RubyLine {
|
||||
span: &span,
|
||||
shaped: &shaped,
|
||||
glyphs: &glyphs,
|
||||
base,
|
||||
leading_gap: 0.0,
|
||||
room: (0.0, 0.0),
|
||||
};
|
||||
(line.baseline(0.0), shaped)
|
||||
};
|
||||
let (descender, shaped) = baseline("yu");
|
||||
let (plain, _) = baseline("lia");
|
||||
assert!(
|
||||
(descender - plain).abs() < 0.01,
|
||||
"{side:?}: yu at {descender}, lia at {plain}"
|
||||
);
|
||||
if side == RubySide::Over {
|
||||
let mut bounds = [skia::Rect::default()];
|
||||
shaped[0]
|
||||
.font
|
||||
.get_bounds(&shaped[0].glyphs[..1], &mut bounds, None);
|
||||
assert!(
|
||||
descender + bounds[0].bottom <= horizontal_annotation_over_top(base, EM) + 0.01,
|
||||
"the descender of y reaches into the base"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emphasis_position_overrides_the_language_and_stacks_on_its_own_side() {
|
||||
let placement = super::emphasis::HorizontalEmphasisPlacement {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user