Run rustfmt 🚿

This commit is contained in:
Laurenz 2019-10-13 13:10:21 +02:00
parent 5c04185892
commit 7c0899b537
18 changed files with 435 additions and 282 deletions

View File

@ -1,14 +1,13 @@
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::{Read, BufWriter};
use std::io::{BufWriter, Read};
use std::path::{Path, PathBuf};
use std::process;
use typst::Typesetter;
use typst::export::pdf::PdfExporter;
use typst::toddle::query::FileSystemFontProvider;
use typst::Typesetter;
fn main() {
if let Err(err) = run() {
@ -26,11 +25,16 @@ fn run() -> Result<(), Box<dyn Error>> {
let source_path = Path::new(&args[1]);
// Compute the output filename from the input filename by replacing the extension.
// Compute the output filename from the input filename by replacing the
// extension.
let dest_path = if args.len() <= 2 {
let stem = source_path.file_stem().ok_or_else(|| "missing destation file name")?;
let stem = source_path
.file_stem()
.ok_or_else(|| "missing destation file name")?;
let base = source_path.parent().ok_or_else(|| "missing destation folder")?;
let base = source_path
.parent()
.ok_or_else(|| "missing destation folder")?;
base.join(format!("{}.pdf", stem.to_string_lossy()))
} else {
@ -43,7 +47,9 @@ fn run() -> Result<(), Box<dyn Error>> {
let mut src = String::new();
let mut source_file = File::open(source_path).map_err(|_| "failed to open source file")?;
source_file.read_to_string(&mut src).map_err(|_| "failed to read from source file")?;
source_file
.read_to_string(&mut src)
.map_err(|_| "failed to read from source file")?;
// Create a typesetter with a font provider that provides the default fonts.
let mut typesetter = Typesetter::new();

View File

@ -3,21 +3,22 @@
use std::collections::{HashMap, HashSet};
use std::io::{self, Write};
use tide::{PdfWriter, Ref, Rect, Version, Trailer};
use tide::content::Content;
use tide::doc::{Catalog, PageTree, Page, Resource, Text};
use tide::font::{Type0Font, CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags};
use tide::font::{GlyphUnit, CMap, CMapEncoding, WidthRecord, FontStream};
use tide::doc::{Catalog, Page, PageTree, Resource, Text};
use tide::font::{CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags, Type0Font};
use tide::font::{CMap, CMapEncoding, FontStream, GlyphUnit, WidthRecord};
use tide::{PdfWriter, Rect, Ref, Trailer, Version};
use toddle::tables::{Header, Post, OS2, HorizontalMetrics, CharMap, Name, NameEntry, MacStyleFlags};
use toddle::font::OwnedFont;
use toddle::query::SharedFontLoader;
use toddle::tables::{
CharMap, Header, HorizontalMetrics, MacStyleFlags, Name, NameEntry, Post, OS2,
};
use toddle::Error as FontError;
use crate::layout::{MultiLayout, Layout, LayoutAction};
use crate::layout::{Layout, LayoutAction, MultiLayout};
use crate::size::{Size, Size2D};
/// Exports layouts into _PDFs_.
#[derive(Debug)]
pub struct PdfExporter {}
@ -29,10 +30,16 @@ impl PdfExporter {
PdfExporter {}
}
/// Export a finished layouts into a writer. Returns how many bytes were written.
/// Export a finished layouts into a writer. Returns how many bytes were
/// written.
#[inline]
pub fn export<W: Write>(&self, layout: &MultiLayout, loader: &SharedFontLoader, target: W)
-> PdfResult<usize> {
pub fn export<W: Write>(
&self,
layout: &MultiLayout,
loader: &SharedFontLoader,
target: W,
) -> PdfResult<usize>
{
let mut engine = PdfEngine::new(layout, loader, target)?;
engine.write()
}
@ -59,8 +66,12 @@ struct Offsets {
impl<'d, W: Write> PdfEngine<'d, W> {
/// Create a new _PDF_ engine.
fn new(layout: &'d MultiLayout, loader: &SharedFontLoader, target: W)
-> PdfResult<PdfEngine<'d, W>> {
fn new(
layout: &'d MultiLayout,
loader: &SharedFontLoader,
target: W,
) -> PdfResult<PdfEngine<'d, W>>
{
// Create a subsetted PDF font for each font in the layout.
let mut font_remap = HashMap::new();
let fonts = {
@ -71,24 +82,26 @@ impl<'d, W: Write> PdfEngine<'d, W> {
for boxed in &layout.layouts {
for action in &boxed.actions {
match action {
LayoutAction::WriteText(string) => {
chars.entry(font)
.or_insert_with(HashSet::new)
.extend(string.chars())
},
LayoutAction::WriteText(string) => chars
.entry(font)
.or_insert_with(HashSet::new)
.extend(string.chars()),
LayoutAction::SetFont(id, _) => {
font = *id;
let new_id = font_remap.len();
font_remap.entry(font).or_insert(new_id);
},
_ => {},
}
_ => {}
}
}
}
// Collect the fonts into a vector in the order of the values in the remapping.
let mut loader = loader.borrow_mut();
let mut order = font_remap.iter().map(|(&old, &new)| (old, new)).collect::<Vec<_>>();
let mut order = font_remap
.iter()
.map(|(&old, &new)| (old, new))
.collect::<Vec<_>>();
order.sort_by_key(|&(_, new)| new);
let mut fonts = vec![];
@ -96,8 +109,10 @@ impl<'d, W: Write> PdfEngine<'d, W> {
let font = loader.get_with_index(index);
let subsetted = font.subsetted(
chars[&index].iter().cloned(),
&["name", "OS/2", "post", "head", "hhea", "hmtx", "maxp",
"cmap", "cvt ", "fpgm", "prep", "loca", "glyf"][..]
&[
"name", "OS/2", "post", "head", "hhea", "hmtx", "maxp", "cmap", "cvt ",
"fpgm", "prep", "loca", "glyf",
][..],
)?;
fonts.push(OwnedFont::from_bytes(subsetted)?);
}
@ -111,7 +126,13 @@ impl<'d, W: Write> PdfEngine<'d, W> {
let pages = (page_tree + 1, page_tree + layout.layouts.len() as Ref);
let contents = (pages.1 + 1, pages.1 + layout.layouts.len() as Ref);
let font_offsets = (contents.1 + 1, contents.1 + 5 * fonts.len() as Ref);
let offsets = Offsets { catalog, page_tree, pages, contents, fonts: font_offsets };
let offsets = Offsets {
catalog,
page_tree,
pages,
contents,
fonts: font_offsets,
};
Ok(PdfEngine {
writer: PdfWriter::new(target),
@ -129,32 +150,43 @@ impl<'d, W: Write> PdfEngine<'d, W> {
self.write_pages()?;
self.write_fonts()?;
self.writer.write_xref_table()?;
self.writer.write_trailer(Trailer::new(self.offsets.catalog))?;
self.writer
.write_trailer(Trailer::new(self.offsets.catalog))?;
Ok(self.writer.written())
}
/// Write the document catalog and page tree.
fn write_page_tree(&mut self) -> PdfResult<()> {
// The document catalog
self.writer.write_obj(self.offsets.catalog, &Catalog::new(self.offsets.page_tree))?;
self.writer
.write_obj(self.offsets.catalog, &Catalog::new(self.offsets.page_tree))?;
// The font resources
let offset = self.offsets.fonts.0;
let fonts = (0 .. self.fonts.len())
.map(|i| Resource::Font((i + 1) as u32, offset + 5 * i as u32));
let fonts =
(0..self.fonts.len()).map(|i| Resource::Font((i + 1) as u32, offset + 5 * i as u32));
// The root page tree
self.writer.write_obj(self.offsets.page_tree, PageTree::new()
.kids(ids(self.offsets.pages))
.resources(fonts)
self.writer.write_obj(
self.offsets.page_tree,
PageTree::new()
.kids(ids(self.offsets.pages))
.resources(fonts),
)?;
// The page objects
for (id, page) in ids(self.offsets.pages).zip(&self.layout.layouts) {
let rect = Rect::new(0.0, 0.0, page.dimensions.x.to_pt(), page.dimensions.y.to_pt());
self.writer.write_obj(id, Page::new(self.offsets.page_tree)
.media_box(rect)
.contents(ids(self.offsets.contents))
let rect = Rect::new(
0.0,
0.0,
page.dimensions.x.to_pt(),
page.dimensions.y.to_pt(),
);
self.writer.write_obj(
id,
Page::new(self.offsets.page_tree)
.media_box(rect)
.contents(ids(self.offsets.contents)),
)?;
}
@ -202,8 +234,8 @@ impl<'d, W: Write> PdfEngine<'d, W> {
// Write the text.
text.tj(self.fonts[active_font.0].encode_text(&string)?);
},
LayoutAction::DebugBox(_, _) => {},
}
LayoutAction::DebugBox(_, _) => {}
}
}
@ -217,18 +249,21 @@ impl<'d, W: Write> PdfEngine<'d, W> {
let mut id = self.offsets.fonts.0;
for font in &mut self.fonts {
let name = font.read_table::<Name>()?
let name = font
.read_table::<Name>()?
.get_decoded(NameEntry::PostScriptName)
.unwrap_or_else(|| "unknown".to_string());
let base_font = format!("ABCDEF+{}", name);
// Write the base font object referencing the CID font.
self.writer.write_obj(id,
self.writer.write_obj(
id,
Type0Font::new(
base_font.clone(),
CMapEncoding::Predefined("Identity-H".to_owned()),
id + 1
).to_unicode(id + 3)
id + 1,
)
.to_unicode(id + 3),
)?;
// Extract information from the head table.
@ -252,19 +287,22 @@ impl<'d, W: Write> PdfEngine<'d, W> {
// Transform the width into PDF units.
let widths: Vec<_> = font
.read_table::<HorizontalMetrics>()?
.metrics.iter()
.metrics
.iter()
.map(|m| font_unit_to_glyph_unit(m.advance_width as f32))
.collect();
// Write the CID font referencing the font descriptor.
let system_info = CIDSystemInfo::new("Adobe", "Identity", 0);
self.writer.write_obj(id + 1,
self.writer.write_obj(
id + 1,
CIDFont::new(
CIDFontType::Type2,
base_font.clone(),
system_info.clone(),
id + 2,
).widths(vec![WidthRecord::start(0, widths)])
)
.widths(vec![WidthRecord::start(0, widths)]),
)?;
// Extract information from the post table.
@ -284,24 +322,31 @@ impl<'d, W: Write> PdfEngine<'d, W> {
let os2 = font.read_table::<OS2>()?;
// Write the font descriptor (contains the global information about the font).
self.writer.write_obj(id + 2,
self.writer.write_obj(
id + 2,
FontDescriptor::new(base_font, flags, italic_angle)
.font_bbox(bounding_box)
.ascent(font_unit_to_glyph_unit(os2.s_typo_ascender as f32))
.descent(font_unit_to_glyph_unit(os2.s_typo_descender as f32))
.cap_height(font_unit_to_glyph_unit(os2.s_cap_height.unwrap_or(os2.s_typo_ascender) as f32))
.cap_height(font_unit_to_glyph_unit(
os2.s_cap_height.unwrap_or(os2.s_typo_ascender) as f32,
))
.stem_v((10.0 + 0.244 * (os2.us_weight_class as f32 - 50.0)) as GlyphUnit)
.font_file_2(id + 4)
.font_file_2(id + 4),
)?;
// Write the CMap, which maps glyphs to unicode codepoints.
let mapping = font.read_table::<CharMap>()?
.mapping.iter()
let mapping = font
.read_table::<CharMap>()?
.mapping
.iter()
.map(|(&c, &cid)| (cid, c));
self.writer.write_obj(id + 3, &CMap::new("Custom", system_info, mapping))?;
self.writer
.write_obj(id + 3, &CMap::new("Custom", system_info, mapping))?;
// Finally write the subsetted font program.
self.writer.write_obj(id + 4, &FontStream::new(font.data().get_ref()))?;
self.writer
.write_obj(id + 4, &FontStream::new(font.data().get_ref()))?;
id += 5;
}
@ -311,8 +356,8 @@ impl<'d, W: Write> PdfEngine<'d, W> {
}
/// Create an iterator from a reference pair.
fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item=Ref> {
start ..= end
fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item = Ref> {
start..=end
}
/// The error type for _PDF_ creation.

View File

@ -6,27 +6,27 @@ use std::fmt::{self, Debug, Formatter};
use toddle::query::FontClass;
use crate::layout::{Layout, MultiLayout , LayoutContext, LayoutResult};
use crate::layout::{Layout, LayoutContext, LayoutResult, MultiLayout};
use crate::parsing::{ParseContext, ParseResult};
use crate::syntax::{SyntaxTree, FuncHeader};
use crate::syntax::{FuncHeader, SyntaxTree};
/// Typesetting function types.
///
/// These types have to be able to parse tokens into themselves and store the relevant information
/// from the parsing to do their role in typesetting later.
/// These types have to be able to parse tokens into themselves and store the
/// relevant information from the parsing to do their role in typesetting later.
///
/// The trait `FunctionBounds` is automatically implemented for types which can be used as
/// functions, that is they fulfill the bounds `Debug + PartialEq + 'static`.
/// The trait `FunctionBounds` is automatically implemented for types which can
/// be used as functions, that is they fulfill the bounds `Debug + PartialEq +
/// 'static`.
pub trait Function: FunctionBounds {
/// Parse the header and body into this function given a context.
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext)
-> ParseResult<Self> where Self: Sized;
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
where Self: Sized;
/// Layout this function given a context.
///
/// Returns optionally the resulting layout and a new context if changes to the context should
/// be made.
/// Returns optionally the resulting layout and a new context if changes to
/// the context should be made.
fn layout(&self, ctx: LayoutContext) -> LayoutResult<FuncCommands>;
}
@ -39,15 +39,13 @@ impl PartialEq for dyn Function {
/// A sequence of commands requested for execution by a function.
#[derive(Debug)]
pub struct FuncCommands<'a> {
pub commands: Vec<Command<'a>>
pub commands: Vec<Command<'a>>,
}
impl<'a> FuncCommands<'a> {
/// Create an empty command list.
pub fn new() -> FuncCommands<'a> {
FuncCommands {
commands: vec![],
}
FuncCommands { commands: vec![] }
}
/// Add a command to the sequence.
@ -79,10 +77,11 @@ pub enum Command<'a> {
ToggleStyleClass(FontClass),
}
/// A helper trait that describes requirements for types that can implement [`Function`].
/// A helper trait that describes requirements for types that can implement
/// [`Function`].
///
/// Automatically implemented for all types which fulfill to the bounds `Debug + PartialEq +
/// 'static`. There should be no need to implement this manually.
/// Automatically implemented for all types which fulfill to the bounds `Debug +
/// PartialEq + 'static`. There should be no need to implement this manually.
pub trait FunctionBounds: Debug {
/// Cast self into `Any`.
fn help_cast_as_any(&self) -> &dyn Any;
@ -91,7 +90,9 @@ pub trait FunctionBounds: Debug {
fn help_eq(&self, other: &dyn Function) -> bool;
}
impl<T> FunctionBounds for T where T: Debug + PartialEq + 'static {
impl<T> FunctionBounds for T
where T: Debug + PartialEq + 'static
{
fn help_cast_as_any(&self) -> &dyn Any {
self
}
@ -111,13 +112,14 @@ pub struct Scope {
}
/// A function which parses a function invocation into a function type.
type ParseFunc = dyn Fn(&FuncHeader, Option<&str>, ParseContext)
-> ParseResult<Box<dyn Function>>;
type ParseFunc = dyn Fn(&FuncHeader, Option<&str>, ParseContext) -> ParseResult<Box<dyn Function>>;
impl Scope {
/// Create a new empty scope.
pub fn new() -> Scope {
Scope { parsers: HashMap::new() }
Scope {
parsers: HashMap::new(),
}
}
/// Create a new scope with the standard functions contained.
@ -129,9 +131,7 @@ impl Scope {
pub fn add<F: Function + 'static>(&mut self, name: &str) {
self.parsers.insert(
name.to_owned(),
Box::new(|h, b, c| {
F::parse(h, b, c).map(|func| Box::new(func) as Box<dyn Function>)
})
Box::new(|h, b, c| F::parse(h, b, c).map(|func| Box::new(func) as Box<dyn Function>)),
);
}

View File

@ -3,11 +3,10 @@
use std::fmt::{self, Display, Formatter};
use std::io::{self, Write};
use crate::size::Size2D;
use super::Layout;
use crate::size::Size2D;
use LayoutAction::*;
/// A layouting action.
#[derive(Clone)]
pub enum LayoutAction {
@ -30,8 +29,14 @@ impl LayoutAction {
MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()),
SetFont(i, s) => write!(f, "f {} {}", i, s),
WriteText(s) => write!(f, "w {}", s),
DebugBox(p, s) => write!(f, "b {} {} {} {}",
p.x.to_pt(), p.y.to_pt(), s.x.to_pt(), s.y.to_pt())
DebugBox(p, s) => write!(
f,
"b {} {} {} {}",
p.x.to_pt(),
p.y.to_pt(),
s.x.to_pt(),
s.y.to_pt()
),
}
}
}
@ -81,7 +86,7 @@ impl LayoutActionList {
SetFont(index, size) if (index, size) != self.active_font => {
self.next_font = Some((index, size));
},
}
_ => {
if let Some(target) = self.next_pos.take() {
@ -92,19 +97,21 @@ impl LayoutActionList {
}
self.actions.push(action);
},
}
}
}
/// Add a series of actions.
pub fn extend<I>(&mut self, actions: I) where I: IntoIterator<Item=LayoutAction> {
pub fn extend<I>(&mut self, actions: I)
where I: IntoIterator<Item = LayoutAction> {
for action in actions.into_iter() {
self.add(action);
}
}
/// Add all actions from a box layout at a position. A move to the position
/// is generated and all moves inside the box layout are translated as necessary.
/// is generated and all moves inside the box layout are translated as
/// necessary.
pub fn add_box(&mut self, position: Size2D, layout: Layout) {
if let Some(target) = self.next_pos.take() {
self.actions.push(MoveAbsolute(target));
@ -114,7 +121,8 @@ impl LayoutActionList {
self.origin = position;
if layout.debug_render {
self.actions.push(LayoutAction::DebugBox(position, layout.dimensions));
self.actions
.push(LayoutAction::DebugBox(position, layout.dimensions));
}
self.extend(layout.actions);

View File

@ -1,6 +1,5 @@
use super::*;
/// Finishes a flex layout by justifying the positions of the individual boxes.
#[derive(Debug)]
pub struct FlexLayouter {
@ -32,7 +31,8 @@ enum FlexUnit {
/// A content unit to be arranged flexibly.
Boxed(Layout),
/// A unit which acts as glue between two [`FlexUnit::Boxed`] units and
/// is only present if there was no flow break in between the two surrounding boxes.
/// is only present if there was no flow break in between the two
/// surrounding boxes.
Glue(Layout),
}
@ -107,7 +107,9 @@ impl FlexLayouter {
/// Layout the box.
fn boxed(&mut self, boxed: Layout) -> LayoutResult<()> {
let last_glue_x = self.last_glue.as_ref()
let last_glue_x = self
.last_glue
.as_ref()
.map(|g| g.dimensions.x)
.unwrap_or(Size::zero());
@ -157,7 +159,7 @@ impl FlexLayouter {
// Right align everything by shifting it right by the
// amount of space left to the right of the line.
cursor + remaining_space
},
}
};
self.actions.add_box(position, layout);
@ -173,7 +175,8 @@ impl FlexLayouter {
self.dimensions.y += self.line_metrics.y;
// Reset the cursor the left and move down by the line and the inter-line spacing.
// Reset the cursor the left and move down by the line and the inter-line
// spacing.
self.cursor.x = self.ctx.space.padding.left;
self.cursor.y += self.line_metrics.y + self.ctx.flex_spacing;

View File

@ -4,24 +4,23 @@ use std::borrow::Cow;
use std::io::{self, Write};
use std::mem;
use toddle::query::{SharedFontLoader, FontClass};
use toddle::query::{FontClass, SharedFontLoader};
use toddle::Error as FontError;
use crate::func::Command;
use crate::size::{Size, Size2D, SizeBox};
use crate::syntax::{SyntaxTree, Node, FuncCall};
use crate::style::TextStyle;
use crate::syntax::{FuncCall, Node, SyntaxTree};
mod text;
mod stacked;
mod flex;
mod actions;
mod flex;
mod stacked;
mod text;
pub use actions::{LayoutAction, LayoutActionList};
pub use flex::{FlexContext, FlexLayouter};
pub use stacked::{StackContext, StackLayouter};
pub use text::{layout_text, TextContext};
pub use flex::{FlexLayouter, FlexContext};
pub use stacked::{StackLayouter, StackContext};
/// A box layout has a fixed width and height and composes of actions.
#[derive(Debug, Clone)]
@ -37,7 +36,12 @@ pub struct Layout {
impl Layout {
/// Serialize this layout into an output buffer.
pub fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
writeln!(f, "{:.4} {:.4}", self.dimensions.x.to_pt(), self.dimensions.y.to_pt())?;
writeln!(
f,
"{:.4} {:.4}",
self.dimensions.x.to_pt(),
self.dimensions.y.to_pt()
)?;
for action in &self.actions {
action.serialize(f)?;
writeln!(f)?;
@ -55,9 +59,7 @@ pub struct MultiLayout {
impl MultiLayout {
/// Create an empty multibox layout.
pub fn new() -> MultiLayout {
MultiLayout {
layouts: vec![],
}
MultiLayout { layouts: vec![] }
}
/// Extract a single sublayout and panic if this layout does not have
@ -158,7 +160,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
},
flex_spacing: (ctx.style.line_spacing - 1.0) * Size::pt(ctx.style.font_size),
}),
style: Cow::Borrowed(ctx.style)
style: Cow::Borrowed(ctx.style),
}
}
@ -175,7 +177,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
if !self.flex_layouter.is_empty() {
self.layout_text(" ", true)?;
}
},
}
// Finish the current flex layout and add it to the box layouter.
Node::Newline => {
@ -186,7 +188,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
let size = Size::pt(self.style.font_size)
* (self.style.line_spacing * self.style.paragraph_spacing - 1.0);
self.stack_layouter.add_space(size)?;
},
}
// Toggle the text styles.
Node::ToggleItalics => self.style.to_mut().toggle_class(FontClass::Italic),
@ -208,16 +210,19 @@ impl<'a, 'p> Layouter<'a, 'p> {
}
Ok(MultiLayout {
layouts: vec![self.stack_layouter.finish()]
layouts: vec![self.stack_layouter.finish()],
})
}
/// Layout a piece of text into a box.
fn layout_text(&mut self, text: &str, glue: bool) -> LayoutResult<()> {
let boxed = layout_text(text, TextContext {
loader: &self.ctx.loader,
style: &self.style,
})?;
let boxed = layout_text(
text,
TextContext {
loader: &self.ctx.loader,
style: &self.style,
},
)?;
if glue {
self.flex_layouter.add_glue(boxed);

View File

@ -1,6 +1,5 @@
use super::*;
/// Layouts boxes block-style.
#[derive(Debug)]
pub struct StackLayouter {
@ -29,10 +28,13 @@ impl StackLayouter {
Alignment::Right => Size2D::with_x(space.usable().x),
},
usable: space.usable(),
cursor: Size2D::new(match ctx.space.alignment {
Alignment::Left => space.padding.left,
Alignment::Right => space.dimensions.x - space.padding.right,
}, space.padding.top),
cursor: Size2D::new(
match ctx.space.alignment {
Alignment::Left => space.padding.left,
Alignment::Right => space.dimensions.x - space.padding.right,
},
space.padding.top,
),
}
}

View File

@ -1,9 +1,8 @@
use toddle::query::{FontQuery, SharedFontLoader};
use toddle::tables::{Header, CharMap, HorizontalMetrics};
use toddle::tables::{CharMap, Header, HorizontalMetrics};
use crate::size::{Size, Size2D};
use super::*;
use crate::size::{Size, Size2D};
/// The context for text layouting.
#[derive(Copy, Clone)]
@ -53,7 +52,8 @@ pub fn layout_text(text: &str, ctx: TextContext) -> LayoutResult<Layout> {
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x);
// Add the char width to the total box width.
let glyph = font.read_table::<CharMap>()?
let glyph = font
.read_table::<CharMap>()?
.get(character)
.expect("layout text: font should have char");
@ -61,7 +61,7 @@ pub fn layout_text(text: &str, ctx: TextContext) -> LayoutResult<Layout> {
font.read_table::<HorizontalMetrics>()?
.get(glyph)
.expect("layout text: font should have glyph")
.advance_width as f32
.advance_width as f32,
);
let char_width = glyph_width * ctx.style.font_size;

View File

@ -1,26 +1,28 @@
//! The compiler for the _Typst_ typesetting language.
//!
//! # Steps
//! - **Parsing:** The parsing step first transforms a plain string into an [iterator of
//! tokens](crate::parsing::Tokens). Then parser constructs a syntax tree from the token stream.
//! The structures describing the tree can be found in the [syntax]. Dynamic functions parse
//! their own bodies themselves.
//! - **Layouting:** The next step is to transform the syntax tree into a portable representation of
//! the typesetted document. Types for these can be found in the [layout] module.
//! - **Exporting:** The finished document can then be exported into supported formats. Submodules
//! for the supported formats are located in the [export] module. Currently the only supported
//! format is _PDF_.
//! - **Parsing:** The parsing step first transforms a plain string into an
//! [iterator of tokens](crate::parsing::Tokens). Then parser constructs a
//! syntax tree from the token stream. The structures describing the tree can
//! be found in the [syntax]. Dynamic functions parse their own bodies
//! themselves.
//! - **Layouting:** The next step is to transform the syntax tree into a
//! portable representation of the typesetted document. Types for these can be
//! found in the [layout] module.
//! - **Exporting:** The finished document can then be exported into supported
//! formats. Submodules for the supported formats are located in the [export]
//! module. Currently the only supported format is _PDF_.
pub extern crate toddle;
use std::cell::RefCell;
use toddle::query::{FontLoader, SharedFontLoader, FontProvider};
use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
use crate::func::Scope;
use crate::parsing::{parse, ParseContext, ParseResult, ParseError};
use crate::layout::{layout_tree, LayoutContext, MultiLayout};
use crate::layout::{LayoutSpace, Alignment, LayoutError, LayoutResult};
use crate::layout::{Alignment, LayoutError, LayoutResult, LayoutSpace};
use crate::parsing::{parse, ParseContext, ParseError, ParseResult};
use crate::style::{PageStyle, TextStyle};
use crate::syntax::SyntaxTree;
@ -29,12 +31,11 @@ mod macros;
pub mod export;
pub mod func;
pub mod layout;
pub mod library;
pub mod parsing;
pub mod size;
pub mod style;
pub mod syntax;
pub mod library;
/// Transforms source code into typesetted documents.
///
@ -73,7 +74,8 @@ impl<'p> Typesetter<'p> {
/// Add a font provider to the context of this typesetter.
#[inline]
pub fn add_font_provider<P: 'p>(&mut self, provider: P) where P: FontProvider {
pub fn add_font_provider<P: 'p>(&mut self, provider: P)
where P: FontProvider {
self.loader.get_mut().add_provider(provider);
}
@ -98,12 +100,15 @@ impl<'p> Typesetter<'p> {
shrink_to_fit: false,
};
let pages = layout_tree(&tree, LayoutContext {
loader: &self.loader,
style: &self.text_style,
space,
extra_space: Some(space),
})?;
let pages = layout_tree(
&tree,
LayoutContext {
loader: &self.loader,
style: &self.text_style,
space,
extra_space: Some(space),
},
)?;
Ok(pages)
}
@ -116,7 +121,6 @@ impl<'p> Typesetter<'p> {
}
}
/// The general error type for typesetting.
pub enum TypesetError {
/// An error that occured while parsing.

View File

@ -1,7 +1,6 @@
use super::prelude::*;
use crate::layout::Alignment;
/// Allows to align content in different ways.
#[derive(Debug, PartialEq)]
pub struct AlignFunc {
@ -10,9 +9,8 @@ pub struct AlignFunc {
}
impl Function for AlignFunc {
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext)
-> ParseResult<Self> where Self: Sized {
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
where Self: Sized {
if header.args.len() != 1 || !header.kwargs.is_empty() {
return err("expected exactly one positional argument specifying the alignment");
}
@ -24,7 +22,10 @@ impl Function for AlignFunc {
s => return err(format!("invalid alignment specifier: '{}'", s)),
}
} else {
return err(format!("expected alignment specifier, found: '{}'", header.args[0]));
return err(format!(
"expected alignment specifier, found: '{}'",
header.args[0]
));
};
let body = if let Some(body) = body {

View File

@ -7,11 +7,11 @@ mod styles;
/// Useful imports for creating your own functions.
pub mod prelude {
pub use crate::syntax::{SyntaxTree, FuncHeader, Expression};
pub use crate::parsing::{parse, ParseContext, ParseResult, ParseError};
pub use crate::layout::{layout_tree, LayoutContext, MultiLayout, Layout};
pub use crate::layout::{LayoutResult, LayoutError};
pub use crate::func::{Function, Command, FuncCommands};
pub use crate::func::{Command, FuncCommands, Function};
pub use crate::layout::{layout_tree, Layout, LayoutContext, MultiLayout};
pub use crate::layout::{LayoutError, LayoutResult};
pub use crate::parsing::{parse, ParseContext, ParseError, ParseResult};
pub use crate::syntax::{Expression, FuncHeader, SyntaxTree};
pub fn err<S: Into<String>, T>(message: S) -> ParseResult<T> {
Err(ParseError::new(message))
@ -19,8 +19,7 @@ pub mod prelude {
}
pub use align::AlignFunc;
pub use styles::{ItalicFunc, BoldFunc, MonospaceFunc};
pub use styles::{BoldFunc, ItalicFunc, MonospaceFunc};
/// Create a scope with all standard functions.
pub fn std() -> Scope {

View File

@ -2,7 +2,6 @@ use toddle::query::FontClass;
use super::prelude::*;
macro_rules! style_func {
(
$(#[$outer:meta])*

View File

@ -5,14 +5,13 @@ use std::collections::HashMap;
use unicode_xid::UnicodeXID;
use crate::func::{Function, Scope};
use crate::syntax::*;
use crate::size::Size;
use crate::syntax::*;
mod tokens;
pub use tokens::{tokenize, Tokens};
/// Parses source code into a syntax tree given a context.
#[inline]
pub fn parse(src: &str, ctx: ParseContext) -> ParseResult<SyntaxTree> {
@ -105,10 +104,7 @@ impl<'s> Parser<'s> {
let body = self.parse_func_body(&header)?;
// Finally this function is parsed to the end.
self.append(Node::Func(FuncCall {
header,
body,
}));
self.append(Node::Func(FuncCall { header, body }));
Ok(self.switch(ParserState::Body))
}
@ -124,7 +120,7 @@ impl<'s> Parser<'s> {
} else {
Err(ParseError::new(format!("invalid identifier: '{}'", word)))
}
},
}
_ => Err(ParseError::new("expected identifier")),
}?;
@ -138,13 +134,17 @@ impl<'s> Parser<'s> {
// Check for arguments
match self.tokens.next() {
Some(Token::RightBracket) => {},
Some(Token::RightBracket) => {}
Some(Token::Colon) => {
let (args, kwargs) = self.parse_func_args()?;
header.args = args;
header.kwargs = kwargs;
},
_ => return Err(ParseError::new("expected function arguments or closing bracket")),
}
_ => {
return Err(ParseError::new(
"expected function arguments or closing bracket",
))
}
}
// Store the header information of the function invocation.
@ -164,16 +164,16 @@ impl<'s> Parser<'s> {
Some(Token::Text(_)) | Some(Token::Quoted(_)) if !comma => {
args.push(self.parse_expression()?);
comma = true;
},
}
Some(Token::Comma) if comma => {
self.advance();
comma = false
},
}
Some(Token::RightBracket) => {
self.advance();
break
},
break;
}
_ if comma => return Err(ParseError::new("expected comma or closing bracket")),
_ => return Err(ParseError::new("expected closing bracket")),
@ -197,7 +197,7 @@ impl<'s> Parser<'s> {
} else {
Expression::Ident(text.to_owned())
}
},
}
_ => return Err(ParseError::new("expected expression")),
})
}
@ -211,19 +211,25 @@ impl<'s> Parser<'s> {
}
// Now we want to parse this function dynamically.
let parser = self.ctx.scope.get_parser(&header.name)
let parser = self
.ctx
.scope
.get_parser(&header.name)
.ok_or_else(|| ParseError::new(format!("unknown function: '{}'", &header.name)))?;
// Do the parsing dependent on whether the function has a body.
Ok(if has_body {
// Find out the string which makes the body of this function.
let (start, end) = self.tokens.current_index().and_then(|index| {
find_closing_bracket(&self.src[index..])
.map(|end| (index, index + end))
}).ok_or_else(|| ParseError::new("expected closing bracket"))?;
let (start, end) = self
.tokens
.current_index()
.and_then(|index| {
find_closing_bracket(&self.src[index..]).map(|end| (index, index + end))
})
.ok_or_else(|| ParseError::new("expected closing bracket"))?;
// Parse the body.
let body_string = &self.src[start .. end];
let body_string = &self.src[start..end];
let body = parser(&header, Some(body_string), self.ctx)?;
// Skip to the end of the function in the token stream.
@ -246,12 +252,12 @@ impl<'s> Parser<'s> {
Token::Newline => {
self.append_consumed(Node::Newline);
self.switch(ParserState::WroteNewline);
},
}
Token::Space => self.append_space_consumed(),
_ => {
self.append_space();
self.switch(ParserState::Body);
},
}
},
ParserState::WroteNewline => match token {
Token::Newline | Token::Space => self.append_space_consumed(),
@ -263,17 +269,17 @@ impl<'s> Parser<'s> {
Token::Newline => {
self.advance();
self.switch(ParserState::FirstNewline);
},
}
// Comments
Token::LineComment(_) | Token::BlockComment(_) => self.advance(),
Token::StarSlash => {
return Err(ParseError::new("unexpected end of block comment"));
},
}
// Anything else skips out of the function.
_ => break,
}
},
}
}
@ -284,8 +290,9 @@ impl<'s> Parser<'s> {
fn skip_white(&mut self) {
while let Some(token) = self.tokens.peek() {
match token {
Token::Space | Token::Newline
| Token::LineComment(_) | Token::BlockComment(_) => self.advance(),
Token::Space | Token::Newline | Token::LineComment(_) | Token::BlockComment(_) => {
self.advance()
}
_ => break,
}
}
@ -335,19 +342,19 @@ fn find_closing_bracket(src: &str) -> Option<usize> {
'\\' => {
escaped = !escaped;
continue;
},
}
']' if !escaped && parens == 0 => return Some(index),
'[' if !escaped => parens += 1,
']' if !escaped => parens -= 1,
_ => {},
_ => {}
}
escaped = false;
}
None
}
/// A peekable iterator for tokens which allows access to the original iterator inside this module
/// (which is needed by the parser).
/// A peekable iterator for tokens which allows access to the original iterator
/// inside this module (which is needed by the parser).
#[derive(Debug, Clone)]
struct PeekableTokens<'s> {
tokens: Tokens<'s>,
@ -411,7 +418,6 @@ fn is_identifier(string: &str) -> bool {
true
}
/// The error type for parsing.
pub struct ParseError(String);
@ -430,17 +436,16 @@ error_type! {
show: f => f.write_str(&err.0),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::func::{Function, FuncCommands, Scope};
use crate::func::{FuncCommands, Function, Scope};
use crate::layout::{LayoutContext, LayoutResult};
use Node::{Space as S, Newline as N, Func as F};
use funcs::*;
use Node::{Func as F, Newline as N, Space as S};
/// Two test functions, one which parses it's body as another syntax tree and another one which
/// does not expect a body.
/// Two test functions, one which parses it's body as another syntax tree
/// and another one which does not expect a body.
mod funcs {
use super::*;
@ -449,8 +454,8 @@ mod tests {
pub struct TreeFn(pub SyntaxTree);
impl Function for TreeFn {
fn parse(_: &FuncHeader, body: Option<&str>, ctx: ParseContext)
-> ParseResult<Self> where Self: Sized {
fn parse(_: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
where Self: Sized {
if let Some(src) = body {
parse(src, ctx).map(|tree| TreeFn(tree))
} else {
@ -468,8 +473,8 @@ mod tests {
pub struct BodylessFn;
impl Function for BodylessFn {
fn parse(_: &FuncHeader, body: Option<&str>, _: ParseContext)
-> ParseResult<Self> where Self: Sized {
fn parse(_: &FuncHeader, body: Option<&str>, _: ParseContext) -> ParseResult<Self>
where Self: Sized {
if body.is_none() {
Ok(BodylessFn)
} else {
@ -485,7 +490,9 @@ mod tests {
/// Test if the source code parses into the syntax tree.
fn test(src: &str, tree: SyntaxTree) {
let ctx = ParseContext { scope: &Scope::new() };
let ctx = ParseContext {
scope: &Scope::new(),
};
assert_eq!(parse(src, ctx).unwrap(), tree);
}
@ -497,7 +504,9 @@ mod tests {
/// Test if the source parses into the error.
fn test_err(src: &str, err: &str) {
let ctx = ParseContext { scope: &Scope::new() };
let ctx = ParseContext {
scope: &Scope::new(),
};
assert_eq!(parse(src, ctx).unwrap_err().to_string(), err);
}
@ -509,9 +518,12 @@ mod tests {
/// Create a text node.
#[allow(non_snake_case)]
fn T(s: &str) -> Node { Node::Text(s.to_owned()) }
fn T(s: &str) -> Node {
Node::Text(s.to_owned())
}
/// Shortcut macro to create a syntax tree. Is `vec`-like and the elements are the nodes.
/// Shortcut macro to create a syntax tree. Is `vec`-like and the elements
/// are the nodes.
macro_rules! tree {
($($x:expr),*) => (
SyntaxTree { nodes: vec![$($x),*] }

View File

@ -4,7 +4,6 @@ use smallvec::SmallVec;
use crate::syntax::*;
/// Builds an iterator over the tokens of the source code.
#[inline]
pub fn tokenize(src: &str) -> Tokens {
@ -15,7 +14,7 @@ pub fn tokenize(src: &str) -> Tokens {
#[derive(Debug, Clone)]
pub struct Tokens<'s> {
src: &'s str,
pub(in super) chars: PeekableChars<'s>,
pub(super) chars: PeekableChars<'s>,
state: TokensState,
stack: SmallVec<[TokensState; 1]>,
}
@ -56,7 +55,7 @@ impl<'s> Tokens<'s> {
/// Go back to the top-of-stack state.
fn unswitch(&mut self) {
self.state = self.stack.pop().unwrap_or(TokensState::Body);
self.state = self.stack.pop().unwrap_or(TokensState::Body);
}
/// Advance and return the given token.
@ -67,7 +66,7 @@ impl<'s> Tokens<'s> {
/// Returns a word containing the string bounded by the given indices.
fn text(&self, start: usize, end: usize) -> Token<'s> {
Token::Text(&self.src[start .. end])
Token::Text(&self.src[start..end])
}
}
@ -78,7 +77,8 @@ impl<'s> Iterator for Tokens<'s> {
fn next(&mut self) -> Option<Token<'s>> {
use TokensState as TU;
// Go to the body state if the function has a body or return to the top-of-stack state.
// Go to the body state if the function has a body or return to the top-of-stack
// state.
if self.state == TU::MaybeBody {
if self.chars.peek()?.1 == '[' {
self.state = TU::Body;
@ -97,7 +97,7 @@ impl<'s> Iterator for Tokens<'s> {
'[' => {
self.switch(TU::Function);
Token::LeftBracket
},
}
']' => {
if self.state == TU::Function {
self.state = TU::MaybeBody;
@ -105,7 +105,7 @@ impl<'s> Iterator for Tokens<'s> {
self.unswitch();
}
Token::RightBracket
},
}
// Line comment
'/' if afterwards == Some('/') => {
@ -121,8 +121,8 @@ impl<'s> Iterator for Tokens<'s> {
}
let end = end.0 + end.1.len_utf8();
Token::LineComment(&self.src[start .. end])
},
Token::LineComment(&self.src[start..end])
}
// Block comment
'/' if afterwards == Some('*') => {
@ -133,17 +133,26 @@ impl<'s> Iterator for Tokens<'s> {
while let Some((index, c)) = self.chars.next() {
let after = self.chars.peek().map(|p| p.1);
match (c, after) {
('*', Some('/')) if nested == 0 => { self.advance(); break },
('/', Some('*')) => { self.advance(); nested += 1 },
('*', Some('/')) => { self.advance(); nested -= 1 },
_ => {},
('*', Some('/')) if nested == 0 => {
self.advance();
break;
}
('/', Some('*')) => {
self.advance();
nested += 1
}
('*', Some('/')) => {
self.advance();
nested -= 1
}
_ => {}
}
end = (index, c);
}
let end = end.0 + end.1.len_utf8();
Token::BlockComment(&self.src[start .. end])
},
Token::BlockComment(&self.src[start..end])
}
// Unexpected end of block comment
'*' if afterwards == Some('/') => self.consumed(Token::StarSlash),
@ -189,7 +198,7 @@ impl<'s> Iterator for Tokens<'s> {
}
let end_pos = end.0 + end.1.len_utf8();
Token::Quoted(&self.src[next_pos + 1 .. end_pos])
Token::Quoted(&self.src[next_pos + 1..end_pos])
}
// Escaping
@ -207,7 +216,7 @@ impl<'s> Iterator for Tokens<'s> {
}
Token::Text("\\")
},
}
// Normal text
_ => {
@ -241,7 +250,7 @@ impl<'s> Iterator for Tokens<'s> {
let end_pos = end.0 + end.1.len_utf8();
self.text(next_pos, end_pos)
},
}
})
}
}
@ -328,20 +337,20 @@ impl Iterator for PeekableChars<'_> {
Some(value) => {
self.peek1 = self.peek2.take();
value
},
}
None => self.next_inner(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use Token::{Space as S, Newline as N, LeftBracket as L, RightBracket as R,
Colon as C, Equals as E, Quoted as Q, Underscore as TU, Star as TS,
Backtick as TB, Text as T, LineComment as LC, BlockComment as BC,
StarSlash as SS};
use Token::{
Backtick as TB, BlockComment as BC, Colon as C, Equals as E, LeftBracket as L,
LineComment as LC, Newline as N, Quoted as Q, RightBracket as R, Space as S, Star as TS,
StarSlash as SS, Text as T, Underscore as TU,
};
/// Test if the source code tokenizes to the tokens.
fn test(src: &str, tokens: Vec<Token>) {

View File

@ -6,7 +6,6 @@ use std::iter::Sum;
use std::ops::*;
use std::str::FromStr;
/// A general spacing type.
#[derive(Copy, Clone, PartialEq, Default)]
pub struct Size {
@ -39,57 +38,89 @@ pub struct SizeBox {
impl Size {
/// Create a zeroed size.
#[inline]
pub fn zero() -> Size { Size::default() }
pub fn zero() -> Size {
Size::default()
}
/// Create a size from an amount of points.
#[inline]
pub fn pt(points: f32) -> Size { Size { points } }
pub fn pt(points: f32) -> Size {
Size { points }
}
/// Create a size from an amount of millimeters.
#[inline]
pub fn mm(mm: f32) -> Size { Size { points: 2.83465 * mm } }
pub fn mm(mm: f32) -> Size {
Size {
points: 2.83465 * mm,
}
}
/// Create a size from an amount of centimeters.
#[inline]
pub fn cm(cm: f32) -> Size { Size { points: 28.3465 * cm } }
pub fn cm(cm: f32) -> Size {
Size {
points: 28.3465 * cm,
}
}
/// Create a size from an amount of inches.
#[inline]
pub fn inches(inches: f32) -> Size { Size { points: 72.0 * inches } }
pub fn inches(inches: f32) -> Size {
Size {
points: 72.0 * inches,
}
}
/// Convert this size into points.
#[inline]
pub fn to_pt(&self) -> f32 { self.points }
pub fn to_pt(&self) -> f32 {
self.points
}
/// Convert this size into millimeters.
#[inline]
pub fn to_mm(&self) -> f32 { self.points * 0.352778 }
pub fn to_mm(&self) -> f32 {
self.points * 0.352778
}
/// Convert this size into centimeters.
#[inline]
pub fn to_cm(&self) -> f32 { self.points * 0.0352778 }
pub fn to_cm(&self) -> f32 {
self.points * 0.0352778
}
/// Convert this size into inches.
#[inline]
pub fn to_inches(&self) -> f32 { self.points * 0.0138889 }
pub fn to_inches(&self) -> f32 {
self.points * 0.0138889
}
}
impl Size2D {
/// Create a new vector from two sizes.
#[inline]
pub fn new(x: Size, y: Size) -> Size2D { Size2D { x, y } }
pub fn new(x: Size, y: Size) -> Size2D {
Size2D { x, y }
}
/// Create a vector with all set to zero.
#[inline]
pub fn zero() -> Size2D { Size2D::default() }
pub fn zero() -> Size2D {
Size2D::default()
}
/// Create a new vector with `y` set to zero and `x` to a value.
#[inline]
pub fn with_x(x: Size) -> Size2D { Size2D { x, y: Size::zero() } }
pub fn with_x(x: Size) -> Size2D {
Size2D { x, y: Size::zero() }
}
/// Create a new vector with `x` set to zero and `y` to a value.
#[inline]
pub fn with_y(y: Size) -> Size2D { Size2D { x: Size::zero(), y } }
pub fn with_y(y: Size) -> Size2D {
Size2D { x: Size::zero(), y }
}
/// Return a [`Size2D`] padded by the paddings of the given box.
#[inline]
@ -105,7 +136,12 @@ impl SizeBox {
/// Create a new box from four sizes.
#[inline]
pub fn new(left: Size, top: Size, right: Size, bottom: Size) -> SizeBox {
SizeBox { left, top, right, bottom }
SizeBox {
left,
top,
right,
bottom,
}
}
/// Create a box with all set to zero.
@ -117,12 +153,20 @@ impl SizeBox {
/// The maximum of two sizes.
pub fn max(a: Size, b: Size) -> Size {
if a >= b { a } else { b }
if a >= b {
a
} else {
b
}
}
/// The minimum of two sizes.
pub fn min(a: Size, b: Size) -> Size {
if a <= b { a } else { b }
if a <= b {
a
} else {
b
}
}
//------------------------------------------------------------------------------------------------//
@ -146,10 +190,11 @@ impl FromStr for Size {
return Err(ParseSizeError);
}
let value = src[.. src.len() - 2].parse::<f32>()
let value = src[..src.len() - 2]
.parse::<f32>()
.map_err(|_| ParseSizeError)?;
Ok(match &src[src.len() - 2 ..] {
Ok(match &src[src.len() - 2..] {
"pt" => Size::pt(value),
"mm" => Size::mm(value),
"cm" => Size::cm(value),
@ -171,13 +216,16 @@ impl Neg for Size {
#[inline]
fn neg(self) -> Size {
Size { points: -self.points }
Size {
points: -self.points,
}
}
}
impl Sum for Size {
#[inline]
fn sum<I>(iter: I) -> Size where I: Iterator<Item=Size> {
fn sum<I>(iter: I) -> Size
where I: Iterator<Item = Size> {
iter.fold(Size::zero(), Add::add)
}
}
@ -189,7 +237,9 @@ macro_rules! impl_reflexive {
#[inline]
fn $func(self, other: Size) -> Size {
Size { points: $trait::$func(self.points, other.points) }
Size {
points: $trait::$func(self.points, other.points),
}
}
}
@ -209,7 +259,9 @@ macro_rules! impl_num_back {
#[inline]
fn $func(self, other: $ty) -> Size {
Size { points: $trait::$func(self.points, other as f32) }
Size {
points: $trait::$func(self.points, other as f32),
}
}
}
@ -231,7 +283,9 @@ macro_rules! impl_num_both {
#[inline]
fn $func(self, other: Size) -> Size {
Size { points: $trait::$func(self as f32, other.points) }
Size {
points: $trait::$func(self as f32, other.points),
}
}
}
};
@ -257,7 +311,10 @@ impl Neg for Size2D {
#[inline]
fn neg(self) -> Size2D {
Size2D { x: -self.x, y: -self.y }
Size2D {
x: -self.x,
y: -self.y,
}
}
}
@ -336,8 +393,11 @@ impl_num_back2d!(Div, div, DivAssign, div_assign, i32);
impl Display for SizeBox {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "[left: {}, top: {}, right: {}, bottom: {}]",
self.left, self.top, self.right, self.bottom)
write!(
f,
"[left: {}, top: {}, right: {}, bottom: {}]",
self.left, self.top, self.right, self.bottom
)
}
}

View File

@ -4,14 +4,13 @@ use toddle::query::FontClass;
use crate::size::{Size, Size2D, SizeBox};
/// Default styles for text.
#[derive(Debug, Clone)]
pub struct TextStyle {
/// The classes the font we want has to be part of.
pub classes: Vec<FontClass>,
/// A sequence of classes. We need the font to be part of at least one of these
/// and preferably the leftmost possible.
/// A sequence of classes. We need the font to be part of at least one of
/// these and preferably the leftmost possible.
pub fallback: Vec<FontClass>,
/// The font size.
pub font_size: f32,
@ -26,13 +25,14 @@ impl TextStyle {
///
/// If the class was one of _italic_ or _bold_, then:
/// - If it was not present, the _regular_ class will be removed.
/// - If it was present, the _regular_ class will be added in case the
/// other style class is not present.
/// - If it was present, the _regular_ class will be added in case the other
/// style class is not present.
pub fn toggle_class(&mut self, class: FontClass) {
if self.classes.contains(&class) {
self.classes.retain(|x| x != &class);
if (class == FontClass::Italic && !self.classes.contains(&FontClass::Bold))
|| (class == FontClass::Bold && !self.classes.contains(&FontClass::Italic)) {
|| (class == FontClass::Bold && !self.classes.contains(&FontClass::Italic))
{
self.classes.push(FontClass::Regular);
}
} else {

View File

@ -6,24 +6,26 @@ use std::fmt::{self, Display, Formatter};
use crate::func::Function;
use crate::size::Size;
/// A logical unit of the incoming text stream.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Token<'s> {
/// One or more whitespace (non-newline) codepoints.
Space,
/// A line feed (`\n`, `\r\n` and some more as defined by the Unicode standard).
/// A line feed (`\n`, `\r\n` and some more as defined by the Unicode
/// standard).
Newline,
/// A left bracket: `[`.
LeftBracket,
/// A right bracket: `]`.
RightBracket,
/// A colon (`:`) indicating the beginning of function arguments (Function header only).
/// A colon (`:`) indicating the beginning of function arguments (Function
/// header only).
///
/// If a colon occurs outside of a function header, it will be tokenized as a
/// [Word](Token::Word).
/// If a colon occurs outside of a function header, it will be tokenized as
/// a [Word](Token::Word).
Colon,
/// An equals (`=`) sign assigning a function argument a value (Function header only).
/// An equals (`=`) sign assigning a function argument a value (Function
/// header only).
Equals,
/// A comma (`,`) separating two function arguments (Function header only).
Comma,
@ -39,8 +41,9 @@ pub enum Token<'s> {
LineComment(&'s str),
/// A block comment.
BlockComment(&'s str),
/// A star followed by a slash unexpectedly ending a block comment (the comment was not started
/// before, otherwise a [BlockComment](Token::BlockComment) would be returned).
/// A star followed by a slash unexpectedly ending a block comment (the
/// comment was not started before, otherwise a
/// [BlockComment](Token::BlockComment) would be returned).
StarSlash,
/// Everything else is just text.
Text(&'s str),
@ -98,7 +101,7 @@ impl PartialEq for FuncCall {
pub struct FuncHeader {
pub name: String,
pub args: Vec<Expression>,
pub kwargs: HashMap<String, Expression>
pub kwargs: HashMap<String, Expression>,
}
/// A value expression.

View File

@ -1,15 +1,14 @@
use std::fs::{self, File};
use std::io::{Write, Read, BufWriter};
use std::io::{BufWriter, Read, Write};
use std::process::Command;
use typst::Typesetter;
use typst::export::pdf::PdfExporter;
use typst::layout::LayoutAction;
use typst::toddle::query::FileSystemFontProvider;
use typst::export::pdf::PdfExporter;
use typst::Typesetter;
const CACHE_DIR: &str = "test-cache";
fn main() {
let mut perfect_match = false;
let mut filter = Vec::new();
@ -31,9 +30,7 @@ fn main() {
for entry in fs::read_dir("tests/layouts/").unwrap() {
let path = entry.unwrap().path();
let name = path
.file_stem().unwrap()
.to_str().unwrap();
let name = path.file_stem().unwrap().to_str().unwrap();
let matches = if perfect_match {
filter.iter().any(|pattern| name == pattern)