mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 12:17:14 -06:00
Type: Top-to-Top Leading follow-ups (review of #385)
- The em box top that Top-to-Top Leading asks of every glyph's line metrics parsed the font's cmap and vertical metrics per glyph on every layout: each face now reads it once. - The Paragraph panel menu shows Top-to-Top / Bottom-to-Bottom Leading with the East Asian options (Preferences › Type), as the Mojikumi Set does, and Reset Panel goes back to Bottom-to-Bottom Leading, the default. - Saved as `leading_model`, like the other paragraph attributes in files. - Simplified Chinese (landed since) translates the two menu items. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
46dc8b1bde
commit
5865e4cb51
@@ -327,7 +327,7 @@ struct ParaStyleFile {
|
||||
mojikumi: Mojikumi,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
direction: Option<ParaDirection>,
|
||||
#[serde(default, rename = "leadingModel", skip_serializing_if = "crate::skip::is_default")]
|
||||
#[serde(default, skip_serializing_if = "crate::skip::is_default")]
|
||||
leading_model: LeadingModel,
|
||||
}
|
||||
|
||||
|
||||
@@ -428,9 +428,9 @@ mod area_tests {
|
||||
let t = text(&s);
|
||||
assert_eq!(t.para.leading_model, LeadingModel::EmBoxTop);
|
||||
assert_ne!(t.cached_bounds, before, "the first line moves up to the frame's top");
|
||||
assert_eq!(serde_json::to_value(&t.para).unwrap()["leadingModel"], "emBoxTop");
|
||||
assert_eq!(serde_json::to_value(&t.para).unwrap()["leading_model"], "emBoxTop");
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert!(serde_json::to_value(&text(&s).para).unwrap().get("leadingModel").is_none());
|
||||
assert!(serde_json::to_value(&text(&s).para).unwrap().get("leading_model").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -77,6 +77,8 @@ pub struct FontFace {
|
||||
location: Location,
|
||||
/// [`Self::variations`] for the shaper (advances, kerning, feature variations).
|
||||
pub(crate) instance: Option<harfrust::ShaperInstance>,
|
||||
/// [`Self::ideographic_centre`], read once: layout asks for it per glyph.
|
||||
ideographic_centre: std::sync::OnceLock<f64>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for FontFace {
|
||||
@@ -170,8 +172,10 @@ impl FontFace {
|
||||
/// vertical metrics (an ideograph's cell), else the usual 0.38 (the box running from 0.12 em
|
||||
/// below the baseline to 0.88 em above it).
|
||||
pub fn ideographic_centre(&self) -> f64 {
|
||||
let gid = ['国', 'あ', '一'].into_iter().map(|c| self.glyph_for(c)).find(|g| *g != 0);
|
||||
gid.and_then(|g| self.vertical_glyph(g)).map_or(0.38, |(advance, origin)| (origin - advance * 0.5) / self.upem)
|
||||
*self.ideographic_centre.get_or_init(|| {
|
||||
let gid = ['国', 'あ', '一'].into_iter().map(|c| self.glyph_for(c)).find(|g| *g != 0);
|
||||
gid.and_then(|g| self.vertical_glyph(g)).map_or(0.38, |(advance, origin)| (origin - advance * 0.5) / self.upem)
|
||||
})
|
||||
}
|
||||
/// Glyph id for `c` (0 = .notdef).
|
||||
pub fn glyph_for(&self, c: char) -> u32 {
|
||||
@@ -813,6 +817,7 @@ fn make_face(bytes: FontBytes, index: u32, spec: FaceStyle, path: Option<std::pa
|
||||
bytes,
|
||||
index,
|
||||
path,
|
||||
ideographic_centre: std::sync::OnceLock::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -2953,3 +2953,7 @@
|
||||
— {n} characters from another font — {n} caracteres de otra fuente
|
||||
[Normal Character Style] [Estilo de carácter normal]
|
||||
[Normal Paragraph Style] [Estilo de párrafo normal]
|
||||
|
||||
# Leading model (Paragraph panel menu, East Asian options).
|
||||
Top-to-Top Leading Interlineado de superior a superior
|
||||
Bottom-to-Bottom Leading Interlineado de inferior a inferior
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -243,6 +243,7 @@
|
||||
Blur 模糊
|
||||
Bottom 下
|
||||
Bottom in Front 下方置前
|
||||
Bottom-to-Bottom Leading 基线至基线行距
|
||||
Bottom: 下:
|
||||
Bounds 边界
|
||||
Bounds (Fill Proportionally) 边界 (按比例填满)
|
||||
@@ -2567,6 +2568,7 @@
|
||||
Tools 工具
|
||||
Top 上
|
||||
Top in Front 上方在前
|
||||
Top-to-Top Leading 上缘至上缘行距
|
||||
Top: 上:
|
||||
Total Width: 总宽度:
|
||||
Touch Type Tool 修饰文字工具
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -150,14 +150,17 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
for l in [tl!("Roman Hanging Punctuation"), tl!("Justification…"), tl!("Hyphenation…")] {
|
||||
menu_item(ui, l, false, false);
|
||||
}
|
||||
ui.separator();
|
||||
// How leading is measured (Japanese layout measures it from em box top to em box top).
|
||||
let top_to_top = style.as_ref().is_some_and(|(_, p)| p.leading_model == vectorcraft_doc::LeadingModel::EmBoxTop);
|
||||
if menu_item(ui, tl!("Top-to-Top Leading"), has, top_to_top) {
|
||||
format(app, json!({"leadingModel": "emBoxTop"}));
|
||||
}
|
||||
if menu_item(ui, tl!("Bottom-to-Bottom Leading"), has, has && !top_to_top) {
|
||||
format(app, json!({"leadingModel": "romanBaseline"}));
|
||||
// How leading is measured (Japanese layout measures it from em box top to em box top), with
|
||||
// the East Asian options.
|
||||
if app.session.prefs.show_east_asian_options {
|
||||
ui.separator();
|
||||
let top_to_top = style.as_ref().is_some_and(|(_, p)| p.leading_model == vectorcraft_doc::LeadingModel::EmBoxTop);
|
||||
if menu_item(ui, tl!("Top-to-Top Leading"), has, top_to_top) {
|
||||
format(app, json!({"leadingModel": "emBoxTop"}));
|
||||
}
|
||||
if menu_item(ui, tl!("Bottom-to-Bottom Leading"), has, has && !top_to_top) {
|
||||
format(app, json!({"leadingModel": "romanBaseline"}));
|
||||
}
|
||||
}
|
||||
ui.separator();
|
||||
menu_item(ui, tl!("Single-line Composer"), false, false);
|
||||
@@ -167,7 +170,7 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
para_cmd(app, "text.setStyle", json!({"justify": "auto"}));
|
||||
format(
|
||||
app,
|
||||
json!({"leftIndent": 0, "rightIndent": 0, "firstLineIndent": 0, "spaceBefore": 0, "spaceAfter": 0, "hyphenate": false, "direction": "auto"}),
|
||||
json!({"leftIndent": 0, "rightIndent": 0, "firstLineIndent": 0, "spaceBefore": 0, "spaceAfter": 0, "hyphenate": false, "direction": "auto", "leadingModel": "romanBaseline"}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user