mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Remove unncessary wrappers and typedefs 🛑
This commit is contained in:
parent
105f70867d
commit
c1dd872b34
@ -9,7 +9,7 @@ use fontdock::{FontStretch, FontStyle, FontWeight};
|
||||
use super::dict::{Dict, SpannedEntry};
|
||||
use crate::color::RgbaColor;
|
||||
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::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree};
|
||||
use crate::{DynFuture, Feedback};
|
||||
@ -49,7 +49,7 @@ pub enum Value {
|
||||
/// An executable function.
|
||||
Func(ValueFunc),
|
||||
/// Layouting commands.
|
||||
Commands(Commands),
|
||||
Commands(Vec<Command>),
|
||||
/// The result of invalid operations.
|
||||
Error,
|
||||
}
|
||||
@ -83,7 +83,7 @@ impl Spanned<Value> {
|
||||
///
|
||||
/// If this is already a command-value, it is simply unwrapped, otherwise
|
||||
/// 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 {
|
||||
// Pass-through.
|
||||
Value::Commands(commands) => commands,
|
||||
|
@ -138,7 +138,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
||||
let mut face = FaceId::MAX;
|
||||
let mut size = 0.0;
|
||||
|
||||
for (pos, element) in &page.elements.0 {
|
||||
for (pos, element) in &page.elements {
|
||||
match element {
|
||||
LayoutElement::Text(shaped) => {
|
||||
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
|
||||
// each text element to find out which face is uses.
|
||||
for layout in layouts {
|
||||
for (_, element) in &layout.elements.0 {
|
||||
for (_, element) in &layout.elements {
|
||||
let LayoutElement::Text(shaped) = element;
|
||||
to_pdf.entry(shaped.face).or_insert_with(|| {
|
||||
let next_id = to_layout.len();
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ pub struct LineLayouter {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LineContext {
|
||||
/// The spaces to layout into.
|
||||
pub spaces: LayoutSpaces,
|
||||
pub spaces: Vec<LayoutSpace>,
|
||||
/// The initial layouting system, which can be updated through `set_sys`.
|
||||
pub sys: LayoutSystem,
|
||||
/// The alignment of the _resulting_ layout. This does not effect the line
|
||||
@ -132,15 +132,6 @@ impl LineLayouter {
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
/// no boxes laid out into it yet. Otherwise, the followup spaces are
|
||||
/// 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());
|
||||
}
|
||||
|
||||
@ -212,7 +203,7 @@ impl LineLayouter {
|
||||
|
||||
/// The remaining inner spaces. If something is laid out into these spaces,
|
||||
/// 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();
|
||||
*spaces[0].size.secondary_mut(self.ctx.sys) -= self.run.size.height;
|
||||
spaces
|
||||
@ -224,7 +215,7 @@ impl LineLayouter {
|
||||
}
|
||||
|
||||
/// 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.stack.finish()
|
||||
}
|
||||
@ -239,27 +230,25 @@ impl LineLayouter {
|
||||
|
||||
/// Finish the active line and start a new one.
|
||||
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);
|
||||
for (offset, layout) in layouts {
|
||||
for (offset, child) in layouts {
|
||||
let x = match self.ctx.sys.primary.is_positive() {
|
||||
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);
|
||||
elements.push_elements(pos, layout.elements);
|
||||
layout.push_layout(pos, child);
|
||||
}
|
||||
|
||||
self.stack.add(BoxLayout {
|
||||
size: self.run.size.specialized(self.ctx.sys),
|
||||
align: self.run.align.unwrap_or(LayoutAlign::START),
|
||||
elements,
|
||||
});
|
||||
self.stack.add(layout);
|
||||
|
||||
self.run = LineRun::new();
|
||||
|
||||
self.stack.add_spacing(self.ctx.line_spacing, SpacingKind::LINE);
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
//! Layouting of syntax trees into box layouts.
|
||||
//! Layouting of syntax trees.
|
||||
|
||||
pub mod primitive;
|
||||
|
||||
mod elements;
|
||||
mod line;
|
||||
mod stack;
|
||||
mod tree;
|
||||
|
||||
pub use elements::*;
|
||||
pub use line::*;
|
||||
pub use primitive::*;
|
||||
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::font::SharedFontLoader;
|
||||
use crate::shaping::Shaped;
|
||||
use crate::syntax::SynTree;
|
||||
use crate::{Feedback, Pass};
|
||||
|
||||
@ -25,7 +24,7 @@ pub async fn layout(
|
||||
tree: &SynTree,
|
||||
state: State,
|
||||
loader: SharedFontLoader,
|
||||
) -> Pass<MultiLayout> {
|
||||
) -> Pass<Vec<BoxLayout>> {
|
||||
let space = LayoutSpace {
|
||||
size: state.page.size,
|
||||
insets: state.page.insets(),
|
||||
@ -51,9 +50,6 @@ pub async fn layout(
|
||||
Pass::new(layouts, ctx.f)
|
||||
}
|
||||
|
||||
/// A collection of layouts.
|
||||
pub type MultiLayout = Vec<BoxLayout>;
|
||||
|
||||
/// A finished box with content at fixed positions.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BoxLayout {
|
||||
@ -62,7 +58,34 @@ pub struct BoxLayout {
|
||||
/// How to align this box in a parent container.
|
||||
pub align: LayoutAlign,
|
||||
/// 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.
|
||||
@ -86,15 +109,12 @@ pub struct LayoutConstraints {
|
||||
/// The unpadded size of this container (the base 100% for relative sizes).
|
||||
pub base: Size,
|
||||
/// 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
|
||||
/// when the last space is used up.
|
||||
pub repeat: bool,
|
||||
}
|
||||
|
||||
/// A collection of layout spaces.
|
||||
pub type LayoutSpaces = Vec<LayoutSpace>;
|
||||
|
||||
/// The space into which content is laid out.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
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.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Command {
|
||||
@ -146,10 +163,6 @@ pub enum Command {
|
||||
|
||||
/// Add a finished layout.
|
||||
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
|
||||
/// kind defines how the spacing interacts with surrounding spacing.
|
||||
AddSpacing(f64, SpacingKind, GenAxis),
|
||||
|
@ -17,12 +17,10 @@ impl Default for LayoutSystem {
|
||||
/// Specifies where to align a layout in a parent container.
|
||||
pub type LayoutAlign = Gen2<GenAlign>;
|
||||
|
||||
impl LayoutAlign {
|
||||
/// The layout alignment that has both components set to `Start`.
|
||||
pub const START: Self = Self {
|
||||
primary: GenAlign::Start,
|
||||
secondary: GenAlign::Start,
|
||||
};
|
||||
impl Default for LayoutAlign {
|
||||
fn default() -> Self {
|
||||
Self::new(GenAlign::Start, GenAlign::Start)
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to expand a layout to an area's full size or shrink it to fit its content.
|
||||
|
@ -24,7 +24,7 @@ use super::*;
|
||||
/// Performs the stack layouting.
|
||||
pub struct StackLayouter {
|
||||
ctx: StackContext,
|
||||
layouts: MultiLayout,
|
||||
layouts: Vec<BoxLayout>,
|
||||
/// The in-progress space.
|
||||
space: Space,
|
||||
}
|
||||
@ -33,7 +33,7 @@ pub struct StackLayouter {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StackContext {
|
||||
/// The spaces to layout into.
|
||||
pub spaces: LayoutSpaces,
|
||||
pub spaces: Vec<LayoutSpace>,
|
||||
/// The initial layouting system, which can be updated through `set_sys`.
|
||||
pub sys: LayoutSystem,
|
||||
/// The alignment of the _resulting_ layout. This does not effect the line
|
||||
@ -75,7 +75,7 @@ impl StackLayouter {
|
||||
let space = ctx.spaces[0];
|
||||
Self {
|
||||
ctx,
|
||||
layouts: MultiLayout::new(),
|
||||
layouts: vec![],
|
||||
space: Space::new(0, true, space.usable()),
|
||||
}
|
||||
}
|
||||
@ -110,15 +110,6 @@ impl StackLayouter {
|
||||
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.
|
||||
pub fn add_spacing(&mut self, mut spacing: f64, kind: SpacingKind) {
|
||||
match kind {
|
||||
@ -129,11 +120,13 @@ impl StackLayouter {
|
||||
let size = Size::new(0.0, spacing);
|
||||
|
||||
self.update_metrics(size);
|
||||
self.space.layouts.push((self.ctx.sys, BoxLayout {
|
||||
size: size.specialized(self.ctx.sys),
|
||||
align: LayoutAlign::START,
|
||||
elements: LayoutElements::new(),
|
||||
}));
|
||||
self.space.layouts.push((
|
||||
self.ctx.sys,
|
||||
BoxLayout::new(
|
||||
size.specialized(self.ctx.sys),
|
||||
LayoutAlign::default(),
|
||||
),
|
||||
));
|
||||
|
||||
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
|
||||
/// no boxes laid out into it yet. Otherwise, the followup spaces are
|
||||
/// 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() {
|
||||
self.ctx.spaces = spaces;
|
||||
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,
|
||||
/// it will fit into this stack.
|
||||
pub fn remaining(&self) -> LayoutSpaces {
|
||||
pub fn remaining(&self) -> Vec<LayoutSpace> {
|
||||
let size = self.usable();
|
||||
|
||||
let mut spaces = vec![LayoutSpace {
|
||||
@ -267,7 +260,7 @@ impl StackLayouter {
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
self.finish_space(false);
|
||||
}
|
||||
@ -360,12 +353,12 @@ impl StackLayouter {
|
||||
// Step 4: Align each layout in its bounding box and collect everything
|
||||
// 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);
|
||||
for ((sys, layout), bound) in layouts.into_iter().zip(bounds) {
|
||||
let size = layout.size.specialized(sys);
|
||||
let align = layout.align;
|
||||
for ((sys, child), bound) in layouts.into_iter().zip(bounds) {
|
||||
let size = child.size.specialized(sys);
|
||||
let align = child.align;
|
||||
|
||||
// The space in which this layout is aligned is given by the
|
||||
// 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 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.
|
||||
|
@ -8,7 +8,7 @@ use crate::syntax::{
|
||||
use crate::DynFuture;
|
||||
|
||||
/// 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);
|
||||
layouter.layout_tree(tree).await;
|
||||
layouter.finish()
|
||||
@ -38,7 +38,7 @@ impl<'a> TreeLayouter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(self) -> MultiLayout {
|
||||
fn finish(self) -> Vec<BoxLayout> {
|
||||
self.layouter.finish()
|
||||
}
|
||||
|
||||
@ -175,7 +175,6 @@ impl<'a> TreeLayouter<'a> {
|
||||
LayoutSyntaxTree(tree) => self.layout_tree(&tree).await,
|
||||
|
||||
Add(layout) => self.layouter.add(layout),
|
||||
AddMultiple(layouts) => self.layouter.add_multiple(layouts),
|
||||
AddSpacing(space, kind, axis) => match axis {
|
||||
GenAxis::Primary => self.layouter.add_primary_spacing(space, kind),
|
||||
GenAxis::Secondary => self.layouter.add_secondary_spacing(space, kind),
|
||||
|
13
src/lib.rs
13
src/lib.rs
@ -43,9 +43,9 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::eval::{State, Value};
|
||||
use crate::eval::State;
|
||||
use crate::font::SharedFontLoader;
|
||||
use crate::layout::{Commands, MultiLayout};
|
||||
use crate::layout::BoxLayout;
|
||||
use crate::syntax::{Decoration, Offset, Pos, SpanVec};
|
||||
|
||||
/// Process source code directly into a collection of layouts.
|
||||
@ -53,7 +53,7 @@ pub async fn typeset(
|
||||
src: &str,
|
||||
state: State,
|
||||
loader: SharedFontLoader,
|
||||
) -> Pass<MultiLayout> {
|
||||
) -> Pass<Vec<BoxLayout>> {
|
||||
let parsed = parse::parse(src);
|
||||
let layouted = layout::layout(&parsed.output, state, loader).await;
|
||||
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.
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub struct Feedback {
|
||||
|
@ -4,7 +4,7 @@
|
||||
pub use crate::eval::{Dict, Value, ValueDict};
|
||||
pub use crate::layout::primitive::*;
|
||||
#[doc(no_inline)]
|
||||
pub use crate::layout::{layout_tree, Command, Commands, LayoutContext};
|
||||
pub use crate::layout::{layout_tree, Command, LayoutContext};
|
||||
#[doc(no_inline)]
|
||||
pub use crate::syntax::{Span, Spanned, SynTree};
|
||||
pub use crate::{Feedback, Pass};
|
||||
|
@ -4,15 +4,17 @@
|
||||
//! font for each individual character. When the direction is right-to-left, the
|
||||
//! word is spelled backwards. Vertical shaping is not supported.
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant};
|
||||
use ttf_parser::GlyphId;
|
||||
|
||||
use crate::eval::TextState;
|
||||
use crate::font::FontLoader;
|
||||
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(
|
||||
text: &str,
|
||||
dir: Dir,
|
||||
@ -23,6 +25,51 @@ pub async fn shape(
|
||||
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.
|
||||
struct Shaper<'a> {
|
||||
text: &'a str,
|
||||
@ -64,11 +111,7 @@ impl<'a> Shaper<'a> {
|
||||
fallback: &state.fallback,
|
||||
loader,
|
||||
shaped: Shaped::new(FaceId::MAX, state.font_size()),
|
||||
layout: BoxLayout {
|
||||
size: Size::new(0.0, state.font_size()),
|
||||
align,
|
||||
elements: LayoutElements::new(),
|
||||
},
|
||||
layout: BoxLayout::new(Size::new(0.0, state.font_size()), align),
|
||||
offset: 0.0,
|
||||
}
|
||||
}
|
||||
@ -88,7 +131,7 @@ impl<'a> Shaper<'a> {
|
||||
// Flush the last buffered parts of the word.
|
||||
if !self.shaped.text.is_empty() {
|
||||
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
|
||||
@ -111,7 +154,7 @@ impl<'a> Shaper<'a> {
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,9 @@ use typstc::eval::State;
|
||||
use typstc::export::pdf;
|
||||
use typstc::font::{FontLoader, SharedFontLoader};
|
||||
use typstc::geom::{Point, Vec2};
|
||||
use typstc::layout::{LayoutElement, MultiLayout, Shaped};
|
||||
use typstc::layout::{BoxLayout, LayoutElement};
|
||||
use typstc::parse::LineMap;
|
||||
use typstc::shaping::Shaped;
|
||||
use typstc::{typeset, Feedback, Pass};
|
||||
|
||||
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 width = 2.0 * pad
|
||||
+ layouts
|
||||
@ -167,7 +168,7 @@ fn render(layouts: &MultiLayout, loader: &FontLoader, scale: f64) -> DrawTarget
|
||||
&Default::default(),
|
||||
);
|
||||
|
||||
for &(pos, ref element) in &layout.elements.0 {
|
||||
for (pos, element) in &layout.elements {
|
||||
match element {
|
||||
LayoutElement::Text(shaped) => render_shaped(
|
||||
&mut surface,
|
||||
|
Loading…
x
Reference in New Issue
Block a user