Remove unncessary wrappers and typedefs 🛑

This commit is contained in:
Laurenz 2020-10-04 19:57:39 +02:00
parent 105f70867d
commit c1dd872b34
12 changed files with 134 additions and 188 deletions

View File

@ -9,7 +9,7 @@ use fontdock::{FontStretch, FontStyle, FontWeight};
use super::dict::{Dict, SpannedEntry}; use super::dict::{Dict, SpannedEntry};
use crate::color::RgbaColor; use crate::color::RgbaColor;
use crate::geom::Linear; use crate::geom::Linear;
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign}; use crate::layout::{Command, Dir, LayoutContext, SpecAlign};
use crate::paper::Paper; use crate::paper::Paper;
use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree}; use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree};
use crate::{DynFuture, Feedback}; use crate::{DynFuture, Feedback};
@ -49,7 +49,7 @@ pub enum Value {
/// An executable function. /// An executable function.
Func(ValueFunc), Func(ValueFunc),
/// Layouting commands. /// Layouting commands.
Commands(Commands), Commands(Vec<Command>),
/// The result of invalid operations. /// The result of invalid operations.
Error, Error,
} }
@ -83,7 +83,7 @@ impl Spanned<Value> {
/// ///
/// If this is already a command-value, it is simply unwrapped, otherwise /// If this is already a command-value, it is simply unwrapped, otherwise
/// the value is represented as layoutable content in a reasonable way. /// the value is represented as layoutable content in a reasonable way.
pub fn into_commands(self) -> Commands { pub fn into_commands(self) -> Vec<Command> {
match self.v { match self.v {
// Pass-through. // Pass-through.
Value::Commands(commands) => commands, Value::Commands(commands) => commands,

View File

@ -138,7 +138,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
let mut face = FaceId::MAX; let mut face = FaceId::MAX;
let mut size = 0.0; let mut size = 0.0;
for (pos, element) in &page.elements.0 { for (pos, element) in &page.elements {
match element { match element {
LayoutElement::Text(shaped) => { LayoutElement::Text(shaped) => {
if shaped.face != face || shaped.size != size { if shaped.face != face || shaped.size != size {
@ -287,7 +287,7 @@ fn remap_fonts(layouts: &[BoxLayout]) -> (HashMap<FaceId, usize>, Vec<FaceId>) {
// We want to find out which font faces are used at all. To do that, look at // We want to find out which font faces are used at all. To do that, look at
// each text element to find out which face is uses. // each text element to find out which face is uses.
for layout in layouts { for layout in layouts {
for (_, element) in &layout.elements.0 { for (_, element) in &layout.elements {
let LayoutElement::Text(shaped) = element; let LayoutElement::Text(shaped) = element;
to_pdf.entry(shaped.face).or_insert_with(|| { to_pdf.entry(shaped.face).or_insert_with(|| {
let next_id = to_layout.len(); let next_id = to_layout.len();

View File

@ -1,83 +0,0 @@
//! Basic building blocks of layouts.
use std::fmt::{self, Debug, Formatter};
use fontdock::FaceId;
use ttf_parser::GlyphId;
use crate::geom::Point;
/// A collection of absolutely positioned layout elements.
#[derive(Debug, Default, Clone, PartialEq)]
pub struct LayoutElements(pub Vec<(Point, LayoutElement)>);
impl LayoutElements {
/// Create an new empty collection.
pub fn new() -> Self {
Self(vec![])
}
/// Add an element at a position.
pub fn push(&mut self, pos: Point, element: LayoutElement) {
self.0.push((pos, element));
}
/// Add all elements of another collection, placing them relative to the
/// given position.
pub fn push_elements(&mut self, pos: Point, more: Self) {
for (subpos, element) in more.0 {
self.0.push((pos + subpos.to_vec2(), element));
}
}
}
/// A layout element, the basic building block layouts are composed of.
#[derive(Debug, Clone, PartialEq)]
pub enum LayoutElement {
Text(Shaped),
}
/// A shaped run of text.
#[derive(Clone, PartialEq)]
pub struct Shaped {
/// The shaped text.
pub text: String,
/// The font face the text was shaped with.
pub face: FaceId,
/// The shaped glyphs.
pub glyphs: Vec<GlyphId>,
/// The horizontal offsets of the glyphs. This is indexed parallel to `glyphs`.
/// Vertical offets are not yet supported.
pub offsets: Vec<f64>,
/// The font size.
pub size: f64,
}
impl Shaped {
/// Create a new shape run with empty `text`, `glyphs` and `offsets`.
pub fn new(face: FaceId, size: f64) -> Self {
Self {
text: String::new(),
face,
glyphs: vec![],
offsets: vec![],
size,
}
}
/// Encode the glyph ids into a big-endian byte buffer.
pub fn encode_glyphs_be(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(2 * self.glyphs.len());
for &GlyphId(g) in &self.glyphs {
bytes.push((g >> 8) as u8);
bytes.push((g & 0xff) as u8);
}
bytes
}
}
impl Debug for Shaped {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Shaped({})", self.text)
}
}

View File

@ -22,7 +22,7 @@ pub struct LineLayouter {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LineContext { pub struct LineContext {
/// The spaces to layout into. /// The spaces to layout into.
pub spaces: LayoutSpaces, pub spaces: Vec<LayoutSpace>,
/// The initial layouting system, which can be updated through `set_sys`. /// The initial layouting system, which can be updated through `set_sys`.
pub sys: LayoutSystem, pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line /// The alignment of the _resulting_ layout. This does not effect the line
@ -132,15 +132,6 @@ impl LineLayouter {
self.run.last_spacing = LastSpacing::None; self.run.last_spacing = LastSpacing::None;
} }
/// Add multiple layouts.
///
/// This is equivalent to calling `add` repeatedly for each layout.
pub fn add_multiple(&mut self, layouts: MultiLayout) {
for layout in layouts {
self.add(layout);
}
}
/// The remaining usable size of the line. /// The remaining usable size of the line.
/// ///
/// This specifies how much more would fit before a line break would be /// This specifies how much more would fit before a line break would be
@ -201,7 +192,7 @@ impl LineLayouter {
/// If `replace_empty` is true, the current space is replaced if there are /// If `replace_empty` is true, the current space is replaced if there are
/// no boxes laid out into it yet. Otherwise, the followup spaces are /// no boxes laid out into it yet. Otherwise, the followup spaces are
/// replaced. /// replaced.
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) { pub fn set_spaces(&mut self, spaces: Vec<LayoutSpace>, replace_empty: bool) {
self.stack.set_spaces(spaces, replace_empty && self.line_is_empty()); self.stack.set_spaces(spaces, replace_empty && self.line_is_empty());
} }
@ -212,7 +203,7 @@ impl LineLayouter {
/// The remaining inner spaces. If something is laid out into these spaces, /// The remaining inner spaces. If something is laid out into these spaces,
/// it will fit into this layouter's underlying stack. /// it will fit into this layouter's underlying stack.
pub fn remaining(&self) -> LayoutSpaces { pub fn remaining(&self) -> Vec<LayoutSpace> {
let mut spaces = self.stack.remaining(); let mut spaces = self.stack.remaining();
*spaces[0].size.secondary_mut(self.ctx.sys) -= self.run.size.height; *spaces[0].size.secondary_mut(self.ctx.sys) -= self.run.size.height;
spaces spaces
@ -224,7 +215,7 @@ impl LineLayouter {
} }
/// Finish everything up and return the final collection of boxes. /// Finish everything up and return the final collection of boxes.
pub fn finish(mut self) -> MultiLayout { pub fn finish(mut self) -> Vec<BoxLayout> {
self.finish_line_if_not_empty(); self.finish_line_if_not_empty();
self.stack.finish() self.stack.finish()
} }
@ -239,27 +230,25 @@ impl LineLayouter {
/// Finish the active line and start a new one. /// Finish the active line and start a new one.
pub fn finish_line(&mut self) { pub fn finish_line(&mut self) {
let mut elements = LayoutElements::new(); let mut layout = BoxLayout::new(
self.run.size.specialized(self.ctx.sys),
self.run.align.unwrap_or_default(),
);
let layouts = std::mem::take(&mut self.run.layouts); let layouts = std::mem::take(&mut self.run.layouts);
for (offset, layout) in layouts { for (offset, child) in layouts {
let x = match self.ctx.sys.primary.is_positive() { let x = match self.ctx.sys.primary.is_positive() {
true => offset, true => offset,
false => self.run.size.width - offset - layout.size.primary(self.ctx.sys), false => self.run.size.width - offset - child.size.primary(self.ctx.sys),
}; };
let pos = Point::new(x, 0.0); let pos = Point::new(x, 0.0);
elements.push_elements(pos, layout.elements); layout.push_layout(pos, child);
} }
self.stack.add(BoxLayout { self.stack.add(layout);
size: self.run.size.specialized(self.ctx.sys),
align: self.run.align.unwrap_or(LayoutAlign::START),
elements,
});
self.run = LineRun::new(); self.run = LineRun::new();
self.stack.add_spacing(self.ctx.line_spacing, SpacingKind::LINE); self.stack.add_spacing(self.ctx.line_spacing, SpacingKind::LINE);
} }

View File

@ -1,13 +1,11 @@
//! Layouting of syntax trees into box layouts. //! Layouting of syntax trees.
pub mod primitive; pub mod primitive;
mod elements;
mod line; mod line;
mod stack; mod stack;
mod tree; mod tree;
pub use elements::*;
pub use line::*; pub use line::*;
pub use primitive::*; pub use primitive::*;
pub use stack::*; pub use stack::*;
@ -17,6 +15,7 @@ use crate::geom::{Insets, Point, Rect, RectExt, Sides, Size, SizeExt};
use crate::eval::{PageState, State, TextState}; use crate::eval::{PageState, State, TextState};
use crate::font::SharedFontLoader; use crate::font::SharedFontLoader;
use crate::shaping::Shaped;
use crate::syntax::SynTree; use crate::syntax::SynTree;
use crate::{Feedback, Pass}; use crate::{Feedback, Pass};
@ -25,7 +24,7 @@ pub async fn layout(
tree: &SynTree, tree: &SynTree,
state: State, state: State,
loader: SharedFontLoader, loader: SharedFontLoader,
) -> Pass<MultiLayout> { ) -> Pass<Vec<BoxLayout>> {
let space = LayoutSpace { let space = LayoutSpace {
size: state.page.size, size: state.page.size,
insets: state.page.insets(), insets: state.page.insets(),
@ -51,9 +50,6 @@ pub async fn layout(
Pass::new(layouts, ctx.f) Pass::new(layouts, ctx.f)
} }
/// A collection of layouts.
pub type MultiLayout = Vec<BoxLayout>;
/// A finished box with content at fixed positions. /// A finished box with content at fixed positions.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct BoxLayout { pub struct BoxLayout {
@ -62,7 +58,34 @@ pub struct BoxLayout {
/// How to align this box in a parent container. /// How to align this box in a parent container.
pub align: LayoutAlign, pub align: LayoutAlign,
/// The elements composing this layout. /// The elements composing this layout.
pub elements: LayoutElements, pub elements: Vec<(Point, LayoutElement)>,
}
impl BoxLayout {
/// Create an new empty collection.
pub fn new(size: Size, align: LayoutAlign) -> Self {
Self { size, align, elements: vec![] }
}
/// Add an element at a position.
pub fn push(&mut self, pos: Point, element: LayoutElement) {
self.elements.push((pos, element));
}
/// Add all elements of another collection, placing them relative to the
/// given position.
pub fn push_layout(&mut self, pos: Point, more: Self) {
for (subpos, element) in more.elements {
self.push(pos + subpos.to_vec2(), element);
}
}
}
/// A layout element, the basic building block layouts are composed of.
#[derive(Debug, Clone, PartialEq)]
pub enum LayoutElement {
/// Shaped text.
Text(Shaped),
} }
/// The context for layouting. /// The context for layouting.
@ -86,15 +109,12 @@ pub struct LayoutConstraints {
/// The unpadded size of this container (the base 100% for relative sizes). /// The unpadded size of this container (the base 100% for relative sizes).
pub base: Size, pub base: Size,
/// The spaces to layout into. /// The spaces to layout into.
pub spaces: LayoutSpaces, pub spaces: Vec<LayoutSpace>,
/// Whether to spill over into copies of the last space or finish layouting /// Whether to spill over into copies of the last space or finish layouting
/// when the last space is used up. /// when the last space is used up.
pub repeat: bool, pub repeat: bool,
} }
/// A collection of layout spaces.
pub type LayoutSpaces = Vec<LayoutSpace>;
/// The space into which content is laid out. /// The space into which content is laid out.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct LayoutSpace { pub struct LayoutSpace {
@ -129,9 +149,6 @@ impl LayoutSpace {
} }
} }
/// A sequence of layouting commands.
pub type Commands = Vec<Command>;
/// Commands executable by the layouting engine. /// Commands executable by the layouting engine.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Command { pub enum Command {
@ -146,10 +163,6 @@ pub enum Command {
/// Add a finished layout. /// Add a finished layout.
Add(BoxLayout), Add(BoxLayout),
/// Add multiple layouts, one after another. This is equivalent to multiple
/// `Add` commands.
AddMultiple(MultiLayout),
/// Add spacing of the given kind along the primary or secondary axis. The /// Add spacing of the given kind along the primary or secondary axis. The
/// kind defines how the spacing interacts with surrounding spacing. /// kind defines how the spacing interacts with surrounding spacing.
AddSpacing(f64, SpacingKind, GenAxis), AddSpacing(f64, SpacingKind, GenAxis),

View File

@ -17,12 +17,10 @@ impl Default for LayoutSystem {
/// Specifies where to align a layout in a parent container. /// Specifies where to align a layout in a parent container.
pub type LayoutAlign = Gen2<GenAlign>; pub type LayoutAlign = Gen2<GenAlign>;
impl LayoutAlign { impl Default for LayoutAlign {
/// The layout alignment that has both components set to `Start`. fn default() -> Self {
pub const START: Self = Self { Self::new(GenAlign::Start, GenAlign::Start)
primary: GenAlign::Start, }
secondary: GenAlign::Start,
};
} }
/// Whether to expand a layout to an area's full size or shrink it to fit its content. /// Whether to expand a layout to an area's full size or shrink it to fit its content.

View File

@ -24,7 +24,7 @@ use super::*;
/// Performs the stack layouting. /// Performs the stack layouting.
pub struct StackLayouter { pub struct StackLayouter {
ctx: StackContext, ctx: StackContext,
layouts: MultiLayout, layouts: Vec<BoxLayout>,
/// The in-progress space. /// The in-progress space.
space: Space, space: Space,
} }
@ -33,7 +33,7 @@ pub struct StackLayouter {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StackContext { pub struct StackContext {
/// The spaces to layout into. /// The spaces to layout into.
pub spaces: LayoutSpaces, pub spaces: Vec<LayoutSpace>,
/// The initial layouting system, which can be updated through `set_sys`. /// The initial layouting system, which can be updated through `set_sys`.
pub sys: LayoutSystem, pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line /// The alignment of the _resulting_ layout. This does not effect the line
@ -75,7 +75,7 @@ impl StackLayouter {
let space = ctx.spaces[0]; let space = ctx.spaces[0];
Self { Self {
ctx, ctx,
layouts: MultiLayout::new(), layouts: vec![],
space: Space::new(0, true, space.usable()), space: Space::new(0, true, space.usable()),
} }
} }
@ -110,15 +110,6 @@ impl StackLayouter {
self.space.last_spacing = LastSpacing::None; self.space.last_spacing = LastSpacing::None;
} }
/// Add multiple layouts to the stack.
///
/// This is equivalent to calling `add` repeatedly for each layout.
pub fn add_multiple(&mut self, layouts: MultiLayout) {
for layout in layouts {
self.add(layout);
}
}
/// Add spacing to the stack. /// Add spacing to the stack.
pub fn add_spacing(&mut self, mut spacing: f64, kind: SpacingKind) { pub fn add_spacing(&mut self, mut spacing: f64, kind: SpacingKind) {
match kind { match kind {
@ -129,11 +120,13 @@ impl StackLayouter {
let size = Size::new(0.0, spacing); let size = Size::new(0.0, spacing);
self.update_metrics(size); self.update_metrics(size);
self.space.layouts.push((self.ctx.sys, BoxLayout { self.space.layouts.push((
size: size.specialized(self.ctx.sys), self.ctx.sys,
align: LayoutAlign::START, BoxLayout::new(
elements: LayoutElements::new(), size.specialized(self.ctx.sys),
})); LayoutAlign::default(),
),
));
self.space.last_spacing = LastSpacing::Hard; self.space.last_spacing = LastSpacing::Hard;
} }
@ -208,7 +201,7 @@ impl StackLayouter {
/// If `replace_empty` is true, the current space is replaced if there are /// If `replace_empty` is true, the current space is replaced if there are
/// no boxes laid out into it yet. Otherwise, the followup spaces are /// no boxes laid out into it yet. Otherwise, the followup spaces are
/// replaced. /// replaced.
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) { pub fn set_spaces(&mut self, spaces: Vec<LayoutSpace>, replace_empty: bool) {
if replace_empty && self.space_is_empty() { if replace_empty && self.space_is_empty() {
self.ctx.spaces = spaces; self.ctx.spaces = spaces;
self.start_space(0, self.space.hard); self.start_space(0, self.space.hard);
@ -233,7 +226,7 @@ impl StackLayouter {
/// The remaining inner spaces. If something is laid out into these spaces, /// The remaining inner spaces. If something is laid out into these spaces,
/// it will fit into this stack. /// it will fit into this stack.
pub fn remaining(&self) -> LayoutSpaces { pub fn remaining(&self) -> Vec<LayoutSpace> {
let size = self.usable(); let size = self.usable();
let mut spaces = vec![LayoutSpace { let mut spaces = vec![LayoutSpace {
@ -267,7 +260,7 @@ impl StackLayouter {
} }
/// Finish everything up and return the final collection of boxes. /// Finish everything up and return the final collection of boxes.
pub fn finish(mut self) -> MultiLayout { pub fn finish(mut self) -> Vec<BoxLayout> {
if self.space.hard || !self.space_is_empty() { if self.space.hard || !self.space_is_empty() {
self.finish_space(false); self.finish_space(false);
} }
@ -360,12 +353,12 @@ impl StackLayouter {
// Step 4: Align each layout in its bounding box and collect everything // Step 4: Align each layout in its bounding box and collect everything
// into a single finished layout. // into a single finished layout.
let mut elements = LayoutElements::new(); let mut layout = BoxLayout::new(size, self.ctx.align);
let layouts = std::mem::take(&mut self.space.layouts); let layouts = std::mem::take(&mut self.space.layouts);
for ((sys, layout), bound) in layouts.into_iter().zip(bounds) { for ((sys, child), bound) in layouts.into_iter().zip(bounds) {
let size = layout.size.specialized(sys); let size = child.size.specialized(sys);
let align = layout.align; let align = child.align;
// The space in which this layout is aligned is given by the // The space in which this layout is aligned is given by the
// distances between the borders of its bounding box. // distances between the borders of its bounding box.
@ -373,10 +366,10 @@ impl StackLayouter {
let local = usable.anchor(align, sys) - size.anchor(align, sys); let local = usable.anchor(align, sys) - size.anchor(align, sys);
let pos = bound.origin() + local.to_size().specialized(sys).to_vec2(); let pos = bound.origin() + local.to_size().specialized(sys).to_vec2();
elements.push_elements(pos, layout.elements); layout.push_layout(pos, child);
} }
self.layouts.push(BoxLayout { size, align: self.ctx.align, elements }); self.layouts.push(layout);
// ------------------------------------------------------------------ // // ------------------------------------------------------------------ //
// Step 5: Start the next space. // Step 5: Start the next space.

View File

@ -8,7 +8,7 @@ use crate::syntax::{
use crate::DynFuture; use crate::DynFuture;
/// Layout a syntax tree in a given context. /// Layout a syntax tree in a given context.
pub async fn layout_tree(tree: &SynTree, ctx: &mut LayoutContext) -> MultiLayout { pub async fn layout_tree(tree: &SynTree, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
let mut layouter = TreeLayouter::new(ctx); let mut layouter = TreeLayouter::new(ctx);
layouter.layout_tree(tree).await; layouter.layout_tree(tree).await;
layouter.finish() layouter.finish()
@ -38,7 +38,7 @@ impl<'a> TreeLayouter<'a> {
} }
} }
fn finish(self) -> MultiLayout { fn finish(self) -> Vec<BoxLayout> {
self.layouter.finish() self.layouter.finish()
} }
@ -175,7 +175,6 @@ impl<'a> TreeLayouter<'a> {
LayoutSyntaxTree(tree) => self.layout_tree(&tree).await, LayoutSyntaxTree(tree) => self.layout_tree(&tree).await,
Add(layout) => self.layouter.add(layout), Add(layout) => self.layouter.add(layout),
AddMultiple(layouts) => self.layouter.add_multiple(layouts),
AddSpacing(space, kind, axis) => match axis { AddSpacing(space, kind, axis) => match axis {
GenAxis::Primary => self.layouter.add_primary_spacing(space, kind), GenAxis::Primary => self.layouter.add_primary_spacing(space, kind),
GenAxis::Secondary => self.layouter.add_secondary_spacing(space, kind), GenAxis::Secondary => self.layouter.add_secondary_spacing(space, kind),

View File

@ -43,9 +43,9 @@ use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::eval::{State, Value}; use crate::eval::State;
use crate::font::SharedFontLoader; use crate::font::SharedFontLoader;
use crate::layout::{Commands, MultiLayout}; use crate::layout::BoxLayout;
use crate::syntax::{Decoration, Offset, Pos, SpanVec}; use crate::syntax::{Decoration, Offset, Pos, SpanVec};
/// Process source code directly into a collection of layouts. /// Process source code directly into a collection of layouts.
@ -53,7 +53,7 @@ pub async fn typeset(
src: &str, src: &str,
state: State, state: State,
loader: SharedFontLoader, loader: SharedFontLoader,
) -> Pass<MultiLayout> { ) -> Pass<Vec<BoxLayout>> {
let parsed = parse::parse(src); let parsed = parse::parse(src);
let layouted = layout::layout(&parsed.output, state, loader).await; let layouted = layout::layout(&parsed.output, state, loader).await;
let feedback = Feedback::merge(parsed.feedback, layouted.feedback); let feedback = Feedback::merge(parsed.feedback, layouted.feedback);
@ -93,13 +93,6 @@ impl<T> Pass<T> {
} }
} }
impl Pass<Value> {
/// Create a new pass with a list of layouting commands.
pub fn commands(commands: Commands, feedback: Feedback) -> Self {
Pass::new(Value::Commands(commands), feedback)
}
}
/// Diagnostic and semantic syntax highlighting data. /// Diagnostic and semantic syntax highlighting data.
#[derive(Debug, Default, Clone, Eq, PartialEq)] #[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Feedback { pub struct Feedback {

View File

@ -4,7 +4,7 @@
pub use crate::eval::{Dict, Value, ValueDict}; pub use crate::eval::{Dict, Value, ValueDict};
pub use crate::layout::primitive::*; pub use crate::layout::primitive::*;
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::layout::{layout_tree, Command, Commands, LayoutContext}; pub use crate::layout::{layout_tree, Command, LayoutContext};
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::syntax::{Span, Spanned, SynTree}; pub use crate::syntax::{Span, Spanned, SynTree};
pub use crate::{Feedback, Pass}; pub use crate::{Feedback, Pass};

View File

@ -4,15 +4,17 @@
//! font for each individual character. When the direction is right-to-left, the //! font for each individual character. When the direction is right-to-left, the
//! word is spelled backwards. Vertical shaping is not supported. //! word is spelled backwards. Vertical shaping is not supported.
use std::fmt::{self, Debug, Formatter};
use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant}; use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant};
use ttf_parser::GlyphId; use ttf_parser::GlyphId;
use crate::eval::TextState; use crate::eval::TextState;
use crate::font::FontLoader; use crate::font::FontLoader;
use crate::geom::{Point, Size}; use crate::geom::{Point, Size};
use crate::layout::{BoxLayout, Dir, LayoutAlign, LayoutElement, LayoutElements, Shaped}; use crate::layout::{BoxLayout, Dir, LayoutAlign, LayoutElement};
/// Shape text into a box. /// Shape text into a box containing shaped runs.
pub async fn shape( pub async fn shape(
text: &str, text: &str,
dir: Dir, dir: Dir,
@ -23,6 +25,51 @@ pub async fn shape(
Shaper::new(text, dir, align, state, loader).shape().await Shaper::new(text, dir, align, state, loader).shape().await
} }
/// A shaped run of text.
#[derive(Clone, PartialEq)]
pub struct Shaped {
/// The shaped text.
pub text: String,
/// The font face the text was shaped with.
pub face: FaceId,
/// The shaped glyphs.
pub glyphs: Vec<GlyphId>,
/// The horizontal offsets of the glyphs. This is indexed parallel to `glyphs`.
/// Vertical offets are not yet supported.
pub offsets: Vec<f64>,
/// The font size.
pub size: f64,
}
impl Shaped {
/// Create a new shape run with empty `text`, `glyphs` and `offsets`.
pub fn new(face: FaceId, size: f64) -> Self {
Self {
text: String::new(),
face,
glyphs: vec![],
offsets: vec![],
size,
}
}
/// Encode the glyph ids into a big-endian byte buffer.
pub fn encode_glyphs_be(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(2 * self.glyphs.len());
for &GlyphId(g) in &self.glyphs {
bytes.push((g >> 8) as u8);
bytes.push((g & 0xff) as u8);
}
bytes
}
}
impl Debug for Shaped {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Shaped({})", self.text)
}
}
/// Performs super-basic text shaping. /// Performs super-basic text shaping.
struct Shaper<'a> { struct Shaper<'a> {
text: &'a str, text: &'a str,
@ -64,11 +111,7 @@ impl<'a> Shaper<'a> {
fallback: &state.fallback, fallback: &state.fallback,
loader, loader,
shaped: Shaped::new(FaceId::MAX, state.font_size()), shaped: Shaped::new(FaceId::MAX, state.font_size()),
layout: BoxLayout { layout: BoxLayout::new(Size::new(0.0, state.font_size()), align),
size: Size::new(0.0, state.font_size()),
align,
elements: LayoutElements::new(),
},
offset: 0.0, offset: 0.0,
} }
} }
@ -88,7 +131,7 @@ impl<'a> Shaper<'a> {
// Flush the last buffered parts of the word. // Flush the last buffered parts of the word.
if !self.shaped.text.is_empty() { if !self.shaped.text.is_empty() {
let pos = Point::new(self.offset, 0.0); let pos = Point::new(self.offset, 0.0);
self.layout.elements.push(pos, LayoutElement::Text(self.shaped)); self.layout.push(pos, LayoutElement::Text(self.shaped));
} }
self.layout self.layout
@ -111,7 +154,7 @@ impl<'a> Shaper<'a> {
); );
let pos = Point::new(self.offset, 0.0); let pos = Point::new(self.offset, 0.0);
self.layout.elements.push(pos, LayoutElement::Text(shaped)); self.layout.push(pos, LayoutElement::Text(shaped));
self.offset = self.layout.size.width; self.offset = self.layout.size.width;
} }

View File

@ -15,8 +15,9 @@ use typstc::eval::State;
use typstc::export::pdf; use typstc::export::pdf;
use typstc::font::{FontLoader, SharedFontLoader}; use typstc::font::{FontLoader, SharedFontLoader};
use typstc::geom::{Point, Vec2}; use typstc::geom::{Point, Vec2};
use typstc::layout::{LayoutElement, MultiLayout, Shaped}; use typstc::layout::{BoxLayout, LayoutElement};
use typstc::parse::LineMap; use typstc::parse::LineMap;
use typstc::shaping::Shaped;
use typstc::{typeset, Feedback, Pass}; use typstc::{typeset, Feedback, Pass};
const TEST_DIR: &str = "tests"; const TEST_DIR: &str = "tests";
@ -136,7 +137,7 @@ impl TestFilter {
} }
} }
fn render(layouts: &MultiLayout, loader: &FontLoader, scale: f64) -> DrawTarget { fn render(layouts: &[BoxLayout], loader: &FontLoader, scale: f64) -> DrawTarget {
let pad = scale * 10.0; let pad = scale * 10.0;
let width = 2.0 * pad let width = 2.0 * pad
+ layouts + layouts
@ -167,7 +168,7 @@ fn render(layouts: &MultiLayout, loader: &FontLoader, scale: f64) -> DrawTarget
&Default::default(), &Default::default(),
); );
for &(pos, ref element) in &layout.elements.0 { for (pos, element) in &layout.elements {
match element { match element {
LayoutElement::Text(shaped) => render_shaped( LayoutElement::Text(shaped) => render_shaped(
&mut surface, &mut surface,