mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Fix math styling
This commit is contained in:
parent
8fbfa594e0
commit
60dfe8f893
@ -1,4 +1,5 @@
|
||||
use ttf_parser::math::MathValue;
|
||||
use typst::font::{FontStyle, FontWeight};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::*;
|
||||
@ -24,21 +25,18 @@ macro_rules! percent {
|
||||
/// The context for math layout.
|
||||
pub(super) struct MathContext<'a, 'b, 'v> {
|
||||
pub vt: &'v mut Vt<'b>,
|
||||
pub outer: StyleChain<'a>,
|
||||
pub map: StyleMap,
|
||||
pub regions: Regions<'a>,
|
||||
pub font: &'a Font,
|
||||
pub ttf: &'a ttf_parser::Face<'a>,
|
||||
pub table: ttf_parser::math::Table<'a>,
|
||||
pub constants: ttf_parser::math::Constants<'a>,
|
||||
pub space_width: Em,
|
||||
pub fill: Paint,
|
||||
pub lang: Lang,
|
||||
pub row: MathRow,
|
||||
pub map: StyleMap,
|
||||
pub style: MathStyle,
|
||||
base_size: Abs,
|
||||
scaled_size: Abs,
|
||||
style_stack: Vec<MathStyle>,
|
||||
pub size: Abs,
|
||||
outer: StyleChain<'a>,
|
||||
style_stack: Vec<(MathStyle, Abs)>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||
@ -52,7 +50,6 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||
let table = font.ttf().tables().math.unwrap();
|
||||
let constants = table.constants.unwrap();
|
||||
let size = styles.get(TextNode::SIZE);
|
||||
|
||||
let ttf = font.ttf();
|
||||
let space_width = ttf
|
||||
.glyph_index(' ')
|
||||
@ -60,38 +57,38 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||
.map(|advance| font.to_em(advance))
|
||||
.unwrap_or(THICK);
|
||||
|
||||
let variant = variant(styles);
|
||||
Self {
|
||||
vt,
|
||||
outer: styles,
|
||||
map: StyleMap::new(),
|
||||
regions: {
|
||||
let size = Size::new(regions.first.x, regions.base.y);
|
||||
Regions::one(size, regions.base, Axes::splat(false))
|
||||
},
|
||||
style: MathStyle {
|
||||
variant: MathVariant::Serif,
|
||||
size: if block { MathSize::Display } else { MathSize::Text },
|
||||
cramped: false,
|
||||
bold: false,
|
||||
italic: true,
|
||||
},
|
||||
fill: styles.get(TextNode::FILL),
|
||||
lang: styles.get(TextNode::LANG),
|
||||
font: &font,
|
||||
ttf: font.ttf(),
|
||||
table,
|
||||
constants,
|
||||
space_width,
|
||||
row: MathRow::new(),
|
||||
base_size: size,
|
||||
scaled_size: size,
|
||||
map: StyleMap::new(),
|
||||
style: MathStyle {
|
||||
variant: MathVariant::Serif,
|
||||
size: if block { MathSize::Display } else { MathSize::Text },
|
||||
cramped: false,
|
||||
bold: variant.weight >= FontWeight::BOLD,
|
||||
italic: match variant.style {
|
||||
FontStyle::Normal => Smart::Auto,
|
||||
FontStyle::Italic | FontStyle::Oblique => Smart::Custom(true),
|
||||
},
|
||||
},
|
||||
size,
|
||||
outer: styles,
|
||||
style_stack: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, fragment: impl Into<MathFragment>) {
|
||||
self.row
|
||||
.push(self.scaled_size, self.space_width, self.style, fragment);
|
||||
self.row.push(self.size, self.space_width, self.style, fragment);
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, row: MathRow) {
|
||||
@ -130,11 +127,12 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||
Ok(self.layout_fragment(node)?.to_frame(self))
|
||||
}
|
||||
|
||||
pub fn layout_text(&mut self, text: &EcoString) -> SourceResult<()> {
|
||||
pub fn layout_text(&mut self, text: &str) -> SourceResult<()> {
|
||||
let mut chars = text.chars();
|
||||
if let Some(glyph) = chars
|
||||
.next()
|
||||
.filter(|_| chars.next().is_none())
|
||||
.map(|c| self.style.styled_char(c))
|
||||
.and_then(|c| GlyphFragment::try_new(self, c))
|
||||
{
|
||||
// A single letter that is available in the math font.
|
||||
@ -147,10 +145,10 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||
self.push(glyph);
|
||||
}
|
||||
} else if text.chars().all(|c| c.is_ascii_digit()) {
|
||||
// A number that should respect math styling and can therefore
|
||||
// not fall back to the normal text layout.
|
||||
// Numbers aren't that difficult.
|
||||
let mut vec = vec![];
|
||||
for c in text.chars() {
|
||||
let c = self.style.styled_char(c);
|
||||
vec.push(GlyphFragment::new(self, c).into());
|
||||
}
|
||||
let frame = MathRow(vec).to_frame(self);
|
||||
@ -158,7 +156,12 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||
} else {
|
||||
// Anything else is handled by Typst's standard text layout.
|
||||
let spaced = text.graphemes(true).count() > 1;
|
||||
let frame = self.layout_non_math(&TextNode::packed(text.clone()))?;
|
||||
let mut style = self.style;
|
||||
if self.style.italic == Smart::Auto {
|
||||
style = style.with_italic(false);
|
||||
}
|
||||
let text: EcoString = text.chars().map(|c| style.styled_char(c)).collect();
|
||||
let frame = self.layout_non_math(&TextNode::packed(text))?;
|
||||
self.push(
|
||||
FrameFragment::new(frame)
|
||||
.with_class(MathClass::Alphabetic)
|
||||
@ -169,33 +172,35 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Abs {
|
||||
self.scaled_size
|
||||
pub fn styles(&self) -> StyleChain {
|
||||
self.outer.chain(&self.map)
|
||||
}
|
||||
|
||||
pub fn style(&mut self, style: MathStyle) {
|
||||
self.style_stack.push(self.style);
|
||||
self.style_stack.push((self.style, self.size));
|
||||
let base_size = self.styles().get(TextNode::SIZE) / self.style.size.factor(self);
|
||||
self.size = base_size * style.size.factor(self);
|
||||
self.map.set(TextNode::SIZE, TextSize(self.size.into()));
|
||||
self.map.set(
|
||||
TextNode::STYLE,
|
||||
if style.italic == Smart::Custom(true) {
|
||||
FontStyle::Italic
|
||||
} else {
|
||||
FontStyle::Normal
|
||||
},
|
||||
);
|
||||
self.map.set(
|
||||
TextNode::WEIGHT,
|
||||
if style.bold { FontWeight::BOLD } else { FontWeight::REGULAR },
|
||||
);
|
||||
self.style = style;
|
||||
self.rescale();
|
||||
self.map.set(TextNode::SIZE, TextSize(self.scaled_size.into()));
|
||||
}
|
||||
|
||||
pub fn unstyle(&mut self) {
|
||||
self.style = self.style_stack.pop().unwrap();
|
||||
self.rescale();
|
||||
(self.style, self.size) = self.style_stack.pop().unwrap();
|
||||
self.map.unset();
|
||||
self.map.unset();
|
||||
self.map.unset();
|
||||
}
|
||||
|
||||
fn rescale(&mut self) {
|
||||
self.scaled_size = match self.style.size {
|
||||
MathSize::Display | MathSize::Text => self.base_size,
|
||||
MathSize::Script => {
|
||||
self.base_size * percent!(self, script_percent_scale_down)
|
||||
}
|
||||
MathSize::ScriptScript => {
|
||||
self.base_size * percent!(self, script_script_percent_scale_down)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +222,7 @@ impl Scaled for u16 {
|
||||
|
||||
impl Scaled for Em {
|
||||
fn scaled(self, ctx: &MathContext) -> Abs {
|
||||
self.at(ctx.size())
|
||||
self.at(ctx.size)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,8 +157,10 @@ fn layout(
|
||||
frame.push(
|
||||
line_pos,
|
||||
Element::Shape(
|
||||
Geometry::Line(Point::with_x(line_width))
|
||||
.stroked(Stroke { paint: ctx.fill, thickness }),
|
||||
Geometry::Line(Point::with_x(line_width)).stroked(Stroke {
|
||||
paint: ctx.styles().get(TextNode::FILL),
|
||||
thickness,
|
||||
}),
|
||||
),
|
||||
);
|
||||
ctx.push(frame);
|
||||
|
@ -121,6 +121,8 @@ impl From<Frame> for MathFragment {
|
||||
pub(super) struct GlyphFragment {
|
||||
pub id: GlyphId,
|
||||
pub c: char,
|
||||
pub lang: Lang,
|
||||
pub fill: Paint,
|
||||
pub font_size: Abs,
|
||||
pub width: Abs,
|
||||
pub ascent: Abs,
|
||||
@ -131,7 +133,6 @@ pub(super) struct GlyphFragment {
|
||||
|
||||
impl GlyphFragment {
|
||||
pub fn new(ctx: &MathContext, c: char) -> Self {
|
||||
let c = ctx.style.styled_char(c);
|
||||
let id = ctx.ttf.glyph_index(c).unwrap_or_default();
|
||||
Self::with_id(ctx, c, id)
|
||||
}
|
||||
@ -154,7 +155,9 @@ impl GlyphFragment {
|
||||
Self {
|
||||
id,
|
||||
c,
|
||||
font_size: ctx.size(),
|
||||
lang: ctx.styles().get(TextNode::LANG),
|
||||
fill: ctx.styles().get(TextNode::FILL),
|
||||
font_size: ctx.size,
|
||||
width: advance.scaled(ctx),
|
||||
ascent: bbox.y_max.scaled(ctx),
|
||||
descent: -bbox.y_min.scaled(ctx),
|
||||
@ -184,12 +187,12 @@ impl GlyphFragment {
|
||||
let text = Text {
|
||||
font: ctx.font.clone(),
|
||||
size: self.font_size,
|
||||
fill: ctx.fill,
|
||||
lang: ctx.lang,
|
||||
fill: self.fill,
|
||||
lang: self.lang,
|
||||
glyphs: vec![Glyph {
|
||||
id: self.id.0,
|
||||
c: self.c,
|
||||
x_advance: Em::from_length(self.width, ctx.size()),
|
||||
x_advance: Em::from_length(self.width, ctx.size),
|
||||
x_offset: Em::zero(),
|
||||
}],
|
||||
};
|
||||
|
@ -66,7 +66,7 @@ impl LayoutMath for LrNode {
|
||||
let height = self
|
||||
.size
|
||||
.unwrap_or(Rel::one())
|
||||
.resolve(ctx.outer.chain(&ctx.map))
|
||||
.resolve(ctx.styles())
|
||||
.relative_to(2.0 * max_extent);
|
||||
|
||||
match row.0.as_mut_slice() {
|
||||
|
@ -39,20 +39,11 @@ impl VecNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.all()?).pack())
|
||||
}
|
||||
|
||||
fn field(&self, name: &str) -> Option<Value> {
|
||||
match name {
|
||||
"elements" => {
|
||||
Some(Value::Array(self.0.iter().cloned().map(Value::Content).collect()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutMath for VecNode {
|
||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||
let delim = ctx.outer.get(Self::DELIM);
|
||||
let delim = ctx.styles().get(Self::DELIM);
|
||||
layout(ctx, &self.0, Align::Center, Some(delim.open()), Some(delim.close()))
|
||||
}
|
||||
}
|
||||
@ -86,15 +77,6 @@ impl CasesNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.all()?).pack())
|
||||
}
|
||||
|
||||
fn field(&self, name: &str) -> Option<Value> {
|
||||
match name {
|
||||
"branches" => {
|
||||
Some(Value::Array(self.0.iter().cloned().map(Value::Content).collect()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutMath for CasesNode {
|
||||
|
@ -29,29 +29,27 @@ pub use self::root::*;
|
||||
pub use self::stack::*;
|
||||
pub use self::style::*;
|
||||
|
||||
use ttf_parser::GlyphId;
|
||||
use ttf_parser::Rect;
|
||||
use ttf_parser::{GlyphId, Rect};
|
||||
use typst::font::Font;
|
||||
use typst::model::{Guard, Module, Scope, SequenceNode};
|
||||
use typst::model::{Guard, Module, Scope, SequenceNode, StyledNode};
|
||||
use unicode_math_class::MathClass;
|
||||
|
||||
use self::ctx::*;
|
||||
use self::fragment::*;
|
||||
use self::row::*;
|
||||
use self::spacing::*;
|
||||
use crate::layout::HNode;
|
||||
use crate::layout::ParNode;
|
||||
use crate::layout::Spacing;
|
||||
use crate::layout::{HNode, ParNode, Spacing};
|
||||
use crate::prelude::*;
|
||||
use crate::text::LinebreakNode;
|
||||
use crate::text::TextNode;
|
||||
use crate::text::TextSize;
|
||||
use crate::text::{families, variant, FallbackList, FontFamily, SpaceNode};
|
||||
use crate::text::{
|
||||
families, variant, FallbackList, FontFamily, LinebreakNode, SpaceNode, TextNode,
|
||||
TextSize,
|
||||
};
|
||||
|
||||
/// Create a module with all math definitions.
|
||||
pub fn module(sym: &Module) -> Module {
|
||||
let mut math = Scope::deduplicating();
|
||||
math.def_func::<FormulaNode>("formula");
|
||||
math.def_func::<TextNode>("text");
|
||||
|
||||
// Grouping.
|
||||
math.def_func::<LrNode>("lr");
|
||||
@ -83,6 +81,7 @@ pub fn module(sym: &Module) -> Module {
|
||||
math.def_func::<RootNode>("root");
|
||||
|
||||
// Styles.
|
||||
math.def_func::<UprightNode>("upright");
|
||||
math.def_func::<BoldNode>("bold");
|
||||
math.def_func::<ItalicNode>("italic");
|
||||
math.def_func::<SerifNode>("serif");
|
||||
@ -243,6 +242,24 @@ impl LayoutMath for FormulaNode {
|
||||
|
||||
impl LayoutMath for Content {
|
||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||
if let Some(node) = self.to::<SequenceNode>() {
|
||||
for child in &node.0 {
|
||||
child.layout_math(ctx)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(styled) = self.to::<StyledNode>() {
|
||||
let prev_map = std::mem::replace(&mut ctx.map, styled.map.clone());
|
||||
let prev_size = ctx.size;
|
||||
ctx.map.apply(prev_map.clone());
|
||||
ctx.size = ctx.styles().get(TextNode::SIZE);
|
||||
styled.sub.layout_math(ctx)?;
|
||||
ctx.size = prev_size;
|
||||
ctx.map = prev_map;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.is::<SpaceNode>() {
|
||||
ctx.push(MathFragment::Space);
|
||||
return Ok(());
|
||||
@ -256,9 +273,7 @@ impl LayoutMath for Content {
|
||||
if let Some(node) = self.to::<HNode>() {
|
||||
if let Spacing::Relative(rel) = node.amount {
|
||||
if rel.rel.is_zero() {
|
||||
ctx.push(MathFragment::Spacing(
|
||||
rel.abs.resolve(ctx.outer.chain(&ctx.map)),
|
||||
));
|
||||
ctx.push(MathFragment::Spacing(rel.abs.resolve(ctx.styles())));
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
@ -269,13 +284,6 @@ impl LayoutMath for Content {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(node) = self.to::<SequenceNode>() {
|
||||
for child in &node.0 {
|
||||
child.layout_math(ctx)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(node) = self.with::<dyn LayoutMath>() {
|
||||
return node.layout_math(ctx);
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ fn layout(
|
||||
line_pos,
|
||||
Element::Shape(
|
||||
Geometry::Line(Point::with_x(line_length))
|
||||
.stroked(Stroke { paint: ctx.fill, thickness }),
|
||||
.stroked(Stroke { paint: ctx.styles().get(TextNode::FILL), thickness }),
|
||||
),
|
||||
);
|
||||
frame.push_frame(radicand_pos, radicand);
|
||||
|
@ -77,7 +77,7 @@ impl MathRow {
|
||||
let mut frame = Frame::new(Size::zero());
|
||||
let fragments = std::mem::take(&mut self.0);
|
||||
|
||||
let leading = ctx.outer.chain(&ctx.map).get(ParNode::LEADING);
|
||||
let leading = ctx.styles().get(ParNode::LEADING);
|
||||
let rows: Vec<_> = fragments
|
||||
.split(|frag| matches!(frag, MathFragment::Linebreak))
|
||||
.map(|slice| Self(slice.to_vec()))
|
||||
|
@ -1,5 +1,40 @@
|
||||
use super::*;
|
||||
|
||||
/// # Upright
|
||||
/// Upright (non-italic) font style in math.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// $ upright(A) != A $
|
||||
/// ```
|
||||
///
|
||||
/// ## Parameters
|
||||
/// - body: Content (positional, required)
|
||||
/// The piece of formula to style.
|
||||
///
|
||||
/// ## Category
|
||||
/// math
|
||||
#[func]
|
||||
#[capable(LayoutMath)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct UprightNode(pub Content);
|
||||
|
||||
#[node]
|
||||
impl UprightNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutMath for UprightNode {
|
||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||
ctx.style(ctx.style.with_italic(false));
|
||||
self.0.layout_math(ctx)?;
|
||||
ctx.unstyle();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// # Bold
|
||||
/// Bold font style in math.
|
||||
///
|
||||
@ -28,7 +63,7 @@ impl BoldNode {
|
||||
|
||||
impl LayoutMath for BoldNode {
|
||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||
ctx.style(ctx.style.with_italic(false).with_bold_toggled());
|
||||
ctx.style(ctx.style.with_bold(true));
|
||||
self.0.layout_math(ctx)?;
|
||||
ctx.unstyle();
|
||||
Ok(())
|
||||
@ -60,7 +95,7 @@ impl ItalicNode {
|
||||
|
||||
impl LayoutMath for ItalicNode {
|
||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||
ctx.style(ctx.style.with_italic_toggled());
|
||||
ctx.style(ctx.style.with_italic(true));
|
||||
self.0.layout_math(ctx)?;
|
||||
ctx.unstyle();
|
||||
Ok(())
|
||||
@ -290,22 +325,7 @@ pub struct MathStyle {
|
||||
/// Whether to use bold glyphs.
|
||||
pub bold: bool,
|
||||
/// Whether to use italic glyphs.
|
||||
pub italic: bool,
|
||||
}
|
||||
|
||||
/// The size of elements in a formula.
|
||||
///
|
||||
/// See the TeXbook p. 141.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum MathSize {
|
||||
/// Second-level sub- and superscripts.
|
||||
ScriptScript,
|
||||
/// Sub- and superscripts.
|
||||
Script,
|
||||
/// Math in text.
|
||||
Text,
|
||||
/// Math on its own line.
|
||||
Display,
|
||||
pub italic: Smart<bool>,
|
||||
}
|
||||
|
||||
impl MathStyle {
|
||||
@ -331,17 +351,7 @@ impl MathStyle {
|
||||
|
||||
/// This style, with `italic` set to the given value.
|
||||
pub fn with_italic(self, italic: bool) -> Self {
|
||||
Self { italic, ..self }
|
||||
}
|
||||
|
||||
/// This style, with boldness inverted.
|
||||
pub fn with_bold_toggled(self) -> Self {
|
||||
self.with_bold(!self.bold)
|
||||
}
|
||||
|
||||
/// This style, with italicness inverted.
|
||||
pub fn with_italic_toggled(self) -> Self {
|
||||
self.with_italic(!self.italic)
|
||||
Self { italic: Smart::Custom(italic), ..self }
|
||||
}
|
||||
|
||||
/// The style for subscripts in the current style.
|
||||
@ -377,6 +387,31 @@ impl MathStyle {
|
||||
}
|
||||
}
|
||||
|
||||
/// The size of elements in a formula.
|
||||
///
|
||||
/// See the TeXbook p. 141.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum MathSize {
|
||||
/// Second-level sub- and superscripts.
|
||||
ScriptScript,
|
||||
/// Sub- and superscripts.
|
||||
Script,
|
||||
/// Math in text.
|
||||
Text,
|
||||
/// Math on its own line.
|
||||
Display,
|
||||
}
|
||||
|
||||
impl MathSize {
|
||||
pub(super) fn factor(self, ctx: &MathContext) -> f64 {
|
||||
match self {
|
||||
Self::Display | Self::Text => 1.0,
|
||||
Self::Script => percent!(ctx, script_percent_scale_down),
|
||||
Self::ScriptScript => percent!(ctx, script_script_percent_scale_down),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A mathematical style variant, as defined by Unicode.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum MathVariant {
|
||||
@ -401,17 +436,17 @@ impl Default for MathVariant {
|
||||
pub(super) fn styled_char(style: MathStyle, c: char) -> char {
|
||||
use MathVariant::*;
|
||||
|
||||
let tuple = (style.variant, style.bold, style.italic);
|
||||
let base = match c {
|
||||
'a'..='z' => 'a',
|
||||
'A'..='Z' => 'A',
|
||||
'α'..='ω' => 'α',
|
||||
'Α'..='Ω' => 'Α',
|
||||
'0'..='9' => '0',
|
||||
let (base, default_italic) = match c {
|
||||
'a'..='z' => ('a', true),
|
||||
'A'..='Z' => ('A', true),
|
||||
'α'..='ω' => ('α', false),
|
||||
'Α'..='Ω' => ('Α', false),
|
||||
'0'..='9' => ('0', false),
|
||||
'-' => return '−',
|
||||
_ => return c,
|
||||
};
|
||||
|
||||
let tuple = (style.variant, style.bold, style.italic.unwrap_or(default_italic));
|
||||
let start = match c {
|
||||
// Latin upper.
|
||||
'A'..='Z' => match tuple {
|
||||
|
Loading…
x
Reference in New Issue
Block a user