From c492d524c5715ef3d1dd60e3ebf0a55dd23ca841 Mon Sep 17 00:00:00 2001 From: VladimirCeric <215285963+VladimirCeric@users.noreply.github.com> Date: Sat, 31 Oct 2026 20:38:07 +0100 Subject: [PATCH] Languages: Serbian (Cyrillic, Latin) and Croatian, with their quote marks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_011wCJ9DkszhZKxYL3LgcSfZ --- crates/doc/src/setup.rs | 17 +++++++++++++++++ crates/text/src/features.rs | 19 +++++++++++++++++++ docs/type-parity.md | 5 +++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/doc/src/setup.rs b/crates/doc/src/setup.rs index 62276ece9..d5abb8562 100644 --- a/crates/doc/src/setup.rs +++ b/crates/doc/src/setup.rs @@ -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); diff --git a/crates/text/src/features.rs b/crates/text/src/features.rs index dd9c68c75..e2424c59a 100644 --- a/crates/text/src/features.rs +++ b/crates/text/src/features.rs @@ -313,4 +313,23 @@ mod tests { let per_run: Vec = (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::>() + }; + 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); + } } diff --git a/docs/type-parity.md b/docs/type-parity.md index 6346c1b86..c48a1a208 100644 --- a/docs/type-parity.md +++ b/docs/type-parity.md @@ -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 |