Languages: Serbian (Cyrillic, Latin) and Croatian, with their quote marks

Document Setup and Character › Language list Serbian: Cyrillic (sr-Cyrl),
Serbian: Latin (sr-Latn) and Croatian (hr). Text tagged Serbian is shaped
in it (#4940, #4959), so fonts draw their Serbian forms, the italic б,
г, д, п, т. Serbian quotes follow the Pravopis (Matica srpska 2010,
т. 168) and CLDR: „…” and ’…’; Croatian per CLDR: „…“ and ‚…‘.
Illustrator lists Croatian but not Serbian.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wCJ9DkszhZKxYL3LgcSfZ
This commit is contained in:
VladimirCeric
2026-11-01 07:38:03 +07:00
committed by bflatastic
co-authored by Claude Opus 5.5
parent 3d8677a71a
commit c492d524c5
3 changed files with 39 additions and 2 deletions
+17
View File
@@ -164,6 +164,11 @@ pub const LANGUAGES: &[(&str, &str, Quotes)] = &[
("Polish", "pl", Quotes { double: ['„', '”'], single: ['‚', '’'] }),
("Czech", "cs", Quotes { double: ['„', '“'], single: ['‚', '‘'] }),
("Russian", "ru", Quotes { double: ['«', '»'], single: ['„', '“'] }),
// Pravopis srpskoga jezika (Matica srpska, 2010, т. 168) and CLDR: „…” outside, ’…’ inside.
("Serbian: Cyrillic", "sr-Cyrl", Quotes { double: ['„', '”'], single: ['’', '’'] }),
("Serbian: Latin", "sr-Latn", Quotes { double: ['„', '”'], single: ['’', '’'] }),
// CLDR: „…“ outside, ‚…‘ inside.
("Croatian", "hr", Quotes { double: ['„', '“'], single: ['‚', '‘'] }),
("Turkish", "tr", Quotes { double: ['“', '”'], single: ['‘', '’'] }),
("Japanese", "ja", Quotes { double: ['「', '」'], single: ['『', '』'] }),
("Chinese", "zh", Quotes { double: ['“', '”'], single: ['‘', '’'] }),
@@ -373,6 +378,11 @@ mod tests {
(char_language_name(None), char_language_name(Some("gu")), char_language_name(Some("x-unknown"))),
(Some("English: USA"), Some("Gujarati"), None)
);
// Serbian in either script, and Croatian (which Illustrator also lists).
assert_eq!(char_language("Serbian: Cyrillic"), Ok(Some("sr-Cyrl")));
assert_eq!(char_language("sr-latn"), Ok(Some("sr-Latn")));
assert_eq!(char_language("Croatian"), Ok(Some("hr")));
assert_eq!(char_language_name(Some("sr-Cyrl")), Some("Serbian: Cyrillic"));
// Every entry has a tag of its own, and a name of its own.
for (i, (n, t, _)) in LANGUAGES.iter().enumerate() {
assert!(LANGUAGES.iter().skip(i + 1).all(|(n2, t2, _)| n2 != n && t2 != t), "{n} {t}");
@@ -387,6 +397,13 @@ mod tests {
let mut prev = Some('(');
assert_eq!(german.apply("\"a\"", &mut prev), "„a“");
assert_eq!(prev, Some('“'));
// Serbian closes high (”), with ’ on both sides of an inner quote; Croatian as German.
for name in ["Serbian: Cyrillic", "Serbian: Latin"] {
let mut prev = None;
assert_eq!(language_quotes(name).unwrap().apply("\"a 'b' c\"", &mut prev), "„a ’b’ c”", "{name}");
}
let mut prev = None;
assert_eq!(language_quotes("croatian").unwrap().apply("\"a 'b' c\"", &mut prev), "„a ‚b‘ c“");
// Converting in pieces keeps the context.
let mut prev = None;
let a = Quotes::ENGLISH.apply("\"", &mut prev);
+19
View File
@@ -313,4 +313,23 @@ mod tests {
let per_run: Vec<usize> = (0..2).map(|r| glyphs.iter().filter(|g| g.run == r).count()).collect();
assert_eq!(per_run, [2, 1]);
}
/// Serbian text gets the font's Serbian forms (`locl` for `SRB`): in the bundled Source Sans
/// 3 Italic, б, г, д, п, т differ from the Russian ones, in either Serbian list entry.
#[test]
fn serbian_gets_the_fonts_serbian_forms() {
let st = |language: &str| CharStyle {
font_family: "Source Sans 3".into(),
font_style: "Italic".into(),
language: Some(language.to_string()),
..Default::default()
};
let outlines = |language| {
layout(FontDb::global(), &TextObject::point(Point::ZERO, "бгдпт", st(language))).glyphs.into_iter().map(|g| g.outline).collect::<Vec<_>>()
};
let (serbian, russian) = (outlines("sr-Cyrl"), outlines("ru"));
assert_eq!((serbian.len(), russian.len()), (5, 5));
assert!(serbian.iter().zip(&russian).all(|(s, r)| s != r), "every letter in its Serbian form");
assert_eq!(outlines("sr-Latn"), serbian);
}
}
+3 -2
View File
@@ -1,6 +1,6 @@
# Type parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Area Type Options › Text Flow by rows or by columns, #4978; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Serbian in Cyrillic and Latin and Croatian among the languages, with their quote marks; previously minor: Digits: the native digits of the Indic scripts, #4942; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
Type is Illustrator's largest single area by use (weight 13 of 105 across "Type core" and "Type advanced" in
[target-app-parity.md](target-app-parity.md)). This checklist lists the features one by one. Status is from the
@@ -60,7 +60,7 @@ against Illustrator's public documentation; **estimated** unless noted.
| CJK: Proportional Metrics (`palt` in horizontal type, `vpal` in vertical type) | done | #966 |
| CJK: manual tate-chu-yoko | done | Character panel menu › Tate-chu-yoko (`text.setFormat` / `text.setRangeStyle {tateChuYoko}`), with Show East Asian Options: marked characters stand side by side across one em of a vertical column, squeezed to the em when wider; two or three half-width digits still do so without the mark; horizontal type keeps the mark and does not move them. Up/Down and Left/Right settings remain. Evidence: `tests_vertical::manual_tate_chu_yoko_sets_marked_characters_across_one_em`, `typecmd::tate_chu_yoko_is_a_character_attribute_saved_only_when_on` |
| CJK: ruby, manual tate-chu-yoko | partial | ruby (ふりがな) for live text (`type.ruby`): a reading over a byte range, above (right of the column in vertical type) or below, start/centre/end alignment, size, overhang or squeeze; saved natively, laid out with its line growing, drawn on the canvas, in SVG and PDF (as outlines where ruby is present), in EPS, EMF/WMF and legacy `.ai` (legacy `.ai` writes type with ruby as outlines), and kept by Create Outlines, Expand Appearance and flattening, each glyph where the layout puts it (`typecmd::ruby_tests::create_outlines_keeps_the_ruby_where_it_is_laid_out`); manual tate-chu-yoko missing | #633 |
| Language per character (Character panel › Language, character styles), text shaped in it | done | `text.setFormat` / `text.setRangeStyle {language}` by name or BCP 47 tag, one of Document Setup's languages (Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu added, #4943); the shaper gets the tag, so the font's language forms apply (OpenType `locl`: Turkish keeps `fi` apart and the dot on its i, #4940); new type takes the document's language; saved as the tag only when set, carried by character styles (#4946). Not yet: hyphenation and spelling by it, `.ai`/PDF import of a text's language, paragraph styles. Evidence: `features::tests::language_picks_the_fonts_forms`, `typecmd::area_tests::language_is_a_character_attribute_saved_only_when_set` |
| Language per character (Character panel › Language, character styles), text shaped in it | done | `text.setFormat` / `text.setRangeStyle {language}` by name or BCP 47 tag, one of Document Setup's languages (Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu added, #4943; Serbian: Cyrillic, Serbian: Latin and Croatian, beyond Illustrator's list for Serbian); the shaper gets the tag, so the font's language forms apply (OpenType `locl`: Turkish keeps `fi` apart and the dot on its i, #4940); new type takes the document's language; saved as the tag only when set, carried by character styles (#4946). Not yet: hyphenation and spelling by it, `.ai`/PDF import of a text's language, paragraph styles. Evidence: `features::tests::language_picks_the_fonts_forms`, `features::tests::serbian_gets_the_fonts_serbian_forms`, `typecmd::area_tests::language_is_a_character_attribute_saved_only_when_set` |
| No Break | done | Character panel menu (`text.setFormat` / `text.setRangeStyle {noBreak}`): the marked characters stay on one line, including across a space; a run that does not fit after earlier text moves together, and a run longer than the line stays together. Evidence: `layout::wrapping_tests::no_break_keeps_marked_characters_on_one_line`, `tests::no_break_keeps_marked_words_on_one_line`, `typecmd::no_break_is_a_character_attribute_saved_only_when_on` |
| CJK: ruby | missing | #633 |
| Inline symbols in text | beyond Illustrator | not scored |
@@ -72,6 +72,7 @@ Character Styles menu New/Redefine and footer New capture the first selected cha
| Date | Change | Summary |
|---|---|---|
| 2026-11-01 | minor | Serbian: Cyrillic (`sr-Cyrl`), Serbian: Latin (`sr-Latn`) and Croatian (`hr`) in the language list, with their quote marks (Serbian „…” and ’…’ per the Pravopis and CLDR, Croatian „…“ and ‚…‘ per CLDR); Serbian text gets the fonts' Serbian forms (italic б, г, д, п, т). the reference app lists Croatian but not Serbian; scores unchanged |
| 2026-11-01 | minor | Area Type Options › Text Flow (#4978): By Rows / By Columns radio buttons and `text.areaOptions {flow}`; area type with several rows and columns fills them across each row first or down each column first (the default, so existing documents are unchanged; saved only when by rows); scores unchanged |
| 2026-11-01 | minor | Digits (#4942): the native digits of Devanagari, Bengali, Gurmukhi, Gujarati, Odia, Tamil, Telugu, Kannada and Malayalam in the Character panel menu › Digits and `{digits}`; scores unchanged |
| 2026-11-01 | minor | Language per character (#4946): Character panel › Language, `text.setFormat` / `text.setRangeStyle {language}`, character styles; text is shaped in its language (`locl`, #4940); new type takes the document's; Indian languages in the list (#4943); scores unchanged |