summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/math/align.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-library/src/math/align.rs')
-rw-r--r--crates/typst-library/src/math/align.rs63
1 files changed, 63 insertions, 0 deletions
diff --git a/crates/typst-library/src/math/align.rs b/crates/typst-library/src/math/align.rs
new file mode 100644
index 00000000..aee89a89
--- /dev/null
+++ b/crates/typst-library/src/math/align.rs
@@ -0,0 +1,63 @@
+use super::*;
+
+/// A math alignment point: `&`, `&&`.
+///
+/// Display: Alignment Point
+/// Category: math
+#[element(LayoutMath)]
+pub struct AlignPointElem {}
+
+impl LayoutMath for AlignPointElem {
+ #[tracing::instrument(skip(ctx))]
+ fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
+ ctx.push(MathFragment::Align);
+ Ok(())
+ }
+}
+
+pub(super) struct AlignmentResult {
+ pub points: Vec<Abs>,
+ pub width: Abs,
+}
+
+/// Determine the position of the alignment points.
+pub(super) fn alignments(rows: &[MathRow]) -> AlignmentResult {
+ let mut widths = Vec::<Abs>::new();
+
+ let mut pending_width = Abs::zero();
+ for row in rows {
+ let mut width = Abs::zero();
+ let mut alignment_index = 0;
+
+ for fragment in row.iter() {
+ if matches!(fragment, MathFragment::Align) {
+ if alignment_index < widths.len() {
+ widths[alignment_index].set_max(width);
+ } else {
+ widths.push(width.max(pending_width));
+ }
+ width = Abs::zero();
+ alignment_index += 1;
+ } else {
+ width += fragment.width();
+ }
+ }
+ if widths.is_empty() {
+ pending_width.set_max(width);
+ } else if alignment_index < widths.len() {
+ widths[alignment_index].set_max(width);
+ } else {
+ widths.push(width.max(pending_width));
+ }
+ }
+
+ let mut points = widths;
+ for i in 1..points.len() {
+ let prev = points[i - 1];
+ points[i] += prev;
+ }
+ AlignmentResult {
+ width: points.last().copied().unwrap_or(pending_width),
+ points,
+ }
+}