Pretty good stack layouter ✈
This commit is contained in:
parent
d34707a6ae
commit
a791ef1628
@ -66,6 +66,7 @@ pub struct FlexContext {
|
||||
pub axes: LayoutAxes,
|
||||
pub alignment: LayoutAlignment,
|
||||
pub flex_spacing: Size,
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
impl FlexLayouter {
|
||||
@ -75,6 +76,7 @@ impl FlexLayouter {
|
||||
spaces: ctx.spaces,
|
||||
axes: ctx.axes,
|
||||
alignment: ctx.alignment,
|
||||
debug: ctx.debug,
|
||||
});
|
||||
|
||||
let usable = stack.primary_usable();
|
||||
@ -176,7 +178,6 @@ impl FlexLayouter {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn finish_partial_line(&mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -65,14 +65,16 @@ pub struct LayoutContext<'a, 'p> {
|
||||
pub loader: &'a SharedFontLoader<'p>,
|
||||
/// The style for pages and text.
|
||||
pub style: &'a LayoutStyle,
|
||||
/// Whether this layouting process handles the top-level pages.
|
||||
pub top_level: bool,
|
||||
/// The spaces to layout in.
|
||||
pub spaces: LayoutSpaces,
|
||||
/// The initial axes along which content is laid out.
|
||||
pub axes: LayoutAxes,
|
||||
/// The alignment of the finished layout.
|
||||
pub alignment: LayoutAlignment,
|
||||
/// Whether this layouting process handles the top-level pages.
|
||||
pub top_level: bool,
|
||||
/// Whether to debug render a box around the layout.
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
/// A possibly stack-allocated vector of layout spaces.
|
||||
|
@ -1,14 +1,15 @@
|
||||
use smallvec::smallvec;
|
||||
use crate::size::{min, max};
|
||||
use crate::size::max;
|
||||
use super::*;
|
||||
|
||||
/// The stack layouter arranges boxes stacked onto each other.
|
||||
/// The stack layouter stack boxes onto each other along the secondary layouting
|
||||
/// axis.
|
||||
///
|
||||
/// The boxes are laid out in the direction of the secondary layouting axis and
|
||||
/// are aligned along both axes.
|
||||
/// The boxes are aligned along both axes according to their requested
|
||||
/// alignment.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StackLayouter {
|
||||
/// The context for layouter.
|
||||
/// The context for layouting.
|
||||
ctx: StackContext,
|
||||
/// The output layouts.
|
||||
layouts: MultiLayout,
|
||||
@ -17,13 +18,19 @@ pub struct StackLayouter {
|
||||
}
|
||||
|
||||
/// The context for stack layouting.
|
||||
///
|
||||
/// See [`LayoutContext`] for details about the fields.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StackContext {
|
||||
/// The spaces to layout in.
|
||||
pub spaces: LayoutSpaces,
|
||||
/// The initial layouting axes, which can be updated by the
|
||||
/// [`StackLayouter::set_axes`] method.
|
||||
pub axes: LayoutAxes,
|
||||
/// Which alignment to set on the resulting layout. This affects how it will
|
||||
/// be positioned in a parent box.
|
||||
pub alignment: LayoutAlignment,
|
||||
/// Whether to output a command which renders a debugging box showing the
|
||||
/// extent of the layout.
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
/// A layout space composed of subspaces which can have different axes and
|
||||
@ -42,19 +49,26 @@ struct Space {
|
||||
usable: Size2D,
|
||||
/// The specialized extra-needed dimensions to affect the size at all.
|
||||
extra: Size2D,
|
||||
/// The maximal secondary alignment for both specialized axes (horizontal,
|
||||
/// vertical).
|
||||
alignment: (Alignment, Alignment),
|
||||
/// Dictates the valid alignments for new boxes in this space.
|
||||
rulers: Rulers,
|
||||
/// The last added spacing if the last added thing was spacing.
|
||||
last_spacing: LastSpacing,
|
||||
}
|
||||
|
||||
/// The rulers of a space dictate which alignments for new boxes are still
|
||||
/// allowed and which require a new space to be started.
|
||||
#[derive(Debug, Clone)]
|
||||
struct Rulers {
|
||||
top: Alignment,
|
||||
bottom: Alignment,
|
||||
left: Alignment,
|
||||
right: Alignment,
|
||||
}
|
||||
|
||||
impl StackLayouter {
|
||||
/// Create a new stack layouter.
|
||||
pub fn new(ctx: StackContext) -> StackLayouter {
|
||||
let axes = ctx.axes;
|
||||
let space = ctx.spaces[0];
|
||||
|
||||
StackLayouter {
|
||||
ctx,
|
||||
layouts: MultiLayout::new(),
|
||||
@ -64,23 +78,15 @@ impl StackLayouter {
|
||||
|
||||
/// Add a layout to the stack.
|
||||
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
|
||||
// If the layout's secondary alignment is less than what we have already
|
||||
// seen, it needs to go into the next space.
|
||||
if layout.alignment.secondary < *self.secondary_alignment() {
|
||||
if !self.update_rulers(layout.alignment) {
|
||||
self.finish_space(true);
|
||||
}
|
||||
|
||||
if layout.alignment.secondary == *self.secondary_alignment() {
|
||||
// Add a cached soft space if there is one and the alignment stayed
|
||||
// the same. Soft spaces are discarded if the alignment changes.
|
||||
if let LastSpacing::Soft(spacing, _) = self.space.last_spacing {
|
||||
self.add_spacing(spacing, SpacingKind::Hard);
|
||||
}
|
||||
} else {
|
||||
// We want the new maximal alignment and since the layout's
|
||||
// secondary alignment is at least the previous maximum, we just
|
||||
// take it.
|
||||
*self.secondary_alignment() = layout.alignment.secondary;
|
||||
// Now, we add a possibly cached soft space. If the secondary alignment
|
||||
// changed before, a possibly cached space would have already been
|
||||
// discarded.
|
||||
if let LastSpacing::Soft(spacing, _) = self.space.last_spacing {
|
||||
self.add_spacing(spacing, SpacingKind::Hard);
|
||||
}
|
||||
|
||||
// Find the first space that fits the layout.
|
||||
@ -93,8 +99,11 @@ impl StackLayouter {
|
||||
self.finish_space(true);
|
||||
}
|
||||
|
||||
// Change the usable space and size of the space.
|
||||
self.update_metrics(layout.dimensions.generalized(self.ctx.axes));
|
||||
|
||||
// Add the box to the vector and remember that spacings are allowed
|
||||
// again.
|
||||
self.space.layouts.push((self.ctx.axes, layout));
|
||||
self.space.last_spacing = LastSpacing::None;
|
||||
|
||||
@ -103,7 +112,7 @@ impl StackLayouter {
|
||||
|
||||
/// Add multiple layouts to the stack.
|
||||
///
|
||||
/// This function simply calls `add` for each layout.
|
||||
/// This function simply calls `add` repeatedly for each layout.
|
||||
pub fn add_multiple(&mut self, layouts: MultiLayout) -> LayoutResult<()> {
|
||||
for layout in layouts {
|
||||
self.add(layout)?;
|
||||
@ -121,7 +130,6 @@ impl StackLayouter {
|
||||
let dimensions = Size2D::with_y(spacing);
|
||||
|
||||
self.update_metrics(dimensions);
|
||||
|
||||
self.space.layouts.push((self.ctx.axes, Layout {
|
||||
dimensions: dimensions.specialized(self.ctx.axes),
|
||||
alignment: LayoutAlignment::default(),
|
||||
@ -147,6 +155,26 @@ impl StackLayouter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the rulers to account for the new layout. Returns true if a
|
||||
/// space break is necessary.
|
||||
fn update_rulers(&mut self, alignment: LayoutAlignment) -> bool {
|
||||
let axes = self.ctx.axes;
|
||||
let allowed = self.alignment_allowed(axes.primary, alignment.primary)
|
||||
&& self.alignment_allowed(axes.secondary, alignment.secondary);
|
||||
|
||||
if allowed {
|
||||
*self.space.rulers.get(axes.secondary) = alignment.secondary;
|
||||
}
|
||||
|
||||
allowed
|
||||
}
|
||||
|
||||
/// Whether the given alignment is still allowed according to the rulers.
|
||||
fn alignment_allowed(&mut self, axis: Axis, alignment: Alignment) -> bool {
|
||||
alignment >= *self.space.rulers.get(axis)
|
||||
&& alignment <= self.space.rulers.get(axis.inv()).inv()
|
||||
}
|
||||
|
||||
/// Update the size metrics to reflect that a layout or spacing with the
|
||||
/// given generalized dimensions has been added.
|
||||
fn update_metrics(&mut self, dimensions: Size2D) {
|
||||
@ -196,8 +224,12 @@ impl StackLayouter {
|
||||
/// The remaining unpadded, unexpanding spaces. If a multi-layout is laid
|
||||
/// out into these spaces, it will fit into this stack.
|
||||
pub fn remaining(&self) -> LayoutSpaces {
|
||||
let dimensions = self.space.usable
|
||||
- Size2D::with_y(self.space.last_spacing.soft_or_zero())
|
||||
.specialized(self.ctx.axes);
|
||||
|
||||
let mut spaces = smallvec![LayoutSpace {
|
||||
dimensions: self.space.usable,
|
||||
dimensions,
|
||||
padding: SizeBox::ZERO,
|
||||
expand: LayoutExpansion::new(false, false),
|
||||
}];
|
||||
@ -280,18 +312,34 @@ impl StackLayouter {
|
||||
// Step 3: Backward pass. Reduce the bounding boxes from the previous
|
||||
// layouts by what is taken by the following ones.
|
||||
|
||||
let mut extent = Size::ZERO;
|
||||
// The `x` field stores the maximal primary extent in one axis-aligned
|
||||
// run, while the `y` fields stores the accumulated secondary extent.
|
||||
let mut extent = Size2D::ZERO;
|
||||
let mut rotated = false;
|
||||
|
||||
for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() {
|
||||
let (axes, layout) = entry;
|
||||
|
||||
// When the axes get rotated, the the maximal primary size
|
||||
// (`extent.x`) dictates how much secondary extent the whole run
|
||||
// had. This value is thus stored in `extent.y`. The primary extent
|
||||
// is reset for this new axis-aligned run.
|
||||
let is_horizontal = axes.secondary.is_horizontal();
|
||||
if is_horizontal != rotated {
|
||||
extent.y = extent.x;
|
||||
extent.x = Size::ZERO;
|
||||
rotated = is_horizontal;
|
||||
}
|
||||
|
||||
// We reduce the bounding box of this layout at it's end by the
|
||||
// accumulated secondary extent of all layouts we have seen so far,
|
||||
// which are the layouts after this one since we iterate reversed.
|
||||
*bound.secondary_end_mut(*axes) -= axes.secondary.factor() * extent;
|
||||
*bound.secondary_end_mut(*axes) -= axes.secondary.factor() * extent.y;
|
||||
|
||||
// Then, we add this layout's secondary extent to the accumulator.
|
||||
extent += layout.dimensions.secondary(*axes);
|
||||
let size = layout.dimensions.generalized(*axes);
|
||||
extent.x.max_eq(size.x);
|
||||
extent.y += size.y;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
@ -299,38 +347,26 @@ impl StackLayouter {
|
||||
// into a single finished layout.
|
||||
|
||||
let mut actions = LayoutActions::new();
|
||||
actions.add(LayoutAction::DebugBox(dimensions));
|
||||
|
||||
if self.ctx.debug {
|
||||
actions.add(LayoutAction::DebugBox(dimensions));
|
||||
}
|
||||
|
||||
let layouts = std::mem::replace(&mut self.space.layouts, vec![]);
|
||||
|
||||
for ((axes, layout), bound) in layouts.into_iter().zip(bounds) {
|
||||
let LayoutAxes { primary, secondary } = axes;
|
||||
|
||||
let size = layout.dimensions.specialized(axes);
|
||||
let alignment = layout.alignment;
|
||||
|
||||
// The space in which this layout is aligned is given by it's
|
||||
// corresponding bound box.
|
||||
let usable = Size2D::new(
|
||||
bound.right - bound.left,
|
||||
bound.bottom - bound.top
|
||||
).generalized(axes);
|
||||
// The space in which this layout is aligned is given by the
|
||||
// distances between the borders of it's bounding box.
|
||||
let usable =
|
||||
Size2D::new(bound.right - bound.left, bound.bottom - bound.top)
|
||||
.generalized(axes);
|
||||
|
||||
let offsets = Size2D {
|
||||
x: usable.x.anchor(alignment.primary, primary.is_positive())
|
||||
- size.x.anchor(alignment.primary, primary.is_positive()),
|
||||
y: usable.y.anchor(alignment.secondary, secondary.is_positive())
|
||||
- size.y.anchor(alignment.secondary, secondary.is_positive()),
|
||||
};
|
||||
let local = usable.anchor(alignment, axes) - size.anchor(alignment, axes);
|
||||
let pos = Size2D::new(bound.left, bound.top) + local.specialized(axes);
|
||||
|
||||
let position = Size2D::new(bound.left, bound.top)
|
||||
+ offsets.specialized(axes);
|
||||
|
||||
println!("pos: {}", position);
|
||||
println!("usable: {}", usable);
|
||||
println!("size: {}", size);
|
||||
|
||||
actions.add_layout(position, layout);
|
||||
actions.add_layout(pos, layout);
|
||||
}
|
||||
|
||||
self.layouts.push(Layout {
|
||||
@ -339,6 +375,9 @@ impl StackLayouter {
|
||||
actions: actions.to_vec(),
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// Step 5: Start the next space.
|
||||
|
||||
self.start_space(self.next_space(), hard);
|
||||
}
|
||||
|
||||
@ -352,14 +391,6 @@ impl StackLayouter {
|
||||
fn next_space(&self) -> usize {
|
||||
(self.space.index + 1).min(self.ctx.spaces.len() - 1)
|
||||
}
|
||||
|
||||
// Access the secondary alignment in the current system of axes.
|
||||
fn secondary_alignment(&mut self) -> &mut Alignment {
|
||||
match self.ctx.axes.primary.is_horizontal() {
|
||||
true => &mut self.space.alignment.1,
|
||||
false => &mut self.space.alignment.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Space {
|
||||
@ -371,8 +402,24 @@ impl Space {
|
||||
size: Size2D::ZERO,
|
||||
usable,
|
||||
extra: Size2D::ZERO,
|
||||
alignment: (Alignment::Origin, Alignment::Origin),
|
||||
rulers: Rulers {
|
||||
top: Alignment::Origin,
|
||||
bottom: Alignment::Origin,
|
||||
left: Alignment::Origin,
|
||||
right: Alignment::Origin,
|
||||
},
|
||||
last_spacing: LastSpacing::Hard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rulers {
|
||||
fn get(&mut self, axis: Axis) -> &mut Alignment {
|
||||
match axis {
|
||||
Axis::TopToBottom => &mut self.top,
|
||||
Axis::BottomToTop => &mut self.bottom,
|
||||
Axis::LeftToRight => &mut self.left,
|
||||
Axis::RightToLeft => &mut self.right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
spaces: ctx.spaces.clone(),
|
||||
axes: ctx.axes,
|
||||
alignment: ctx.alignment,
|
||||
debug: ctx.debug,
|
||||
}),
|
||||
style: ctx.style.clone(),
|
||||
ctx,
|
||||
@ -75,8 +76,9 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
let commands = func.0.layout(LayoutContext {
|
||||
loader: self.ctx.loader,
|
||||
style: &self.style,
|
||||
top_level: false,
|
||||
spaces,
|
||||
top_level: false,
|
||||
debug: true,
|
||||
.. self.ctx
|
||||
})?;
|
||||
|
||||
|
@ -26,7 +26,7 @@ use toddle::Error as FontError;
|
||||
|
||||
use crate::func::Scope;
|
||||
use crate::layout::{layout_tree, MultiLayout, LayoutContext};
|
||||
use crate::layout::{LayoutAxes, LayoutAlignment, Axis, Alignment};
|
||||
use crate::layout::{LayoutAxes, LayoutAlignment};
|
||||
use crate::layout::{LayoutResult, LayoutSpace, LayoutExpansion};
|
||||
use crate::syntax::{parse, SyntaxTree, ParseContext, Span, ParseResult};
|
||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||
@ -94,7 +94,6 @@ impl<'p> Typesetter<'p> {
|
||||
&tree,
|
||||
LayoutContext {
|
||||
loader: &self.loader,
|
||||
top_level: true,
|
||||
style: &self.style,
|
||||
spaces: smallvec![LayoutSpace {
|
||||
dimensions: self.style.page.dimensions,
|
||||
@ -103,6 +102,8 @@ impl<'p> Typesetter<'p> {
|
||||
}],
|
||||
axes: LayoutAxes::default(),
|
||||
alignment: LayoutAlignment::default(),
|
||||
top_level: true,
|
||||
debug: false,
|
||||
},
|
||||
)?)
|
||||
}
|
||||
|
@ -7,19 +7,25 @@ function! {
|
||||
pub struct Boxed {
|
||||
body: SyntaxTree,
|
||||
map: ExtentMap<PSize>,
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
parse(args, body, ctx) {
|
||||
Boxed {
|
||||
body: parse!(optional: body, ctx).unwrap_or(SyntaxTree::new()),
|
||||
map: ExtentMap::new(&mut args, false)?,
|
||||
debug: args.get_key_opt::<bool>("debug")?
|
||||
.map(Spanned::value)
|
||||
.unwrap_or(true),
|
||||
}
|
||||
}
|
||||
|
||||
layout(self, mut ctx) {
|
||||
use SpecificAxisKind::*;
|
||||
|
||||
ctx.debug = self.debug;
|
||||
let space = &mut ctx.spaces[0];
|
||||
|
||||
self.map.apply_with(ctx.axes, |axis, p| {
|
||||
let entity = match axis {
|
||||
Horizontal => { space.expand.horizontal = true; &mut space.dimensions.x },
|
||||
@ -27,7 +33,7 @@ function! {
|
||||
};
|
||||
|
||||
*entity = p.concretize(*entity)
|
||||
});
|
||||
})?;
|
||||
|
||||
vec![AddMultiple(layout_tree(&self.body, ctx)?)]
|
||||
}
|
||||
|
23
src/size.rs
23
src/size.rs
@ -6,7 +6,7 @@ use std::iter::Sum;
|
||||
use std::ops::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::layout::{LayoutAxes, Axis, Alignment};
|
||||
use crate::layout::{LayoutAxes, LayoutAlignment, Axis, Alignment};
|
||||
|
||||
/// A general space type.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
@ -91,11 +91,11 @@ impl Size {
|
||||
*self = min(*self, other);
|
||||
}
|
||||
|
||||
/// The specialized anchor position for an item with the given alignment in a
|
||||
/// container with a given size along the given axis.
|
||||
pub fn anchor(&self, alignment: Alignment, positive: bool) -> Size {
|
||||
/// The anchor position along the given axis for an item with the given
|
||||
/// alignment in a container with this size.
|
||||
pub fn anchor(&self, alignment: Alignment, axis: Axis) -> Size {
|
||||
use Alignment::*;
|
||||
match (positive, alignment) {
|
||||
match (axis.is_positive(), alignment) {
|
||||
(true, Origin) | (false, End) => Size::ZERO,
|
||||
(_, Center) => *self / 2,
|
||||
(true, End) | (false, Origin) => *self,
|
||||
@ -219,9 +219,16 @@ impl Size2D {
|
||||
self.y.min_eq(other.y);
|
||||
}
|
||||
|
||||
/// Swap the two dimensions.
|
||||
pub fn swap(&mut self) {
|
||||
std::mem::swap(&mut self.x, &mut self.y);
|
||||
/// The anchor position along the given axis for an item with the given
|
||||
/// alignment in a container with this size.
|
||||
///
|
||||
/// This assumes the size to be generalized such that `x` corresponds to the
|
||||
/// primary axis.
|
||||
pub fn anchor(&self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size2D {
|
||||
Size2D {
|
||||
x: self.x.anchor(alignment.primary, axes.primary),
|
||||
y: self.y.anchor(alignment.secondary, axes.secondary),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
81
tests/layouts/stack.typ
Normal file
81
tests/layouts/stack.typ
Normal file
@ -0,0 +1,81 @@
|
||||
[page.size: w=5cm, h=5cm]
|
||||
[page.margins: 0cm]
|
||||
|
||||
// Test 1
|
||||
[box: w=1, h=1, debug=false][
|
||||
[box][
|
||||
[align: center]
|
||||
[box: ps=3cm, ss=1cm]
|
||||
[direction: ttb, ltr]
|
||||
[box: ps=3cm, ss=1cm]
|
||||
[box: ps=1cm, ss=1cm]
|
||||
[box: ps=2cm, ss=1cm]
|
||||
[box: ps=1cm, ss=1cm]
|
||||
]
|
||||
]
|
||||
[page.break]
|
||||
|
||||
// Test 2
|
||||
[box: w=1, h=1, debug=false][
|
||||
[align: secondary=top] Top
|
||||
[align: secondary=center] Center
|
||||
[align: secondary=bottom] Bottom
|
||||
[direction: ttb, ltr]
|
||||
[align: secondary=origin, primary=bottom]
|
||||
[box: w=1cm, h=1cm]
|
||||
]
|
||||
[page.break]
|
||||
|
||||
// Test 3
|
||||
[box: w=1, h=1, debug=false][
|
||||
[align: center][
|
||||
Somelongspacelessword!
|
||||
[align: left] Some
|
||||
[align: right] word!
|
||||
]
|
||||
]
|
||||
[page.break]
|
||||
|
||||
// Test 4
|
||||
[box: w=1, h=1, debug=false][
|
||||
[direction: ltr, ttb]
|
||||
[align: center]
|
||||
[align: secondary=origin]
|
||||
[box: ps=1cm, ss=1cm]
|
||||
[align: secondary=center]
|
||||
[box: ps=3cm, ss=1cm]
|
||||
[box: ps=4cm, ss=0.5cm]
|
||||
[align: secondary=end]
|
||||
[box: ps=2cm, ss=1cm]
|
||||
]
|
||||
[page.break]
|
||||
|
||||
// Test 5
|
||||
[box: w=1, h=1, debug=false][
|
||||
[direction: primary=btt, secondary=ltr]
|
||||
[align: primary=center, secondary=left]
|
||||
[box: h=2cm, w=1cm]
|
||||
|
||||
[direction: rtl, btt]
|
||||
[align: center]
|
||||
[align: vertical=origin] ORIGIN
|
||||
[align: vertical=center] CENTER
|
||||
[align: vertical=end] END
|
||||
]
|
||||
[page.break]
|
||||
|
||||
// Test 6
|
||||
[box: w=1, h=1, debug=false][
|
||||
[box: w=4cm, h=1cm]
|
||||
|
||||
[align: primary=right, secondary=center] CENTER
|
||||
|
||||
[direction: btt, rtl]
|
||||
[align: primary=center, secondary=origin]
|
||||
[box: w=0.5cm, h=0.5cm]
|
||||
[box: w=0.5cm, h=1cm]
|
||||
[box: w=0.5cm, h=0.5cm]
|
||||
|
||||
[align: primary=origin, secondary=end]
|
||||
END
|
||||
]
|
@ -1,52 +0,0 @@
|
||||
[page.size: w=5cm, h=5cm]
|
||||
[page.margins: 0cm]
|
||||
|
||||
// Test 1
|
||||
// [box][
|
||||
// [align: center]
|
||||
// [box: ps=3cm, ss=1cm]
|
||||
// [direction: ttb, ltr]
|
||||
// [box: ps=3cm, ss=1cm]
|
||||
// [box: ps=1cm, ss=1cm]
|
||||
// [box: ps=2cm, ss=1cm]
|
||||
// [box: ps=1cm, ss=1cm]
|
||||
// ]
|
||||
|
||||
// Test 2
|
||||
// [align: secondary=top] Top
|
||||
// [align: secondary=center] Center
|
||||
// [align: secondary=bottom] Bottom
|
||||
// [direction: ttb, ltr]
|
||||
// [align: primary=bottom]
|
||||
// [box: w=1cm, h=1cm]
|
||||
|
||||
// Test 3
|
||||
// [align: center][
|
||||
// Somelongspacelessword!
|
||||
// [align: left] Some
|
||||
// [align: right] word!
|
||||
// ]
|
||||
|
||||
// Test 4: In all combinations, please!
|
||||
// [direction: ltr, ttb]
|
||||
// [align: center]
|
||||
// [align: secondary=origin]
|
||||
// [box: ps=1cm, ss=1cm]
|
||||
// [align: secondary=center]
|
||||
// [box: ps=3cm, ss=1cm]
|
||||
// [box: ps=4cm, ss=0.5cm]
|
||||
// [align: secondary=end]
|
||||
// [box: ps=2cm, ss=1cm]
|
||||
|
||||
[align: primary=left, secondary=center]
|
||||
[box: w=4cm, h=2cm]
|
||||
|
||||
[direction: primary=btt, secondary=ltr]
|
||||
[align: primary=center, secondary=left]
|
||||
[box: h=2cm, w=1cm]
|
||||
|
||||
// [direction: rtl, btt]
|
||||
// [align: center]
|
||||
// [align: vertical=origin] ORIGIN
|
||||
// [align: vertical=center] CENTER
|
||||
// [align: vertical=end] END
|
@ -56,7 +56,6 @@ class MultiboxRenderer:
|
||||
|
||||
renderer = BoxRenderer(self.fonts, width, height)
|
||||
for i in range(action_count):
|
||||
if i == 0: continue
|
||||
command = self.content[start + i]
|
||||
renderer.execute(command)
|
||||
|
||||
@ -134,7 +133,7 @@ class BoxRenderer:
|
||||
|
||||
if cmd == 'm':
|
||||
x, y = (pix(float(s)) for s in parts)
|
||||
self.cursor = (x, y)
|
||||
self.cursor = [x, y]
|
||||
|
||||
elif cmd == 'f':
|
||||
index = int(parts[0])
|
||||
@ -143,7 +142,9 @@ class BoxRenderer:
|
||||
|
||||
elif cmd == 'w':
|
||||
text = command[2:]
|
||||
width = self.draw.textsize(text, font=self.font)[0]
|
||||
self.draw.text(self.cursor, text, (0, 0, 0, 255), font=self.font)
|
||||
self.cursor[0] += width
|
||||
|
||||
elif cmd == 'b':
|
||||
x, y = self.cursor
|
||||
|
Loading…
Reference in New Issue
Block a user