mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Remove Case
s from NumberingPatterns (#5059)
This commit is contained in:
parent
7ff83db757
commit
1a24b29d86
@ -9,7 +9,6 @@ use typst::foundations::Label;
|
|||||||
use typst::introspection::Location;
|
use typst::introspection::Location;
|
||||||
use typst::layout::{Abs, Page};
|
use typst::layout::{Abs, Page};
|
||||||
use typst::model::{Destination, Numbering};
|
use typst::model::{Destination, Numbering};
|
||||||
use typst::text::Case;
|
|
||||||
|
|
||||||
use crate::content;
|
use crate::content;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -246,26 +245,23 @@ impl PdfPageLabel {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let (prefix, kind, case) = pat.pieces.first()?;
|
let (prefix, kind) = pat.pieces.first()?;
|
||||||
|
|
||||||
// If there is a suffix, we cannot use the common style optimisation,
|
// If there is a suffix, we cannot use the common style optimisation,
|
||||||
// since PDF does not provide a suffix field.
|
// since PDF does not provide a suffix field.
|
||||||
let mut style = None;
|
let style = if pat.suffix.is_empty() {
|
||||||
if pat.suffix.is_empty() {
|
|
||||||
use {typst::model::NumberingKind as Kind, PdfPageLabelStyle as Style};
|
use {typst::model::NumberingKind as Kind, PdfPageLabelStyle as Style};
|
||||||
match (kind, case) {
|
match kind {
|
||||||
(Kind::Arabic, _) => style = Some(Style::Arabic),
|
Kind::Arabic => Some(Style::Arabic),
|
||||||
(Kind::Roman, Case::Lower) => style = Some(Style::LowerRoman),
|
Kind::LowerRoman => Some(Style::LowerRoman),
|
||||||
(Kind::Roman, Case::Upper) => style = Some(Style::UpperRoman),
|
Kind::UpperRoman => Some(Style::UpperRoman),
|
||||||
(Kind::Letter, Case::Lower) if number <= 26 => {
|
Kind::LowerLatin if number <= 26 => Some(Style::LowerAlpha),
|
||||||
style = Some(Style::LowerAlpha)
|
Kind::LowerLatin if number <= 26 => Some(Style::UpperAlpha),
|
||||||
}
|
_ => None,
|
||||||
(Kind::Letter, Case::Upper) if number <= 26 => {
|
|
||||||
style = Some(Style::UpperAlpha)
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Prefix and offset depend on the style: If it is supported by the PDF
|
// Prefix and offset depend on the style: If it is supported by the PDF
|
||||||
// spec, we use the given prefix and an offset. Otherwise, everything
|
// spec, we use the given prefix and an offset. Otherwise, everything
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chinese_number::{ChineseCase, ChineseCountMethod, ChineseVariant, NumberToChinese};
|
use chinese_number::{
|
||||||
|
from_usize_to_chinese_ten_thousand as usize_to_chinese, ChineseCase, ChineseVariant,
|
||||||
|
};
|
||||||
use comemo::Tracked;
|
use comemo::Tracked;
|
||||||
use ecow::{eco_format, EcoString, EcoVec};
|
use ecow::{eco_format, EcoString, EcoVec};
|
||||||
use smallvec::{smallvec, SmallVec};
|
|
||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
@ -150,7 +151,7 @@ cast! {
|
|||||||
/// - `(I)`
|
/// - `(I)`
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct NumberingPattern {
|
pub struct NumberingPattern {
|
||||||
pub pieces: EcoVec<(EcoString, NumberingKind, Case)>,
|
pub pieces: EcoVec<(EcoString, NumberingKind)>,
|
||||||
pub suffix: EcoString,
|
pub suffix: EcoString,
|
||||||
trimmed: bool,
|
trimmed: bool,
|
||||||
}
|
}
|
||||||
@ -161,24 +162,21 @@ impl NumberingPattern {
|
|||||||
let mut fmt = EcoString::new();
|
let mut fmt = EcoString::new();
|
||||||
let mut numbers = numbers.iter();
|
let mut numbers = numbers.iter();
|
||||||
|
|
||||||
for (i, ((prefix, kind, case), &n)) in
|
for (i, ((prefix, kind), &n)) in self.pieces.iter().zip(&mut numbers).enumerate()
|
||||||
self.pieces.iter().zip(&mut numbers).enumerate()
|
|
||||||
{
|
{
|
||||||
if i > 0 || !self.trimmed {
|
if i > 0 || !self.trimmed {
|
||||||
fmt.push_str(prefix);
|
fmt.push_str(prefix);
|
||||||
}
|
}
|
||||||
fmt.push_str(&kind.apply(n, *case));
|
fmt.push_str(&kind.apply(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((prefix, kind, case), &n) in
|
for ((prefix, kind), &n) in self.pieces.last().into_iter().cycle().zip(numbers) {
|
||||||
self.pieces.last().into_iter().cycle().zip(numbers)
|
|
||||||
{
|
|
||||||
if prefix.is_empty() {
|
if prefix.is_empty() {
|
||||||
fmt.push_str(&self.suffix);
|
fmt.push_str(&self.suffix);
|
||||||
} else {
|
} else {
|
||||||
fmt.push_str(prefix);
|
fmt.push_str(prefix);
|
||||||
}
|
}
|
||||||
fmt.push_str(&kind.apply(n, *case));
|
fmt.push_str(&kind.apply(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.trimmed {
|
if !self.trimmed {
|
||||||
@ -191,16 +189,16 @@ impl NumberingPattern {
|
|||||||
/// Apply only the k-th segment of the pattern to a number.
|
/// Apply only the k-th segment of the pattern to a number.
|
||||||
pub fn apply_kth(&self, k: usize, number: usize) -> EcoString {
|
pub fn apply_kth(&self, k: usize, number: usize) -> EcoString {
|
||||||
let mut fmt = EcoString::new();
|
let mut fmt = EcoString::new();
|
||||||
if let Some((prefix, _, _)) = self.pieces.first() {
|
if let Some((prefix, _)) = self.pieces.first() {
|
||||||
fmt.push_str(prefix);
|
fmt.push_str(prefix);
|
||||||
}
|
}
|
||||||
if let Some((_, kind, case)) = self
|
if let Some((_, kind)) = self
|
||||||
.pieces
|
.pieces
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self.pieces.last().into_iter().cycle())
|
.chain(self.pieces.last().into_iter().cycle())
|
||||||
.nth(k)
|
.nth(k)
|
||||||
{
|
{
|
||||||
fmt.push_str(&kind.apply(number, *case));
|
fmt.push_str(&kind.apply(number));
|
||||||
}
|
}
|
||||||
fmt.push_str(&self.suffix);
|
fmt.push_str(&self.suffix);
|
||||||
fmt
|
fmt
|
||||||
@ -220,14 +218,12 @@ impl FromStr for NumberingPattern {
|
|||||||
let mut handled = 0;
|
let mut handled = 0;
|
||||||
|
|
||||||
for (i, c) in pattern.char_indices() {
|
for (i, c) in pattern.char_indices() {
|
||||||
let Some(kind) = NumberingKind::from_char(c.to_ascii_lowercase()) else {
|
let Some(kind) = NumberingKind::from_char(c) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let prefix = pattern[handled..i].into();
|
let prefix = pattern[handled..i].into();
|
||||||
let case =
|
pieces.push((prefix, kind));
|
||||||
if c.is_uppercase() || c == '壹' { Case::Upper } else { Case::Lower };
|
|
||||||
pieces.push((prefix, kind, case));
|
|
||||||
handled = c.len_utf8() + i;
|
handled = c.len_utf8() + i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,13 +240,9 @@ cast! {
|
|||||||
NumberingPattern,
|
NumberingPattern,
|
||||||
self => {
|
self => {
|
||||||
let mut pat = EcoString::new();
|
let mut pat = EcoString::new();
|
||||||
for (prefix, kind, case) in &self.pieces {
|
for (prefix, kind) in &self.pieces {
|
||||||
pat.push_str(prefix);
|
pat.push_str(prefix);
|
||||||
let mut c = kind.to_char();
|
pat.push(kind.to_char());
|
||||||
if *case == Case::Upper {
|
|
||||||
c = c.to_ascii_uppercase();
|
|
||||||
}
|
|
||||||
pat.push(c);
|
|
||||||
}
|
}
|
||||||
pat.push_str(&self.suffix);
|
pat.push_str(&self.suffix);
|
||||||
pat.into_value()
|
pat.into_value()
|
||||||
@ -263,26 +255,36 @@ cast! {
|
|||||||
pub enum NumberingKind {
|
pub enum NumberingKind {
|
||||||
/// Arabic numerals (1, 2, 3, etc.).
|
/// Arabic numerals (1, 2, 3, etc.).
|
||||||
Arabic,
|
Arabic,
|
||||||
/// Latin letters (A, B, C, etc.). Items beyond Z use multiple symbols.
|
/// Lowercase Latin letters (a, b, c, etc.). Items beyond z use base-26.
|
||||||
/// Uses both cases.
|
LowerLatin,
|
||||||
Letter,
|
/// Uppercase Latin letters (A, B, C, etc.). Items beyond Z use base-26.
|
||||||
/// Roman numerals (I, II, III, etc.). Uses both cases.
|
UpperLatin,
|
||||||
Roman,
|
/// Lowercase Roman numerals (i, ii, iii, etc.).
|
||||||
/// The symbols *, †, ‡, §, ¶, and ‖. Further items use multiple symbols.
|
LowerRoman,
|
||||||
|
/// Uppercase Roman numerals (I, II, III, etc.).
|
||||||
|
UpperRoman,
|
||||||
|
/// Paragraph/note-like symbols: *, †, ‡, §, ¶, and ‖. Further items use repeated symbols.
|
||||||
Symbol,
|
Symbol,
|
||||||
/// Hebrew numerals.
|
/// Hebrew numerals, including Geresh/Gershayim.
|
||||||
Hebrew,
|
Hebrew,
|
||||||
/// Simplified Chinese numerals. Uses standard numerals for lowercase and
|
/// Simplified Chinese standard numerals. This corresponds to the
|
||||||
/// "banknote" numerals for uppercase.
|
/// `ChineseCase::Lower` variant.
|
||||||
SimplifiedChinese,
|
LowerSimplifiedChinese,
|
||||||
|
/// Simplified Chinese "banknote" numerals. This corresponds to the
|
||||||
|
/// `ChineseCase::Upper` variant.
|
||||||
|
UpperSimplifiedChinese,
|
||||||
// TODO: Pick the numbering pattern based on languages choice.
|
// TODO: Pick the numbering pattern based on languages choice.
|
||||||
// As the first character of Simplified and Traditional Chinese numbering
|
// As the first character of Simplified and Traditional Chinese numbering
|
||||||
// are the same, we are unable to determine if the context requires
|
// are the same, we are unable to determine if the context requires
|
||||||
// Simplified or Traditional by only looking at this character.
|
// Simplified or Traditional by only looking at this character.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
/// Traditional Chinese numerals. Uses standard numerals for lowercase and
|
/// Traditional Chinese standard numerals. This corresponds to the
|
||||||
/// "banknote" numerals for uppercase.
|
/// `ChineseCase::Lower` variant.
|
||||||
TraditionalChinese,
|
LowerTraditionalChinese,
|
||||||
|
#[allow(unused)]
|
||||||
|
/// Traditional Chinese "banknote" numerals. This corresponds to the
|
||||||
|
/// `ChineseCase::Upper` variant.
|
||||||
|
UpperTraditionalChinese,
|
||||||
/// Hiragana in the gojūon order. Includes n but excludes wi and we.
|
/// Hiragana in the gojūon order. Includes n but excludes wi and we.
|
||||||
HiraganaAiueo,
|
HiraganaAiueo,
|
||||||
/// Hiragana in the iroha order. Includes wi and we but excludes n.
|
/// Hiragana in the iroha order. Includes wi and we but excludes n.
|
||||||
@ -312,15 +314,18 @@ pub enum NumberingKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NumberingKind {
|
impl NumberingKind {
|
||||||
/// Create a numbering kind from a lowercase character.
|
/// Create a numbering kind from a representative character.
|
||||||
pub fn from_char(c: char) -> Option<Self> {
|
pub fn from_char(c: char) -> Option<Self> {
|
||||||
Some(match c {
|
Some(match c {
|
||||||
'1' => NumberingKind::Arabic,
|
'1' => NumberingKind::Arabic,
|
||||||
'a' => NumberingKind::Letter,
|
'a' => NumberingKind::LowerLatin,
|
||||||
'i' => NumberingKind::Roman,
|
'A' => NumberingKind::UpperLatin,
|
||||||
|
'i' => NumberingKind::LowerRoman,
|
||||||
|
'I' => NumberingKind::UpperRoman,
|
||||||
'*' => NumberingKind::Symbol,
|
'*' => NumberingKind::Symbol,
|
||||||
'א' => NumberingKind::Hebrew,
|
'א' => NumberingKind::Hebrew,
|
||||||
'一' | '壹' => NumberingKind::SimplifiedChinese,
|
'一' => NumberingKind::LowerSimplifiedChinese,
|
||||||
|
'壹' => NumberingKind::UpperSimplifiedChinese,
|
||||||
'あ' => NumberingKind::HiraganaAiueo,
|
'あ' => NumberingKind::HiraganaAiueo,
|
||||||
'い' => NumberingKind::HiraganaIroha,
|
'い' => NumberingKind::HiraganaIroha,
|
||||||
'ア' => NumberingKind::KatakanaAiueo,
|
'ア' => NumberingKind::KatakanaAiueo,
|
||||||
@ -338,16 +343,18 @@ impl NumberingKind {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The lowercase character for this numbering kind.
|
/// The representative character for this numbering kind.
|
||||||
pub fn to_char(self) -> char {
|
pub fn to_char(self) -> char {
|
||||||
match self {
|
match self {
|
||||||
Self::Arabic => '1',
|
Self::Arabic => '1',
|
||||||
Self::Letter => 'a',
|
Self::LowerLatin => 'a',
|
||||||
Self::Roman => 'i',
|
Self::UpperLatin => 'A',
|
||||||
|
Self::LowerRoman => 'i',
|
||||||
|
Self::UpperRoman => 'I',
|
||||||
Self::Symbol => '*',
|
Self::Symbol => '*',
|
||||||
Self::Hebrew => 'א',
|
Self::Hebrew => 'א',
|
||||||
Self::SimplifiedChinese => '一',
|
Self::LowerSimplifiedChinese | Self::LowerTraditionalChinese => '一',
|
||||||
Self::TraditionalChinese => '一',
|
Self::UpperSimplifiedChinese | Self::UpperTraditionalChinese => '壹',
|
||||||
Self::HiraganaAiueo => 'あ',
|
Self::HiraganaAiueo => 'あ',
|
||||||
Self::HiraganaIroha => 'い',
|
Self::HiraganaIroha => 'い',
|
||||||
Self::KatakanaAiueo => 'ア',
|
Self::KatakanaAiueo => 'ア',
|
||||||
@ -365,109 +372,11 @@ impl NumberingKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the numbering to the given number.
|
/// Apply the numbering to the given number.
|
||||||
pub fn apply(self, mut n: usize, case: Case) -> EcoString {
|
pub fn apply(self, n: usize) -> EcoString {
|
||||||
match self {
|
match self {
|
||||||
Self::Arabic => {
|
Self::Arabic => eco_format!("{n}"),
|
||||||
eco_format!("{n}")
|
Self::LowerRoman => roman_numeral(n, Case::Lower),
|
||||||
}
|
Self::UpperRoman => roman_numeral(n, Case::Upper),
|
||||||
Self::Letter => zeroless::<26>(
|
|
||||||
|x| match case {
|
|
||||||
Case::Lower => char::from(b'a' + x as u8),
|
|
||||||
Case::Upper => char::from(b'A' + x as u8),
|
|
||||||
},
|
|
||||||
n,
|
|
||||||
),
|
|
||||||
Self::HiraganaAiueo => zeroless::<46>(
|
|
||||||
|x| {
|
|
||||||
[
|
|
||||||
'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ',
|
|
||||||
'し', 'す', 'せ', 'そ', 'た', 'ち', 'つ', 'て', 'と', 'な', 'に',
|
|
||||||
'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'へ', 'ほ', 'ま', 'み', 'む',
|
|
||||||
'め', 'も', 'や', 'ゆ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ', 'わ',
|
|
||||||
'を', 'ん',
|
|
||||||
][x]
|
|
||||||
},
|
|
||||||
n,
|
|
||||||
),
|
|
||||||
Self::HiraganaIroha => zeroless::<47>(
|
|
||||||
|x| {
|
|
||||||
[
|
|
||||||
'い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る',
|
|
||||||
'を', 'わ', 'か', 'よ', 'た', 'れ', 'そ', 'つ', 'ね', 'な', 'ら',
|
|
||||||
'む', 'う', 'ゐ', 'の', 'お', 'く', 'や', 'ま', 'け', 'ふ', 'こ',
|
|
||||||
'え', 'て', 'あ', 'さ', 'き', 'ゆ', 'め', 'み', 'し', 'ゑ', 'ひ',
|
|
||||||
'も', 'せ', 'す',
|
|
||||||
][x]
|
|
||||||
},
|
|
||||||
n,
|
|
||||||
),
|
|
||||||
Self::KatakanaAiueo => zeroless::<46>(
|
|
||||||
|x| {
|
|
||||||
[
|
|
||||||
'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ',
|
|
||||||
'シ', 'ス', 'セ', 'ソ', 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ',
|
|
||||||
'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'マ', 'ミ', 'ム',
|
|
||||||
'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ',
|
|
||||||
'ヲ', 'ン',
|
|
||||||
][x]
|
|
||||||
},
|
|
||||||
n,
|
|
||||||
),
|
|
||||||
Self::KatakanaIroha => zeroless::<47>(
|
|
||||||
|x| {
|
|
||||||
[
|
|
||||||
'イ', 'ロ', 'ハ', 'ニ', 'ホ', 'ヘ', 'ト', 'チ', 'リ', 'ヌ', 'ル',
|
|
||||||
'ヲ', 'ワ', 'カ', 'ヨ', 'タ', 'レ', 'ソ', 'ツ', 'ネ', 'ナ', 'ラ',
|
|
||||||
'ム', 'ウ', 'ヰ', 'ノ', 'オ', 'ク', 'ヤ', 'マ', 'ケ', 'フ', 'コ',
|
|
||||||
'エ', 'テ', 'ア', 'サ', 'キ', 'ユ', 'メ', 'ミ', 'シ', 'ヱ', 'ヒ',
|
|
||||||
'モ', 'セ', 'ス',
|
|
||||||
][x]
|
|
||||||
},
|
|
||||||
n,
|
|
||||||
),
|
|
||||||
Self::Roman => {
|
|
||||||
if n == 0 {
|
|
||||||
return 'N'.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adapted from Yann Villessuzanne's roman.rs under the
|
|
||||||
// Unlicense, at https://github.com/linfir/roman.rs/
|
|
||||||
let mut fmt = EcoString::new();
|
|
||||||
for &(name, value) in &[
|
|
||||||
("M̅", 1000000),
|
|
||||||
("D̅", 500000),
|
|
||||||
("C̅", 100000),
|
|
||||||
("L̅", 50000),
|
|
||||||
("X̅", 10000),
|
|
||||||
("V̅", 5000),
|
|
||||||
("I̅V̅", 4000),
|
|
||||||
("M", 1000),
|
|
||||||
("CM", 900),
|
|
||||||
("D", 500),
|
|
||||||
("CD", 400),
|
|
||||||
("C", 100),
|
|
||||||
("XC", 90),
|
|
||||||
("L", 50),
|
|
||||||
("XL", 40),
|
|
||||||
("X", 10),
|
|
||||||
("IX", 9),
|
|
||||||
("V", 5),
|
|
||||||
("IV", 4),
|
|
||||||
("I", 1),
|
|
||||||
] {
|
|
||||||
while n >= value {
|
|
||||||
n -= value;
|
|
||||||
for c in name.chars() {
|
|
||||||
match case {
|
|
||||||
Case::Lower => fmt.extend(c.to_lowercase()),
|
|
||||||
Case::Upper => fmt.push(c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt
|
|
||||||
}
|
|
||||||
Self::Symbol => {
|
Self::Symbol => {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return '-'.into();
|
return '-'.into();
|
||||||
@ -478,130 +387,221 @@ impl NumberingKind {
|
|||||||
let amount = ((n - 1) / SYMBOLS.len()) + 1;
|
let amount = ((n - 1) / SYMBOLS.len()) + 1;
|
||||||
std::iter::repeat(symbol).take(amount).collect()
|
std::iter::repeat(symbol).take(amount).collect()
|
||||||
}
|
}
|
||||||
Self::Hebrew => {
|
Self::Hebrew => hebrew_numeral(n),
|
||||||
if n == 0 {
|
|
||||||
return '-'.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut fmt = EcoString::new();
|
Self::LowerLatin => zeroless(
|
||||||
'outer: for &(name, value) in &[
|
[
|
||||||
('ת', 400),
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||||
('ש', 300),
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||||
('ר', 200),
|
],
|
||||||
('ק', 100),
|
|
||||||
('צ', 90),
|
|
||||||
('פ', 80),
|
|
||||||
('ע', 70),
|
|
||||||
('ס', 60),
|
|
||||||
('נ', 50),
|
|
||||||
('מ', 40),
|
|
||||||
('ל', 30),
|
|
||||||
('כ', 20),
|
|
||||||
('י', 10),
|
|
||||||
('ט', 9),
|
|
||||||
('ח', 8),
|
|
||||||
('ז', 7),
|
|
||||||
('ו', 6),
|
|
||||||
('ה', 5),
|
|
||||||
('ד', 4),
|
|
||||||
('ג', 3),
|
|
||||||
('ב', 2),
|
|
||||||
('א', 1),
|
|
||||||
] {
|
|
||||||
while n >= value {
|
|
||||||
match n {
|
|
||||||
15 => fmt.push_str("ט״ו"),
|
|
||||||
16 => fmt.push_str("ט״ז"),
|
|
||||||
_ => {
|
|
||||||
let append_geresh = n == value && fmt.is_empty();
|
|
||||||
if n == value && !fmt.is_empty() {
|
|
||||||
fmt.push('״');
|
|
||||||
}
|
|
||||||
fmt.push(name);
|
|
||||||
if append_geresh {
|
|
||||||
fmt.push('׳');
|
|
||||||
}
|
|
||||||
|
|
||||||
n -= value;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt
|
|
||||||
}
|
|
||||||
l @ (Self::SimplifiedChinese | Self::TraditionalChinese) => {
|
|
||||||
let chinese_case = match case {
|
|
||||||
Case::Lower => ChineseCase::Lower,
|
|
||||||
Case::Upper => ChineseCase::Upper,
|
|
||||||
};
|
|
||||||
|
|
||||||
match (n as u64).to_chinese(
|
|
||||||
match l {
|
|
||||||
Self::SimplifiedChinese => ChineseVariant::Simple,
|
|
||||||
Self::TraditionalChinese => ChineseVariant::Traditional,
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
chinese_case,
|
|
||||||
ChineseCountMethod::TenThousand,
|
|
||||||
) {
|
|
||||||
Ok(num_str) => EcoString::from(num_str),
|
|
||||||
Err(_) => '-'.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::KoreanJamo => zeroless::<14>(
|
|
||||||
|x| {
|
|
||||||
[
|
|
||||||
'ㄱ', 'ㄴ', 'ㄷ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅅ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ',
|
|
||||||
'ㅌ', 'ㅍ', 'ㅎ',
|
|
||||||
][x]
|
|
||||||
},
|
|
||||||
n,
|
n,
|
||||||
),
|
),
|
||||||
Self::KoreanSyllable => zeroless::<14>(
|
Self::UpperLatin => zeroless(
|
||||||
|x| {
|
[
|
||||||
[
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||||
'가', '나', '다', '라', '마', '바', '사', '아', '자', '차', '카',
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
'타', '파', '하',
|
],
|
||||||
][x]
|
|
||||||
},
|
|
||||||
n,
|
n,
|
||||||
),
|
),
|
||||||
|
Self::HiraganaAiueo => zeroless(
|
||||||
|
[
|
||||||
|
'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ',
|
||||||
|
'し', 'す', 'せ', 'そ', 'た', 'ち', 'つ', 'て', 'と', 'な', 'に',
|
||||||
|
'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'へ', 'ほ', 'ま', 'み', 'む',
|
||||||
|
'め', 'も', 'や', 'ゆ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ', 'わ',
|
||||||
|
'を', 'ん',
|
||||||
|
],
|
||||||
|
n,
|
||||||
|
),
|
||||||
|
Self::HiraganaIroha => zeroless(
|
||||||
|
[
|
||||||
|
'い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る',
|
||||||
|
'を', 'わ', 'か', 'よ', 'た', 'れ', 'そ', 'つ', 'ね', 'な', 'ら',
|
||||||
|
'む', 'う', 'ゐ', 'の', 'お', 'く', 'や', 'ま', 'け', 'ふ', 'こ',
|
||||||
|
'え', 'て', 'あ', 'さ', 'き', 'ゆ', 'め', 'み', 'し', 'ゑ', 'ひ',
|
||||||
|
'も', 'せ', 'す',
|
||||||
|
],
|
||||||
|
n,
|
||||||
|
),
|
||||||
|
Self::KatakanaAiueo => zeroless(
|
||||||
|
[
|
||||||
|
'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ',
|
||||||
|
'シ', 'ス', 'セ', 'ソ', 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ',
|
||||||
|
'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'マ', 'ミ', 'ム',
|
||||||
|
'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ',
|
||||||
|
'ヲ', 'ン',
|
||||||
|
],
|
||||||
|
n,
|
||||||
|
),
|
||||||
|
Self::KatakanaIroha => zeroless(
|
||||||
|
[
|
||||||
|
'イ', 'ロ', 'ハ', 'ニ', 'ホ', 'ヘ', 'ト', 'チ', 'リ', 'ヌ', 'ル',
|
||||||
|
'ヲ', 'ワ', 'カ', 'ヨ', 'タ', 'レ', 'ソ', 'ツ', 'ネ', 'ナ', 'ラ',
|
||||||
|
'ム', 'ウ', 'ヰ', 'ノ', 'オ', 'ク', 'ヤ', 'マ', 'ケ', 'フ', 'コ',
|
||||||
|
'エ', 'テ', 'ア', 'サ', 'キ', 'ユ', 'メ', 'ミ', 'シ', 'ヱ', 'ヒ',
|
||||||
|
'モ', 'セ', 'ス',
|
||||||
|
],
|
||||||
|
n,
|
||||||
|
),
|
||||||
|
Self::KoreanJamo => zeroless(
|
||||||
|
[
|
||||||
|
'ㄱ', 'ㄴ', 'ㄷ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅅ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ',
|
||||||
|
'ㅌ', 'ㅍ', 'ㅎ',
|
||||||
|
],
|
||||||
|
n,
|
||||||
|
),
|
||||||
|
Self::KoreanSyllable => zeroless(
|
||||||
|
[
|
||||||
|
'가', '나', '다', '라', '마', '바', '사', '아', '자', '차', '카',
|
||||||
|
'타', '파', '하',
|
||||||
|
],
|
||||||
|
n,
|
||||||
|
),
|
||||||
|
Self::BengaliLetter => zeroless(
|
||||||
|
[
|
||||||
|
'ক', 'খ', 'গ', 'ঘ', 'ঙ', 'চ', 'ছ', 'জ', 'ঝ', 'ঞ', 'ট', 'ঠ', 'ড', 'ঢ',
|
||||||
|
'ণ', 'ত', 'থ', 'দ', 'ধ', 'ন', 'প', 'ফ', 'ব', 'ভ', 'ম', 'য', 'র', 'ল',
|
||||||
|
'শ', 'ষ', 'স', 'হ',
|
||||||
|
],
|
||||||
|
n,
|
||||||
|
),
|
||||||
|
Self::CircledNumber => zeroless(
|
||||||
|
[
|
||||||
|
'①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬', '⑭',
|
||||||
|
'⑮', '⑯', '⑰', '⑱', '⑲', '⑳', '㉑', '㉒', '㉓', '㉔', '㉕', '㉖',
|
||||||
|
'㉗', '㉘', '㉙', '㉚', '㉛', '㉜', '㉝', '㉞', '㉟', '㊱', '㊲',
|
||||||
|
'㊳', '㊴', '㊵', '㊶', '㊷', '㊸', '㊹', '㊺', '㊻', '㊼', '㊽',
|
||||||
|
'㊾', '㊿',
|
||||||
|
],
|
||||||
|
n,
|
||||||
|
),
|
||||||
|
Self::DoubleCircledNumber => {
|
||||||
|
zeroless(['⓵', '⓶', '⓷', '⓸', '⓹', '⓺', '⓻', '⓼', '⓽', '⓾'], n)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::LowerSimplifiedChinese => {
|
||||||
|
usize_to_chinese(ChineseVariant::Simple, ChineseCase::Lower, n).into()
|
||||||
|
}
|
||||||
|
Self::UpperSimplifiedChinese => {
|
||||||
|
usize_to_chinese(ChineseVariant::Simple, ChineseCase::Upper, n).into()
|
||||||
|
}
|
||||||
|
Self::LowerTraditionalChinese => {
|
||||||
|
usize_to_chinese(ChineseVariant::Traditional, ChineseCase::Lower, n)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
Self::UpperTraditionalChinese => {
|
||||||
|
usize_to_chinese(ChineseVariant::Traditional, ChineseCase::Upper, n)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
Self::EasternArabic => decimal('\u{0660}', n),
|
Self::EasternArabic => decimal('\u{0660}', n),
|
||||||
Self::EasternArabicPersian => decimal('\u{06F0}', n),
|
Self::EasternArabicPersian => decimal('\u{06F0}', n),
|
||||||
Self::DevanagariNumber => decimal('\u{0966}', n),
|
Self::DevanagariNumber => decimal('\u{0966}', n),
|
||||||
Self::BengaliNumber => decimal('\u{09E6}', n),
|
Self::BengaliNumber => decimal('\u{09E6}', n),
|
||||||
Self::BengaliLetter => zeroless::<32>(
|
|
||||||
|x| {
|
|
||||||
[
|
|
||||||
'ক', 'খ', 'গ', 'ঘ', 'ঙ', 'চ', 'ছ', 'জ', 'ঝ', 'ঞ', 'ট', 'ঠ', 'ড',
|
|
||||||
'ঢ', 'ণ', 'ত', 'থ', 'দ', 'ধ', 'ন', 'প', 'ফ', 'ব', 'ভ', 'ম', 'য',
|
|
||||||
'র', 'ল', 'শ', 'ষ', 'স', 'হ',
|
|
||||||
][x]
|
|
||||||
},
|
|
||||||
n,
|
|
||||||
),
|
|
||||||
Self::CircledNumber => zeroless::<50>(
|
|
||||||
|x| {
|
|
||||||
[
|
|
||||||
'①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬',
|
|
||||||
'⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳', '㉑', '㉒', '㉓', '㉔', '㉕',
|
|
||||||
'㉖', '㉗', '㉘', '㉙', '㉚', '㉛', '㉜', '㉝', '㉞', '㉟', '㊱',
|
|
||||||
'㊲', '㊳', '㊴', '㊵', '㊶', '㊷', '㊸', '㊹', '㊺', '㊻', '㊼',
|
|
||||||
'㊽', '㊾', '㊿',
|
|
||||||
][x]
|
|
||||||
},
|
|
||||||
n,
|
|
||||||
),
|
|
||||||
Self::DoubleCircledNumber => zeroless::<10>(
|
|
||||||
|x| ['⓵', '⓶', '⓷', '⓸', '⓹', '⓺', '⓻', '⓼', '⓽', '⓾'][x],
|
|
||||||
n,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hebrew_numeral(mut n: usize) -> EcoString {
|
||||||
|
if n == 0 {
|
||||||
|
return '-'.into();
|
||||||
|
}
|
||||||
|
let mut fmt = EcoString::new();
|
||||||
|
'outer: for (name, value) in [
|
||||||
|
('ת', 400),
|
||||||
|
('ש', 300),
|
||||||
|
('ר', 200),
|
||||||
|
('ק', 100),
|
||||||
|
('צ', 90),
|
||||||
|
('פ', 80),
|
||||||
|
('ע', 70),
|
||||||
|
('ס', 60),
|
||||||
|
('נ', 50),
|
||||||
|
('מ', 40),
|
||||||
|
('ל', 30),
|
||||||
|
('כ', 20),
|
||||||
|
('י', 10),
|
||||||
|
('ט', 9),
|
||||||
|
('ח', 8),
|
||||||
|
('ז', 7),
|
||||||
|
('ו', 6),
|
||||||
|
('ה', 5),
|
||||||
|
('ד', 4),
|
||||||
|
('ג', 3),
|
||||||
|
('ב', 2),
|
||||||
|
('א', 1),
|
||||||
|
] {
|
||||||
|
while n >= value {
|
||||||
|
match n {
|
||||||
|
15 => fmt.push_str("ט״ו"),
|
||||||
|
16 => fmt.push_str("ט״ז"),
|
||||||
|
_ => {
|
||||||
|
let append_geresh = n == value && fmt.is_empty();
|
||||||
|
if n == value && !fmt.is_empty() {
|
||||||
|
fmt.push('״');
|
||||||
|
}
|
||||||
|
fmt.push(name);
|
||||||
|
if append_geresh {
|
||||||
|
fmt.push('׳');
|
||||||
|
}
|
||||||
|
|
||||||
|
n -= value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roman_numeral(mut n: usize, case: Case) -> EcoString {
|
||||||
|
if n == 0 {
|
||||||
|
return match case {
|
||||||
|
Case::Lower => 'n'.into(),
|
||||||
|
Case::Upper => 'N'.into(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from Yann Villessuzanne's roman.rs under the
|
||||||
|
// Unlicense, at https://github.com/linfir/roman.rs/
|
||||||
|
let mut fmt = EcoString::new();
|
||||||
|
for &(name, value) in &[
|
||||||
|
("M̅", 1000000),
|
||||||
|
("D̅", 500000),
|
||||||
|
("C̅", 100000),
|
||||||
|
("L̅", 50000),
|
||||||
|
("X̅", 10000),
|
||||||
|
("V̅", 5000),
|
||||||
|
("I̅V̅", 4000),
|
||||||
|
("M", 1000),
|
||||||
|
("CM", 900),
|
||||||
|
("D", 500),
|
||||||
|
("CD", 400),
|
||||||
|
("C", 100),
|
||||||
|
("XC", 90),
|
||||||
|
("L", 50),
|
||||||
|
("XL", 40),
|
||||||
|
("X", 10),
|
||||||
|
("IX", 9),
|
||||||
|
("V", 5),
|
||||||
|
("IV", 4),
|
||||||
|
("I", 1),
|
||||||
|
] {
|
||||||
|
while n >= value {
|
||||||
|
n -= value;
|
||||||
|
for c in name.chars() {
|
||||||
|
match case {
|
||||||
|
Case::Lower => fmt.extend(c.to_lowercase()),
|
||||||
|
Case::Upper => fmt.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt
|
||||||
|
}
|
||||||
|
|
||||||
/// Stringify a number using a base-N counting system with no zero digit.
|
/// Stringify a number using a base-N counting system with no zero digit.
|
||||||
///
|
///
|
||||||
/// This is best explained by example. Suppose our digits are 'A', 'B', and 'C'.
|
/// This is best explained by example. Suppose our digits are 'A', 'B', and 'C'.
|
||||||
@ -627,19 +627,19 @@ impl NumberingKind {
|
|||||||
/// You might be familiar with this scheme from the way spreadsheet software
|
/// You might be familiar with this scheme from the way spreadsheet software
|
||||||
/// tends to label its columns.
|
/// tends to label its columns.
|
||||||
fn zeroless<const N_DIGITS: usize>(
|
fn zeroless<const N_DIGITS: usize>(
|
||||||
mk_digit: impl Fn(usize) -> char,
|
alphabet: [char; N_DIGITS],
|
||||||
mut n: usize,
|
mut n: usize,
|
||||||
) -> EcoString {
|
) -> EcoString {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return '-'.into();
|
return '-'.into();
|
||||||
}
|
}
|
||||||
let mut cs: SmallVec<[char; 8]> = smallvec![];
|
let mut cs = EcoString::new();
|
||||||
while n > 0 {
|
while n > 0 {
|
||||||
n -= 1;
|
n -= 1;
|
||||||
cs.push(mk_digit(n % N_DIGITS));
|
cs.push(alphabet[n % N_DIGITS]);
|
||||||
n /= N_DIGITS;
|
n /= N_DIGITS;
|
||||||
}
|
}
|
||||||
cs.into_iter().rev().collect()
|
cs.chars().rev().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stringify a number using a base-10 counting system with a zero digit.
|
/// Stringify a number using a base-10 counting system with a zero digit.
|
||||||
@ -649,10 +649,10 @@ fn decimal(start: char, mut n: usize) -> EcoString {
|
|||||||
if n == 0 {
|
if n == 0 {
|
||||||
return start.into();
|
return start.into();
|
||||||
}
|
}
|
||||||
let mut cs: SmallVec<[char; 8]> = smallvec![];
|
let mut cs = EcoString::new();
|
||||||
while n > 0 {
|
while n > 0 {
|
||||||
cs.push(char::from_u32((start as u32) + ((n % 10) as u32)).unwrap());
|
cs.push(char::from_u32((start as u32) + ((n % 10) as u32)).unwrap());
|
||||||
n /= 10;
|
n /= 10;
|
||||||
}
|
}
|
||||||
cs.into_iter().rev().collect()
|
cs.chars().rev().collect()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user