1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
use typst_library::diag::SourceResult;
use typst_library::foundations::{Content, Packed, Resolve, StyleChain, SymbolElem};
use typst_library::layout::{Em, Frame, FrameItem, Point, Size};
use typst_library::math::{BinomElem, FracElem};
use typst_library::text::TextElem;
use typst_library::visualize::{FixedStroke, Geometry};
use typst_syntax::Span;
use super::{
style_for_denominator, style_for_numerator, FrameFragment, GlyphFragment,
MathContext, DELIM_SHORT_FALL,
};
const FRAC_AROUND: Em = Em::new(0.1);
/// Lays out a [`FracElem`].
#[typst_macros::time(name = "math.frac", span = elem.span())]
pub fn layout_frac(
elem: &Packed<FracElem>,
ctx: &mut MathContext,
styles: StyleChain,
) -> SourceResult<()> {
layout_frac_like(
ctx,
styles,
&elem.num,
std::slice::from_ref(&elem.denom),
false,
elem.span(),
)
}
/// Lays out a [`BinomElem`].
#[typst_macros::time(name = "math.binom", span = elem.span())]
pub fn layout_binom(
elem: &Packed<BinomElem>,
ctx: &mut MathContext,
styles: StyleChain,
) -> SourceResult<()> {
layout_frac_like(ctx, styles, &elem.upper, &elem.lower, true, elem.span())
}
/// Layout a fraction or binomial.
fn layout_frac_like(
ctx: &mut MathContext,
styles: StyleChain,
num: &Content,
denom: &[Content],
binom: bool,
span: Span,
) -> SourceResult<()> {
let short_fall = DELIM_SHORT_FALL.resolve(styles);
let axis = scaled!(ctx, styles, axis_height);
let thickness = scaled!(ctx, styles, fraction_rule_thickness);
let shift_up = scaled!(
ctx, styles,
text: fraction_numerator_shift_up,
display: fraction_numerator_display_style_shift_up,
);
let shift_down = scaled!(
ctx, styles,
text: fraction_denominator_shift_down,
display: fraction_denominator_display_style_shift_down,
);
let num_min = scaled!(
ctx, styles,
text: fraction_numerator_gap_min,
display: fraction_num_display_style_gap_min,
);
let denom_min = scaled!(
ctx, styles,
text: fraction_denominator_gap_min,
display: fraction_denom_display_style_gap_min,
);
let num_style = style_for_numerator(styles);
let num = ctx.layout_into_frame(num, styles.chain(&num_style))?;
let denom_style = style_for_denominator(styles);
let denom = ctx.layout_into_frame(
&Content::sequence(
// Add a comma between each element.
denom
.iter()
.flat_map(|a| [SymbolElem::packed(','), a.clone()])
.skip(1),
),
styles.chain(&denom_style),
)?;
let around = FRAC_AROUND.resolve(styles);
let num_gap = (shift_up - (axis + thickness / 2.0) - num.descent()).max(num_min);
let denom_gap =
(shift_down + (axis - thickness / 2.0) - denom.ascent()).max(denom_min);
let line_width = num.width().max(denom.width());
let width = line_width + 2.0 * around;
let height = num.height() + num_gap + thickness + denom_gap + denom.height();
let size = Size::new(width, height);
let num_pos = Point::with_x((width - num.width()) / 2.0);
let line_pos =
Point::new((width - line_width) / 2.0, num.height() + num_gap + thickness / 2.0);
let denom_pos = Point::new((width - denom.width()) / 2.0, height - denom.height());
let baseline = line_pos.y + axis;
let mut frame = Frame::soft(size);
frame.set_baseline(baseline);
frame.push_frame(num_pos, num);
frame.push_frame(denom_pos, denom);
if binom {
let mut left = GlyphFragment::new_char(ctx.font, styles, '(', span)?;
left.stretch_vertical(ctx, height - short_fall);
left.center_on_axis();
ctx.push(left);
ctx.push(FrameFragment::new(styles, frame));
let mut right = GlyphFragment::new_char(ctx.font, styles, ')', span)?;
right.stretch_vertical(ctx, height - short_fall);
right.center_on_axis();
ctx.push(right);
} else {
frame.push(
line_pos,
FrameItem::Shape(
Geometry::Line(Point::with_x(line_width)).stroked(
FixedStroke::from_pair(
styles.get_ref(TextElem::fill).as_decoration(),
thickness,
),
),
span,
),
);
ctx.push(FrameFragment::new(styles, frame));
}
Ok(())
}
|