From cfd6357de04638c0199fce26e967297f75a59f24 Mon Sep 17 00:00:00 2001 From: LU Jialin Date: Sun, 29 Dec 2024 22:11:55 -0800 Subject: [PATCH 1/4] simple refactor to access the chars --- crates/typst-library/src/model/numbering.rs | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/crates/typst-library/src/model/numbering.rs b/crates/typst-library/src/model/numbering.rs index 4e2fe4579..f03b37bb4 100644 --- a/crates/typst-library/src/model/numbering.rs +++ b/crates/typst-library/src/model/numbering.rs @@ -157,6 +157,38 @@ pub struct NumberingPattern { } impl NumberingPattern { + /// Parse braced pattern. i.e "{1}" or "{arabic}" to NumberingKind::Arabic + fn from_braced_str(pattern: &str) -> Option { + let mut pieces = EcoVec::new(); + let mut handled = 0; + let mut cursor = 0; + + for (i, c) in pattern.char_indices() { + if i < cursor { + continue; + } + + if let Some((kind, consumed)) = + NumberingKind::from_braced_numbering_pattern_str(&pattern[cursor..]) + { + let prefix = pattern[handled..i].into(); + pieces.push((prefix, kind)); + cursor += consumed; + handled = cursor; + continue; + }; + + cursor += c.len_utf8(); + } + + let suffix = pattern[handled..].into(); + if pieces.is_empty() { + return None; + } + + Some(Self { pieces, suffix, trimmed: false }) + } + /// Apply the pattern to the given number. pub fn apply(&self, numbers: &[usize]) -> EcoString { let mut fmt = EcoString::new(); @@ -213,7 +245,16 @@ impl NumberingPattern { impl FromStr for NumberingPattern { type Err = &'static str; + /// Parse freehand one-character pattern. i.e "1" to NumberingKind::Arabic fn from_str(pattern: &str) -> Result { + // if pattern contains curly-braces, prioritize parsing braced numbering pattern such as "{arabic}" or "{1}", + // otherwise fallback to brace-less parsing . + if pattern.contains('{') { + if let Some(result) = Self::from_braced_str(pattern) { + return Ok(result); + } + } + let mut pieces = EcoVec::new(); let mut handled = 0; From 8c89b664e5dc839c4963e29e5e04c1beafa297d3 Mon Sep 17 00:00:00 2001 From: LU Jialin Date: Mon, 30 Dec 2024 20:02:54 -0800 Subject: [PATCH 2/4] implement the pattern mapping fromg string to numbering kind --- crates/typst-library/src/model/numbering.rs | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/crates/typst-library/src/model/numbering.rs b/crates/typst-library/src/model/numbering.rs index f03b37bb4..7ea01a746 100644 --- a/crates/typst-library/src/model/numbering.rs +++ b/crates/typst-library/src/model/numbering.rs @@ -421,6 +421,56 @@ impl NumberingKind { } } + fn from_numbering_pattern_str(s: &str) -> Option { + if s.chars().count() == 1 { + return Self::from_char(s.chars().next().unwrap()); + } + + Some(match s { + "arabic" => Self::Arabic, + "latin" => Self::LowerLatin, + "Latin" => Self::UpperLatin, + "roman" => Self::LowerRoman, + "Roman" => Self::UpperRoman, + "greek" => Self::LowerGreek, + "Greek" => Self::UpperGreek, + "symbols" => Self::Symbol, + "hebrew" => Self::Hebrew, + "chinese" | "lowercase-chinese" => Self::LowerSimplifiedChinese, + "Chinese" | "uppercase-chinese" => Self::UpperSimplifiedChinese, + "hiragana" | "hiragana-aiueo" => Self::HiraganaAiueo, + "hiragana-iroha" => Self::HiraganaIroha, + "katakana" | "katakana-aiueo" => Self::KatakanaAiueo, + "katakana-iroha" => Self::KatakanaIroha, + "korean-jamo" => Self::KoreanJamo, + "korean" | "korean-syllable" => Self::KoreanSyllable, + "eastern-arabic" => Self::EasternArabic, + "persian" | "eastern-arabic-persian" => Self::EasternArabicPersian, + "devanagari" => Self::DevanagariNumber, + "bengali" => Self::BengaliNumber, + "bengali-letter" => Self::BengaliLetter, + "circled" => Self::CircledNumber, + "double-circled" => Self::DoubleCircledNumber, + _ => return None, + }) + } + + /// Parse a braced long-form numbering kind like "{arabic}" from a character slice. + /// Returns (kind, consumed_chars) if successful, None if not a valid braced string. + fn from_braced_numbering_pattern_str(s: &str) -> Option<(Self, usize)> { + // Need at least "{x}" (3 bytes minimum for UTF-8) + if s.len() < 3 || s.as_bytes()[0] != b'{' { + return None; + } + + let end_byte_idx = s.find('}')?; + if end_byte_idx < 2 { + return None; + } + + Some((Self::from_numbering_pattern_str(&s[1..end_byte_idx])?, end_byte_idx + 1)) + } + /// Apply the numbering to the given number. pub fn apply(self, n: usize) -> EcoString { match self { From e1c5b44c4899c13934eb49b2bd34170eb12e7a5e Mon Sep 17 00:00:00 2001 From: LU Jialin Date: Mon, 30 Dec 2024 20:04:03 -0800 Subject: [PATCH 3/4] update test (and test reference renderd png --- tests/ref/numbering-braced-format.png | Bin 0 -> 1039 bytes tests/suite/model/numbering.typ | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 tests/ref/numbering-braced-format.png diff --git a/tests/ref/numbering-braced-format.png b/tests/ref/numbering-braced-format.png new file mode 100644 index 0000000000000000000000000000000000000000..13381e2bc01941eecb6561754a6d61111bd587c2 GIT binary patch literal 1039 zcmV+q1n~QbP)&&i)dO?$J|snl~%S|EC#q@$x`y5$O3jKVf>gmqE;wy7#fG9IgfA- z@~K7>TeVwysGHH_QOc?jFEB3ovIE7d{L#4ggM>SYPfoNbgOk7X@zA zSnPS7nZvJWCv!(I72YJuM1R>Z019|OT!$ENTT4$O-zvLNK(uk@BwxFtOn)Xh_gdW@ z&j=V119nzk4MI}{3OLhrH5-UB<)}(aR^2eIp<=)}21_6pbg&z%|Di#+2 zd%;D^NipEsFN@#r`z&&i_|&nF6NS?%{ZwH3%F(k$PM~5>DOs1(=H+t9@UspJg4A&P zV}M!Te%!MG%YTatN5BIc0Ffrvm%jrv%`v}<0(V@oI?C>8O+MPG(s9h!I*2kcR&EAx z?uR~-jbgy9^=)5~bnK>}z;)dCIKmm{iZVSqjsLQ{)#u+ei*pvW!Dw{rM?(Mak;ql) zH8wX)!>3}vm&*PMevTg*p;d) z=C{7Fet5Rf2~=z=<=o|Dvs^A2mf`;oPo(6k0ry;hjSaJ0NII{J|9!LJQ-4r}d##5C zxvN#@@!!SGT6Zvd%X7GYpmT`%+NnEF7j z9-r_t7+y0}-?CPBJgi>y1lApFi`Fe{|EaDiZ}g*;H>Vd~rRC64+8RKt3F>zB0pm%d z<(;8qXl(l|mVK7NU?Jm7`*qJ=9K&b;eon$J75uulz}u_%LuptS0MSE$W48cfYbn{&Ar$1|5B|&tBjf};^$hkc zK5p)QK}YxN7i&jfDj42+U6Xp>rvvEOsdCn+)60Qt>F4eesE!Hh-CBR=#rt}n-thLU zpc&6zyu$kIY}kg6b{F7qZsLFZbFkez0jd9GQf{>j%kZ-Z{{vFx15nHi(iZ>#002ov JPDHLkV1i8S@0b7p literal 0 HcmV?d00001 diff --git a/tests/suite/model/numbering.typ b/tests/suite/model/numbering.typ index 6af989ff1..6eb8724a5 100644 --- a/tests/suite/model/numbering.typ +++ b/tests/suite/model/numbering.typ @@ -108,3 +108,11 @@ --- numbering-negative --- // Error: 17-19 number must be at least zero #numbering("1", -1) + +--- numbering-braced-format --- +#numbering("1", 13) \ +#numbering("{1}", 13) \ +#numbering("{arabic}", 13) \ +#numbering("article {1}", 13) \ +#numbering("article {arabic}", 13) \ +#numbering("prefix {arabic} suffix", 13) \ No newline at end of file From 3ed7218af521c5a4c3565c0eaa54871454331069 Mon Sep 17 00:00:00 2001 From: LU Jialin Date: Mon, 30 Dec 2024 21:36:26 -0800 Subject: [PATCH 4/4] update comment for doc --- crates/typst-library/src/model/numbering.rs | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/crates/typst-library/src/model/numbering.rs b/crates/typst-library/src/model/numbering.rs index 7ea01a746..1091e1225 100644 --- a/crates/typst-library/src/model/numbering.rs +++ b/crates/typst-library/src/model/numbering.rs @@ -64,6 +64,34 @@ pub fn numbering( /// `①`, and `⓵`. They are replaced by the number in the sequence, /// preserving the original case. /// + /// The counting symbols can also be specified using braces, either with the symbol + /// itself or with a descriptive name. Here is the complete list of available formats: + /// + /// - Arabic numerals (1, 2, 3, etc.): `1` or `{1}` or `{arabic}` + /// - Lowercase Latin letters (a, b, c, etc.): `a` or `{a}` or `{latin}` + /// - Uppercase Latin letters (A, B, C, etc.): `A` or `{A}` or `{Latin}` + /// - Lowercase Roman numerals (i, ii, iii, etc.): `i` or `{i}` or `{roman}` + /// - Uppercase Roman numerals (I, II, III, etc.): `I` or `{I}` or `{Roman}` + /// - Lowercase Greek numerals (α, β, γ, etc.): `α` or `{α}` or `{greek}` + /// - Uppercase Greek numerals (Α, Β, Γ, etc.): `Α` or `{Α}` or `{Greek}` + /// - Symbols (*, †, ‡, §, ¶, ‖): `*` or `{*}` or `{symbols}` + /// - Hebrew numerals with Geresh/Gershayim: `א` or `{א}` or `{hebrew}` + /// - Simplified Chinese standard numerals: `一` or `{一}` or `{chinese}` or `{lowercase-chinese}` + /// - Simplified Chinese "banknote" numerals: `壹` or `{壹}` or `{Chinese}` or `{uppercase-chinese}` + /// - Hiragana in gojūon order: `あ` or `{あ}` or `{hiragana}` or `{hiragana-aiueo}` + /// - Hiragana in iroha order: `い` or `{い}` or `{hiragana-iroha}` + /// - Katakana in gojūon order: `ア` or `{ア}` or `{katakana}` or `{katakana-aiueo}` + /// - Katakana in iroha order: `イ` or `{イ}` or `{katakana-iroha}` + /// - Korean jamo (ㄱ, ㄴ, ㄷ, etc.): `ㄱ` or `{ㄱ}` or `{korean-jamo}` + /// - Korean syllables (가, 나, 다, etc.): `가` or `{가}` or `{korean}` or `{korean-syllable}` + /// - Eastern Arabic numerals: `١` or `{١}` or `{eastern-arabic}` + /// - Eastern Arabic numerals (Persian/Urdu): `۱` or `{۱}` or `{persian}` or `{eastern-arabic-persian}` + /// - Devanagari numerals: `१` or `{१}` or `{devanagari}` + /// - Bengali numerals: `১` or `{১}` or `{bengali}` + /// - Bengali letters (ক, খ, গ, etc.): `ক` or `{ক}` or `{bengali-letter}` + /// - Circled numbers (①, ②, ③, etc.): `①` or `{①}` or `{circled}` + /// - Double-circled numbers (⓵, ⓶, ⓷, etc.): `⓵` or `{⓵}` or `{double-circled}` + /// /// The `*` character means that symbols should be used to count, in the /// order of `*`, `†`, `‡`, `§`, `¶`, `‖`. If there are more than six /// items, the number is represented using repeated symbols.