summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-10-17 19:21:47 +0200
committerLaurenz <laurmaedje@gmail.com>2019-10-17 19:21:47 +0200
commit991e879e1d2ed53125dbff4edba80804ff28f2a9 (patch)
tree0917f83108feca10ca4207dd9089fe57cf8098d5
parent1987e5861cf2c033e3a540a5ef7c0f7106016929 (diff)
Extend stack layouts from vertical to horizontal flows ➡
-rw-r--r--fonts/Latin-Modern-Math.otfbin0 -> 733736 bytes
-rw-r--r--fonts/fonts.toml1
-rw-r--r--src/export/pdf.rs6
-rw-r--r--src/layout/flex.rs2
-rw-r--r--src/layout/mod.rs9
-rw-r--r--src/layout/stacked.rs56
-rw-r--r--src/layout/tree.rs1
-rw-r--r--src/lib.rs3
-rw-r--r--src/library/boxed.rs34
-rw-r--r--tests/layouts/boxes.typ10
-rw-r--r--tests/layouts/coma.typ31
11 files changed, 116 insertions, 37 deletions
diff --git a/fonts/Latin-Modern-Math.otf b/fonts/Latin-Modern-Math.otf
new file mode 100644
index 00000000..0e4642e9
--- /dev/null
+++ b/fonts/Latin-Modern-Math.otf
Binary files differ
diff --git a/fonts/fonts.toml b/fonts/fonts.toml
index 844e2d34..b289ff5c 100644
--- a/fonts/fonts.toml
+++ b/fonts/fonts.toml
@@ -10,4 +10,5 @@
"CMU-Typewriter-Italic.ttf" = ["Computer Modern", "Italic", "Serif", "SansSerif", "Monospace"]
"CMU-Typewriter-Bold.ttf" = ["Computer Modern", "Bold", "Serif", "SansSerif", "Monospace"]
"CMU-Typewriter-Bold-Italic.ttf" = ["Computer Modern", "Bold", "Italic", "Serif", "SansSerif", "Monospace"]
+"Latin-Modern-Math.otf" = ["Latin Modern", "Latin Modern Math", "Math", "Regular", "Serif"]
"NotoEmoji-Regular.ttf" = ["Noto", "Noto Emoji", "Regular", "SansSerif", "Serif", "Monospace"]
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index f029a37f..bb688118 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -146,8 +146,10 @@ impl<'d, W: Write> ExportProcess<'d, W> {
for index in 0 .. num_fonts {
let old_index = new_to_old[&index];
let font = font_loader.get_with_index(old_index);
- let subsetted = font.subsetted(font_chars[&old_index].iter().cloned(), &SUBSET_TABLES)?;
- fonts.push(OwnedFont::from_bytes(subsetted)?);
+ let subsetted = font.subsetted(font_chars[&old_index].iter().cloned(), &SUBSET_TABLES)
+ .map(|bytes| OwnedFont::from_bytes(bytes))
+ .unwrap_or_else(|_| font.to_owned())?;
+ fonts.push(subsetted);
}
Ok((fonts, old_to_new))
diff --git a/src/layout/flex.rs b/src/layout/flex.rs
index a6f2e091..b97b4239 100644
--- a/src/layout/flex.rs
+++ b/src/layout/flex.rs
@@ -90,7 +90,7 @@ impl FlexLayouter {
ctx,
units: vec![],
- stack: StackLayouter::new(StackContext::from_flex_ctx(ctx)),
+ stack: StackLayouter::new(StackContext::from_flex_ctx(ctx, Flow::Vertical)),
usable_width: ctx.space.usable().x,
run: FlexRun {
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index fccbe8c8..470ac3ba 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -134,6 +134,8 @@ pub struct LayoutContext<'a, 'p> {
pub style: &'a TextStyle,
/// The alignment to use for the content.
pub alignment: Alignment,
+ /// How to stack the context.
+ pub flow: Flow,
/// The primary space to layout in.
pub space: LayoutSpace,
/// The additional spaces which are used when the primary space
@@ -176,6 +178,13 @@ pub enum Alignment {
Center,
}
+/// The flow of content.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Flow {
+ Vertical,
+ Horizontal,
+}
+
/// The error type for layouting.
pub enum LayoutError {
/// There is not enough space to add an item.
diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs
index d87e5394..0097ce3e 100644
--- a/src/layout/stacked.rs
+++ b/src/layout/stacked.rs
@@ -26,15 +26,17 @@ pub struct StackContext {
pub space: LayoutSpace,
pub followup_spaces: Option<LayoutSpace>,
pub shrink_to_fit: bool,
+ pub flow: Flow,
}
macro_rules! reuse {
- ($ctx:expr) => {
+ ($ctx:expr, $flow:expr) => {
StackContext {
alignment: $ctx.alignment,
space: $ctx.space,
followup_spaces: $ctx.followup_spaces,
- shrink_to_fit: $ctx.shrink_to_fit
+ shrink_to_fit: $ctx.shrink_to_fit,
+ flow: $flow
}
};
}
@@ -42,12 +44,12 @@ macro_rules! reuse {
impl StackContext {
/// Create a stack context from a generic layout context.
pub fn from_layout_ctx(ctx: LayoutContext) -> StackContext {
- reuse!(ctx)
+ reuse!(ctx, ctx.flow)
}
/// Create a stack context from a flex context.
- pub fn from_flex_ctx(ctx: FlexContext) -> StackContext {
- reuse!(ctx)
+ pub fn from_flex_ctx(ctx: FlexContext, flow: Flow) -> StackContext {
+ reuse!(ctx, flow)
}
}
@@ -79,9 +81,15 @@ impl StackLayouter {
self.start_new_space()?;
}
- let new_dimensions = Size2D {
- x: crate::size::max(self.dimensions.x, layout.dimensions.x),
- y: self.dimensions.y + layout.dimensions.y,
+ let new_dimensions = match self.ctx.flow {
+ Flow::Vertical => Size2D {
+ x: crate::size::max(self.dimensions.x, layout.dimensions.x),
+ y: self.dimensions.y + layout.dimensions.y,
+ },
+ Flow::Horizontal => Size2D {
+ x: self.dimensions.x + layout.dimensions.x,
+ y: crate::size::max(self.dimensions.y, layout.dimensions.y),
+ }
};
if self.overflows(new_dimensions) {
@@ -104,9 +112,13 @@ impl StackLayouter {
Alignment::Center => self.cursor - Size2D::with_x(layout.dimensions.x / 2),
};
- self.cursor.y += layout.dimensions.y;
self.dimensions = new_dimensions;
+ match self.ctx.flow {
+ Flow::Vertical => self.cursor.y += layout.dimensions.y,
+ Flow::Horizontal => self.cursor.x += layout.dimensions.x,
+ }
+
self.actions.add_layout(position, layout);
Ok(())
@@ -120,23 +132,26 @@ impl StackLayouter {
Ok(())
}
- /// Add vertical space after the last layout.
+ /// Add space after the last layout.
pub fn add_space(&mut self, space: Size) -> LayoutResult<()> {
if !self.started {
self.start_new_space()?;
}
- let new_dimensions = self.dimensions + Size2D::with_y(space);
+ let new_space = match self.ctx.flow {
+ Flow::Vertical => Size2D::with_y(space),
+ Flow::Horizontal => Size2D::with_x(space),
+ };
- if self.overflows(new_dimensions) {
+ if self.overflows(self.dimensions + new_space) {
if self.ctx.followup_spaces.is_some() {
self.finish_layout(false)?;
} else {
return Err(LayoutError::NotEnoughSpace("cannot fit space into stack"));
}
} else {
- self.cursor.y += space;
- self.dimensions.y += space;
+ self.cursor += new_space;
+ self.dimensions += new_space;
}
Ok(())
@@ -195,10 +210,17 @@ impl StackLayouter {
/// The remaining space for new layouts.
pub fn remaining(&self) -> Size2D {
- Size2D {
- x: self.usable.x,
- y: self.usable.y - self.dimensions.y,
+ match self.ctx.flow {
+ Flow::Vertical => Size2D {
+ x: self.usable.x,
+ y: self.usable.y - self.dimensions.y,
+ },
+ Flow::Horizontal => Size2D {
+ x: self.usable.x - self.dimensions.x,
+ y: self.usable.y,
+ },
}
+
}
/// Whether the active space of this layouter contains no content.
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 2c904369..3a58115f 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -96,6 +96,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
let mut ctx = self.ctx;
ctx.style = &self.style;
+ ctx.flow = Flow::Vertical;
ctx.shrink_to_fit = true;
ctx.space.dimensions = remaining;
ctx.space.padding = SizeBox::zero();
diff --git a/src/lib.rs b/src/lib.rs
index c01e5d7e..0b559be9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,7 +21,7 @@ use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
use crate::func::Scope;
use crate::layout::{layout_tree, LayoutContext, MultiLayout};
-use crate::layout::{Alignment, LayoutError, LayoutResult, LayoutSpace};
+use crate::layout::{Alignment, Flow, LayoutError, LayoutResult, LayoutSpace};
use crate::parsing::{parse, ParseContext, ParseError, ParseResult};
use crate::style::{PageStyle, TextStyle};
use crate::syntax::SyntaxTree;
@@ -105,6 +105,7 @@ impl<'p> Typesetter<'p> {
loader: &self.loader,
style: &self.text_style,
alignment: Alignment::Left,
+ flow: Flow::Vertical,
space,
followup_spaces: Some(space),
shrink_to_fit: false,
diff --git a/src/library/boxed.rs b/src/library/boxed.rs
index 975888f4..71a184a6 100644
--- a/src/library/boxed.rs
+++ b/src/library/boxed.rs
@@ -1,21 +1,39 @@
use super::prelude::*;
+use crate::layout::Flow;
/// Wraps content into a box.
#[derive(Debug, PartialEq)]
pub struct BoxFunc {
- body: SyntaxTree
+ body: SyntaxTree,
+ flow: Flow,
}
impl Function for BoxFunc {
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
where Self: Sized {
- if has_arguments(header) {
- return err("pagebreak: expected no arguments");
- }
+ let flow = if header.args.is_empty() {
+ Flow::Vertical
+ } else if header.args.len() == 1 {
+ if let Expression::Ident(ident) = &header.args[0] {
+ match ident.as_str() {
+ "vertical" => Flow::Vertical,
+ "horizontal" => Flow::Horizontal,
+ f => return err(format!("invalid flow specifier: '{}'", f)),
+ }
+ } else {
+ return err(format!(
+ "expected alignment specifier, found: '{}'",
+ header.args[0]
+ ));
+ }
+ } else {
+ return err("box: expected flow specifier or no arguments");
+ };
if let Some(body) = body {
Ok(BoxFunc {
- body: parse(body, ctx)?
+ body: parse(body, ctx)?,
+ flow,
})
} else {
err("box: expected body")
@@ -23,7 +41,11 @@ impl Function for BoxFunc {
}
fn layout(&self, ctx: LayoutContext) -> LayoutResult<CommandList> {
- let layout = layout_tree(&self.body, ctx)?;
+ let layout = layout_tree(&self.body, LayoutContext {
+ flow: self.flow,
+ .. ctx
+ })?;
+
Ok(commands![Command::AddMany(layout)])
}
}
diff --git a/tests/layouts/boxes.typ b/tests/layouts/boxes.typ
deleted file mode 100644
index 055e09fe..00000000
--- a/tests/layouts/boxes.typ
+++ /dev/null
@@ -1,10 +0,0 @@
-{size:400pt*250pt}
-
-[box][
- *Technical University Berlin* [n]
- *Faculty II, Institute for Mathematics* [n]
- Secretary Example [n]
- Prof. Dr. Example [n]
- Assistant #1, Assistant #2, Assistant #3
-]
-[align: right][*WiSe 2019/2020* [n] Week 1]
diff --git a/tests/layouts/coma.typ b/tests/layouts/coma.typ
new file mode 100644
index 00000000..7b3266a9
--- /dev/null
+++ b/tests/layouts/coma.typ
@@ -0,0 +1,31 @@
+{size:420pt*300pt}
+
+[box: horizontal][
+ *Technical University Berlin* [n]
+ *Faculty II, Institute for Mathematics* [n]
+ Secretary Example [n]
+ Prof. Dr. Example [n]
+ Assistant #1, Assistant #2, Assistant #3
+
+ [align: right][*WiSe 2019/2020* [n] Week 1]
+]
+
+// hack for more whitespace
+[box][]
+
+[align: center][
+ *3. Ubungsblatt Computerorientierte Mathematik II* [n]
+ *Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) [n]
+ *Alle Antworten sind zu beweisen.*
+]
+
+[box: horizontal][
+ *1. Aufgabe* [align: right][(1 + 1 + 2 Punkte)]
+]
+
+Ein _Binärbaum_ ist ein Wurzelbaum, in dem jeder Knoten ≤ 2 Kinder hat.
+Die Tiefe eines Knotens _v_ ist die Länge des eindeutigen Weges von der Wurzel
+zu _v_, und die Höhe von _v_ ist die Länge eines längsten (absteigenden) Weges
+von _v_ zu einem Blatt. Die Höhe des Baumes ist die Höhe der Wurzel.
+
+