* Type: fall back to installed Indic fonts for document text (#4383) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: regenerate scorecard after merging main Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4543ef35b9
commit
20e8f949f9
|
|
@ -59,7 +59,7 @@ fn inflate_font(deflated: &[u8]) -> Vec<u8> {
|
|||
|
||||
/// Families tried (if installed) after the requested one, for missing glyphs. The Southeast Asian
|
||||
/// and CJK families ([`THAI_FAMILIES`], [`KHMER_FAMILIES`], [`LAO_FAMILIES`], [`MYANMAR_FAMILIES`],
|
||||
/// [`crate::cjk::families`]) go between these two lists, in the UI locale's script order.
|
||||
/// [`INDIC_FAMILIES`], [`crate::cjk::families`]) go between these two lists, in the UI locale's script order.
|
||||
const FALLBACK_CANDIDATES: &[&str] = &["Noto Sans", "Segoe UI", "DejaVu Sans", "Geeza Pro", "Arial Hebrew", "Noto Sans Arabic", "Noto Sans Hebrew"];
|
||||
/// Thai-capable families: Windows (Leelawadee UI since Windows 8; Tahoma and Leelawadee on
|
||||
/// older systems), macOS (Thonburi, Sukhumvit Set) and Linux (Noto). Text in a font without Thai
|
||||
|
|
@ -76,6 +76,38 @@ pub const LAO_FAMILIES: &[&str] = &["Lao Sangam MN", "Lao MN", "Lao UI", "Noto S
|
|||
/// Myanmar/Burmese-capable families: macOS (Myanmar Sangam MN, Myanmar MN), Windows (Myanmar Text)
|
||||
/// and Linux (Noto).
|
||||
pub const MYANMAR_FAMILIES: &[&str] = &["Myanmar Sangam MN", "Myanmar MN", "Myanmar Text", "Noto Sans Myanmar"];
|
||||
/// Indic-script families for document text. Windows has `Nirmala UI` for all nine scripts (from
|
||||
/// Windows 8); macOS has the Kohinoor and Sangam MN families; Linux has Noto Sans. Indic text in a
|
||||
/// font without these glyphs had no candidate to fall back to and drew .notdef boxes (#218).
|
||||
pub const DEVANAGARI_FAMILIES: &[&str] = &["Nirmala UI", "Kohinoor Devanagari", "Devanagari Sangam MN", "Noto Sans Devanagari", "Noto Sans Devanagari UI"];
|
||||
/// Bengali-capable families (see [`DEVANAGARI_FAMILIES`]).
|
||||
pub const BENGALI_FAMILIES: &[&str] = &["Nirmala UI", "Kohinoor Bangla", "Bangla Sangam MN", "Noto Sans Bengali", "Noto Sans Bengali UI"];
|
||||
/// Gurmukhi-capable families (see [`DEVANAGARI_FAMILIES`]).
|
||||
pub const GURMUKHI_FAMILIES: &[&str] = &["Nirmala UI", "Gurmukhi Sangam MN", "Noto Sans Gurmukhi", "Noto Sans Gurmukhi UI"];
|
||||
/// Gujarati-capable families (see [`DEVANAGARI_FAMILIES`]).
|
||||
pub const GUJARATI_FAMILIES: &[&str] = &["Nirmala UI", "Kohinoor Gujarati", "Gujarati Sangam MN", "Noto Sans Gujarati", "Noto Sans Gujarati UI"];
|
||||
/// Tamil-capable families (see [`DEVANAGARI_FAMILIES`]).
|
||||
pub const TAMIL_FAMILIES: &[&str] = &["Nirmala UI", "Tamil Sangam MN", "Noto Sans Tamil", "Noto Sans Tamil UI"];
|
||||
/// Telugu-capable families (see [`DEVANAGARI_FAMILIES`]).
|
||||
pub const TELUGU_FAMILIES: &[&str] = &["Nirmala UI", "Kohinoor Telugu", "Telugu Sangam MN", "Noto Sans Telugu", "Noto Sans Telugu UI"];
|
||||
/// Kannada-capable families (see [`DEVANAGARI_FAMILIES`]).
|
||||
pub const KANNADA_FAMILIES: &[&str] = &["Nirmala UI", "Kannada Sangam MN", "Noto Sans Kannada", "Noto Sans Kannada UI"];
|
||||
/// Malayalam-capable families (see [`DEVANAGARI_FAMILIES`]).
|
||||
pub const MALAYALAM_FAMILIES: &[&str] = &["Nirmala UI", "Malayalam Sangam MN", "Noto Sans Malayalam", "Noto Sans Malayalam UI"];
|
||||
/// Sinhala-capable families (see [`DEVANAGARI_FAMILIES`]).
|
||||
pub const SINHALA_FAMILIES: &[&str] = &["Nirmala UI", "Sinhala Sangam MN", "Noto Sans Sinhala", "Noto Sans Sinhala UI"];
|
||||
/// All Indic-script families, in the order they join the fallback candidates.
|
||||
pub const INDIC_FAMILIES: &[&[&str]] = &[
|
||||
DEVANAGARI_FAMILIES,
|
||||
BENGALI_FAMILIES,
|
||||
GURMUKHI_FAMILIES,
|
||||
GUJARATI_FAMILIES,
|
||||
TAMIL_FAMILIES,
|
||||
TELUGU_FAMILIES,
|
||||
KANNADA_FAMILIES,
|
||||
MALAYALAM_FAMILIES,
|
||||
SINHALA_FAMILIES,
|
||||
];
|
||||
/// Broad-coverage and emoji fonts, tried after the CJK script fonts so shared Han characters get
|
||||
/// the locale's forms instead of Arial Unicode's.
|
||||
const FALLBACK_LAST: &[&str] = &["Arial Unicode MS", "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji"];
|
||||
|
|
@ -87,6 +119,14 @@ pub fn fallback_candidates(order: &[crate::cjk::CjkScript; 4]) -> Vec<&'static s
|
|||
v.extend_from_slice(KHMER_FAMILIES);
|
||||
v.extend_from_slice(LAO_FAMILIES);
|
||||
v.extend_from_slice(MYANMAR_FAMILIES);
|
||||
for fams in INDIC_FAMILIES {
|
||||
for f in *fams {
|
||||
// Nirmala UI covers all nine scripts and is listed by each; probe it once.
|
||||
if !v.contains(f) {
|
||||
v.push(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
for s in order {
|
||||
// craft-fonts' Japanese fonts (if built in) go ahead of the installed Japanese fonts, in
|
||||
// the Japanese slot of the locale order, so shared Han keeps the locale's forms.
|
||||
|
|
|
|||
|
|
@ -1655,6 +1655,59 @@ fn southeast_asian_fallback_candidates_cover_every_platform() {
|
|||
}
|
||||
}
|
||||
|
||||
/// The Indic scripts of document text, with conjuncts, matras and viramas (#218).
|
||||
const INDIC: &[(&str, &str, &[&str])] = &[
|
||||
("Devanagari", "हिन्दी भाषा क्षत्रिय श्री", fonts::DEVANAGARI_FAMILIES),
|
||||
("Bengali", "বাংলা ভাষা ক্ষত্রিয় শ্রী", fonts::BENGALI_FAMILIES),
|
||||
("Gurmukhi", "ਪੰਜਾਬੀ ਭਾਸ਼ਾ ਸਤਿ ਸ੍ਰੀ", fonts::GURMUKHI_FAMILIES),
|
||||
("Gujarati", "ગુજરાતી ભાષા ક્ષત્રિય શ્રી", fonts::GUJARATI_FAMILIES),
|
||||
("Tamil", "தமிழ் மொழி ஸ்ரீ கொடை", fonts::TAMIL_FAMILIES),
|
||||
("Telugu", "తెలుగు భాష క్షత్రియ శ్రీ", fonts::TELUGU_FAMILIES),
|
||||
("Kannada", "ಕನ್ನಡ ಭಾಷೆ ಕ್ಷತ್ರಿಯ ಶ್ರೀ", fonts::KANNADA_FAMILIES),
|
||||
("Malayalam", "മലയാളം ഭാഷ ക്ഷത്രിയ ശ്രീ", fonts::MALAYALAM_FAMILIES),
|
||||
("Sinhala", "සිංහල භාෂාව ක්ෂත්රිය ශ්රී", fonts::SINHALA_FAMILIES),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn indic_scripts_fall_back_to_installed_fonts() {
|
||||
for (name, sample, families) in INDIC {
|
||||
let mut e = TextEngine::with_system_fonts();
|
||||
let Some(hit) = families.iter().find(|f| e.fonts.has_family(f)) else {
|
||||
eprintln!("skipped: no {name}-capable font installed");
|
||||
continue;
|
||||
};
|
||||
let direct = TextLayer { text: (*sample).into(), font_family: hit.to_string(), size_pt: 24.0, ..Default::default() };
|
||||
let l = e.layout(&direct, 72.0);
|
||||
assert!(!l.glyphs.is_empty() && l.glyphs.iter().all(|g| g.id != 0), "{hit} is installed but shapes .notdef for {name}");
|
||||
let mixed = format!("Latin {sample}");
|
||||
let l = e.layout(&point(&mixed, 24.0), 72.0);
|
||||
assert!(l.glyphs.iter().all(|g| g.id != 0), "{name} in a Latin font drawn with .notdef although {hit} is installed");
|
||||
let latin = l.glyphs.first().map(|g| g.face);
|
||||
assert!(l.glyphs.iter().any(|g| Some(g.face) != latin), "Latin and {name} drawn with the same face");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indic_fallback_candidates_cover_every_platform() {
|
||||
// Logic-level half (runs without these fonts): every script has a Windows, macOS and Linux
|
||||
// family in the candidates, ahead of the broad last-resort fonts; the shared Nirmala UI is
|
||||
// probed once.
|
||||
for order in [crate::cjk::script_order(None), crate::cjk::script_order(Some("ja"))] {
|
||||
let fb = fonts::fallback_candidates(&order);
|
||||
let last = fb.iter().position(|f| *f == "Arial Unicode MS").unwrap();
|
||||
assert_eq!(fb.iter().filter(|f| **f == "Nirmala UI").count(), 1, "{fb:?}");
|
||||
for (name, _, families) in INDIC {
|
||||
assert!(families.contains(&"Nirmala UI"), "{name}");
|
||||
assert!(families.iter().any(|f| f.ends_with("Sangam MN") || f.starts_with("Kohinoor")), "{name}: no macOS family");
|
||||
assert!(families.iter().any(|f| f.starts_with("Noto Sans ")), "{name}: no Linux family");
|
||||
for fam in *families {
|
||||
let i = fb.iter().position(|f| f == fam).unwrap_or_else(|| panic!("{fam} missing from {fb:?}"));
|
||||
assert!(i < last, "{fam} after the last-resort fonts");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_served_script_fallback_joins_the_fallback_stack_once_it_arrives() {
|
||||
let Some(cairo) = crate::CRAFT_FONTS.iter().find(|f| f.family == "Cairo") else {
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ Unread: `general.color_picker`, `general.animated_zoom`, `general.zoom_resizes_w
|
|||
|---|---|---|---|---|
|
||||
| TYPE-218-1 | Type on a path: path shape, glyphs along the curve, caret follows, PSD round trip | missing | [#218](https://github.com/storytold/photocraft/issues/218) | doc text.rs:209 TextShape has Point and Box only |
|
||||
| TYPE-218-2 | Paragraph direction (Auto/LTR/RTL) in the panel and options bar; visual RTL caret | missing | [#218](https://github.com/storytold/photocraft/issues/218) | Engine has TextDirection (text.rs:155); no UI |
|
||||
| TYPE-218-3 | Thai/Khmer/Indic fallback fonts; golden shaping tests for complex scripts and RTL | partial | [#218](https://github.com/storytold/photocraft/issues/218) | Thai is covered: THAI_FAMILIES (fonts.rs) lists the Windows, macOS and Linux families and thai_in_latin_font_falls_back_to_installed_thai_font checks a sample with above and below marks (#1909); Arabic and Hebrew fall back too. Khmer and Indic have no fallback list, and there are no per-script golden shaping tests (only a bidi test) |
|
||||
| TYPE-218-3 | Thai/Khmer/Indic fallback fonts; golden shaping tests for complex scripts and RTL | partial | [#218](https://github.com/storytold/photocraft/issues/218) | Fallback fonts are covered for Thai, Khmer, Lao, Myanmar (THAI_FAMILIES, KHMER_FAMILIES, LAO_FAMILIES, MYANMAR_FAMILIES in fonts.rs; #1909, #3259), the nine Indic scripts (INDIC_FAMILIES: Nirmala UI, Kohinoor and Sangam MN, Noto Sans) and Arabic and Hebrew. Still missing: per-script golden shaping tests against reference output (only smoke tests that an installed font shapes without .notdef, plus a bidi test) |
|
||||
| TYPE-218-4 | Variable font axis sliders from the font's axes; opsz follows the point size | done | [#218](https://github.com/storytold/photocraft/issues/218) | One slider per axis of the style's font in the Character section (Properties and Window > Character), setting type.setStyle variations; opsz follows the type size unless set (layout.rs variation_list); a style or weight resets the wght axis (#2985). PSD does not round-trip axis values yet |
|
||||
| TYPE-218-5 | OpenType UI: Character panel buttons, Numerals and Stylistic Sets submenus | partial | [#218](https://github.com/storytold/photocraft/issues/218) | Type > OpenType toggles (type_extra_cmds.rs:512). Character panel (and the Properties Character section) has Photoshop's eight OpenType buttons: Standard Ligatures, Contextual Alternates, Discretionary Ligatures, Swash, Stylistic Alternates, Titling Alternates, Ordinals, Fractions, pressed while the feature is on at the selection and dispatching the type.openType.* commands (type_tool.rs opentype_row; character_panel_opentype_buttons_toggle_and_show_the_feature, #3752). Still missing: the Numerals and Stylistic Sets submenus, and greying out features the font lacks |
|
||||
| TYPE-218-6 | Manual pair kerning (Alt+arrows), optical kerning rendered, both round-trip PSD (#206) | done | [#218](https://github.com/storytold/photocraft/issues/218) | Alt+Left/Right at a collapsed caret kerns the pair (type_tool.rs kern_pair, 20 per step and 100 with Cmd; type_tool_alt_arrows_kern_the_pair). Optical kerning is computed from the glyph outlines in text/src/optical.rs and applied by layout.rs when the run's mode is Optical (optical_matches_photoshop, optical_kerning_tightens_open_pairs). Both modes and manual values round-trip through PSD (io/tests/psd_kerning.rs; the optical mode travels in Txt2, txt2_carries_optical_kerning) |
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ id = "TYPE-218-3"
|
|||
target = "Thai/Khmer/Indic fallback fonts; golden shaping tests for complex scripts and RTL"
|
||||
status = "partial"
|
||||
issue = 218
|
||||
note = "Thai is covered: THAI_FAMILIES (fonts.rs) lists the Windows, macOS and Linux families and thai_in_latin_font_falls_back_to_installed_thai_font checks a sample with above and below marks (#1909); Arabic and Hebrew fall back too. Khmer and Indic have no fallback list, and there are no per-script golden shaping tests (only a bidi test)"
|
||||
note = "Fallback fonts are covered for Thai, Khmer, Lao, Myanmar (THAI_FAMILIES, KHMER_FAMILIES, LAO_FAMILIES, MYANMAR_FAMILIES in fonts.rs; #1909, #3259), the nine Indic scripts (INDIC_FAMILIES: Nirmala UI, Kohinoor and Sangam MN, Noto Sans) and Arabic and Hebrew. Still missing: per-script golden shaping tests against reference output (only smoke tests that an installed font shapes without .notdef, plus a bidi test)"
|
||||
|
||||
[[item]]
|
||||
id = "TYPE-218-4"
|
||||
|
|
|
|||
Loading…
Reference in New Issue