mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Extend stack layouts from vertical to horizontal flows ➡
This commit is contained in:
parent
1987e5861c
commit
991e879e1d
BIN
fonts/Latin-Modern-Math.otf
Normal file
BIN
fonts/Latin-Modern-Math.otf
Normal file
Binary file not shown.
@ -10,4 +10,5 @@
|
|||||||
"CMU-Typewriter-Italic.ttf" = ["Computer Modern", "Italic", "Serif", "SansSerif", "Monospace"]
|
"CMU-Typewriter-Italic.ttf" = ["Computer Modern", "Italic", "Serif", "SansSerif", "Monospace"]
|
||||||
"CMU-Typewriter-Bold.ttf" = ["Computer Modern", "Bold", "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"]
|
"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"]
|
"NotoEmoji-Regular.ttf" = ["Noto", "Noto Emoji", "Regular", "SansSerif", "Serif", "Monospace"]
|
||||||
|
@ -146,8 +146,10 @@ impl<'d, W: Write> ExportProcess<'d, W> {
|
|||||||
for index in 0 .. num_fonts {
|
for index in 0 .. num_fonts {
|
||||||
let old_index = new_to_old[&index];
|
let old_index = new_to_old[&index];
|
||||||
let font = font_loader.get_with_index(old_index);
|
let font = font_loader.get_with_index(old_index);
|
||||||
let subsetted = font.subsetted(font_chars[&old_index].iter().cloned(), &SUBSET_TABLES)?;
|
let subsetted = font.subsetted(font_chars[&old_index].iter().cloned(), &SUBSET_TABLES)
|
||||||
fonts.push(OwnedFont::from_bytes(subsetted)?);
|
.map(|bytes| OwnedFont::from_bytes(bytes))
|
||||||
|
.unwrap_or_else(|_| font.to_owned())?;
|
||||||
|
fonts.push(subsetted);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((fonts, old_to_new))
|
Ok((fonts, old_to_new))
|
||||||
|
@ -90,7 +90,7 @@ impl FlexLayouter {
|
|||||||
ctx,
|
ctx,
|
||||||
units: vec![],
|
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,
|
usable_width: ctx.space.usable().x,
|
||||||
run: FlexRun {
|
run: FlexRun {
|
||||||
|
@ -134,6 +134,8 @@ pub struct LayoutContext<'a, 'p> {
|
|||||||
pub style: &'a TextStyle,
|
pub style: &'a TextStyle,
|
||||||
/// The alignment to use for the content.
|
/// The alignment to use for the content.
|
||||||
pub alignment: Alignment,
|
pub alignment: Alignment,
|
||||||
|
/// How to stack the context.
|
||||||
|
pub flow: Flow,
|
||||||
/// The primary space to layout in.
|
/// The primary space to layout in.
|
||||||
pub space: LayoutSpace,
|
pub space: LayoutSpace,
|
||||||
/// The additional spaces which are used when the primary space
|
/// The additional spaces which are used when the primary space
|
||||||
@ -176,6 +178,13 @@ pub enum Alignment {
|
|||||||
Center,
|
Center,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The flow of content.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum Flow {
|
||||||
|
Vertical,
|
||||||
|
Horizontal,
|
||||||
|
}
|
||||||
|
|
||||||
/// The error type for layouting.
|
/// The error type for layouting.
|
||||||
pub enum LayoutError {
|
pub enum LayoutError {
|
||||||
/// There is not enough space to add an item.
|
/// There is not enough space to add an item.
|
||||||
|
@ -26,15 +26,17 @@ pub struct StackContext {
|
|||||||
pub space: LayoutSpace,
|
pub space: LayoutSpace,
|
||||||
pub followup_spaces: Option<LayoutSpace>,
|
pub followup_spaces: Option<LayoutSpace>,
|
||||||
pub shrink_to_fit: bool,
|
pub shrink_to_fit: bool,
|
||||||
|
pub flow: Flow,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reuse {
|
macro_rules! reuse {
|
||||||
($ctx:expr) => {
|
($ctx:expr, $flow:expr) => {
|
||||||
StackContext {
|
StackContext {
|
||||||
alignment: $ctx.alignment,
|
alignment: $ctx.alignment,
|
||||||
space: $ctx.space,
|
space: $ctx.space,
|
||||||
followup_spaces: $ctx.followup_spaces,
|
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 {
|
impl StackContext {
|
||||||
/// Create a stack context from a generic layout context.
|
/// Create a stack context from a generic layout context.
|
||||||
pub fn from_layout_ctx(ctx: LayoutContext) -> StackContext {
|
pub fn from_layout_ctx(ctx: LayoutContext) -> StackContext {
|
||||||
reuse!(ctx)
|
reuse!(ctx, ctx.flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a stack context from a flex context.
|
/// Create a stack context from a flex context.
|
||||||
pub fn from_flex_ctx(ctx: FlexContext) -> StackContext {
|
pub fn from_flex_ctx(ctx: FlexContext, flow: Flow) -> StackContext {
|
||||||
reuse!(ctx)
|
reuse!(ctx, flow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,9 +81,15 @@ impl StackLayouter {
|
|||||||
self.start_new_space()?;
|
self.start_new_space()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_dimensions = Size2D {
|
let new_dimensions = match self.ctx.flow {
|
||||||
|
Flow::Vertical => Size2D {
|
||||||
x: crate::size::max(self.dimensions.x, layout.dimensions.x),
|
x: crate::size::max(self.dimensions.x, layout.dimensions.x),
|
||||||
y: self.dimensions.y + layout.dimensions.y,
|
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) {
|
if self.overflows(new_dimensions) {
|
||||||
@ -104,9 +112,13 @@ impl StackLayouter {
|
|||||||
Alignment::Center => self.cursor - Size2D::with_x(layout.dimensions.x / 2),
|
Alignment::Center => self.cursor - Size2D::with_x(layout.dimensions.x / 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cursor.y += layout.dimensions.y;
|
|
||||||
self.dimensions = new_dimensions;
|
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);
|
self.actions.add_layout(position, layout);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -120,23 +132,26 @@ impl StackLayouter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add vertical space after the last layout.
|
/// Add space after the last layout.
|
||||||
pub fn add_space(&mut self, space: Size) -> LayoutResult<()> {
|
pub fn add_space(&mut self, space: Size) -> LayoutResult<()> {
|
||||||
if !self.started {
|
if !self.started {
|
||||||
self.start_new_space()?;
|
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() {
|
if self.ctx.followup_spaces.is_some() {
|
||||||
self.finish_layout(false)?;
|
self.finish_layout(false)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(LayoutError::NotEnoughSpace("cannot fit space into stack"));
|
return Err(LayoutError::NotEnoughSpace("cannot fit space into stack"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.cursor.y += space;
|
self.cursor += new_space;
|
||||||
self.dimensions.y += space;
|
self.dimensions += new_space;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -195,10 +210,17 @@ impl StackLayouter {
|
|||||||
|
|
||||||
/// The remaining space for new layouts.
|
/// The remaining space for new layouts.
|
||||||
pub fn remaining(&self) -> Size2D {
|
pub fn remaining(&self) -> Size2D {
|
||||||
Size2D {
|
match self.ctx.flow {
|
||||||
|
Flow::Vertical => Size2D {
|
||||||
x: self.usable.x,
|
x: self.usable.x,
|
||||||
y: self.usable.y - self.dimensions.y,
|
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.
|
/// Whether the active space of this layouter contains no content.
|
||||||
|
@ -96,6 +96,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
|
|
||||||
let mut ctx = self.ctx;
|
let mut ctx = self.ctx;
|
||||||
ctx.style = &self.style;
|
ctx.style = &self.style;
|
||||||
|
ctx.flow = Flow::Vertical;
|
||||||
ctx.shrink_to_fit = true;
|
ctx.shrink_to_fit = true;
|
||||||
ctx.space.dimensions = remaining;
|
ctx.space.dimensions = remaining;
|
||||||
ctx.space.padding = SizeBox::zero();
|
ctx.space.padding = SizeBox::zero();
|
||||||
|
@ -21,7 +21,7 @@ use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
|
|||||||
|
|
||||||
use crate::func::Scope;
|
use crate::func::Scope;
|
||||||
use crate::layout::{layout_tree, LayoutContext, MultiLayout};
|
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::parsing::{parse, ParseContext, ParseError, ParseResult};
|
||||||
use crate::style::{PageStyle, TextStyle};
|
use crate::style::{PageStyle, TextStyle};
|
||||||
use crate::syntax::SyntaxTree;
|
use crate::syntax::SyntaxTree;
|
||||||
@ -105,6 +105,7 @@ impl<'p> Typesetter<'p> {
|
|||||||
loader: &self.loader,
|
loader: &self.loader,
|
||||||
style: &self.text_style,
|
style: &self.text_style,
|
||||||
alignment: Alignment::Left,
|
alignment: Alignment::Left,
|
||||||
|
flow: Flow::Vertical,
|
||||||
space,
|
space,
|
||||||
followup_spaces: Some(space),
|
followup_spaces: Some(space),
|
||||||
shrink_to_fit: false,
|
shrink_to_fit: false,
|
||||||
|
@ -1,21 +1,39 @@
|
|||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
|
use crate::layout::Flow;
|
||||||
|
|
||||||
/// Wraps content into a box.
|
/// Wraps content into a box.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct BoxFunc {
|
pub struct BoxFunc {
|
||||||
body: SyntaxTree
|
body: SyntaxTree,
|
||||||
|
flow: Flow,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function for BoxFunc {
|
impl Function for BoxFunc {
|
||||||
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
|
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
|
||||||
where Self: Sized {
|
where Self: Sized {
|
||||||
if has_arguments(header) {
|
let flow = if header.args.is_empty() {
|
||||||
return err("pagebreak: expected no arguments");
|
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 {
|
if let Some(body) = body {
|
||||||
Ok(BoxFunc {
|
Ok(BoxFunc {
|
||||||
body: parse(body, ctx)?
|
body: parse(body, ctx)?,
|
||||||
|
flow,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
err("box: expected body")
|
err("box: expected body")
|
||||||
@ -23,7 +41,11 @@ impl Function for BoxFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&self, ctx: LayoutContext) -> LayoutResult<CommandList> {
|
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)])
|
Ok(commands![Command::AddMany(layout)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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]
|
|
31
tests/layouts/coma.typ
Normal file
31
tests/layouts/coma.typ
Normal file
@ -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.
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user