mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Run rustfmt 🚿
This commit is contained in:
parent
5c04185892
commit
7c0899b537
@ -1,14 +1,13 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, BufWriter};
|
use std::io::{BufWriter, Read};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use typst::Typesetter;
|
|
||||||
use typst::export::pdf::PdfExporter;
|
use typst::export::pdf::PdfExporter;
|
||||||
use typst::toddle::query::FileSystemFontProvider;
|
use typst::toddle::query::FileSystemFontProvider;
|
||||||
|
use typst::Typesetter;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(err) = run() {
|
if let Err(err) = run() {
|
||||||
@ -26,11 +25,16 @@ fn run() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
let source_path = Path::new(&args[1]);
|
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 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()))
|
base.join(format!("{}.pdf", stem.to_string_lossy()))
|
||||||
} else {
|
} else {
|
||||||
@ -43,7 +47,9 @@ fn run() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
let mut src = String::new();
|
let mut src = String::new();
|
||||||
let mut source_file = File::open(source_path).map_err(|_| "failed to open source file")?;
|
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.
|
// Create a typesetter with a font provider that provides the default fonts.
|
||||||
let mut typesetter = Typesetter::new();
|
let mut typesetter = Typesetter::new();
|
||||||
|
@ -3,21 +3,22 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use tide::{PdfWriter, Ref, Rect, Version, Trailer};
|
|
||||||
use tide::content::Content;
|
use tide::content::Content;
|
||||||
use tide::doc::{Catalog, PageTree, Page, Resource, Text};
|
use tide::doc::{Catalog, Page, PageTree, Resource, Text};
|
||||||
use tide::font::{Type0Font, CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags};
|
use tide::font::{CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags, Type0Font};
|
||||||
use tide::font::{GlyphUnit, CMap, CMapEncoding, WidthRecord, FontStream};
|
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::font::OwnedFont;
|
||||||
use toddle::query::SharedFontLoader;
|
use toddle::query::SharedFontLoader;
|
||||||
|
use toddle::tables::{
|
||||||
|
CharMap, Header, HorizontalMetrics, MacStyleFlags, Name, NameEntry, Post, OS2,
|
||||||
|
};
|
||||||
use toddle::Error as FontError;
|
use toddle::Error as FontError;
|
||||||
|
|
||||||
use crate::layout::{MultiLayout, Layout, LayoutAction};
|
use crate::layout::{Layout, LayoutAction, MultiLayout};
|
||||||
use crate::size::{Size, Size2D};
|
use crate::size::{Size, Size2D};
|
||||||
|
|
||||||
|
|
||||||
/// Exports layouts into _PDFs_.
|
/// Exports layouts into _PDFs_.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PdfExporter {}
|
pub struct PdfExporter {}
|
||||||
@ -29,10 +30,16 @@ impl PdfExporter {
|
|||||||
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]
|
#[inline]
|
||||||
pub fn export<W: Write>(&self, layout: &MultiLayout, loader: &SharedFontLoader, target: W)
|
pub fn export<W: Write>(
|
||||||
-> PdfResult<usize> {
|
&self,
|
||||||
|
layout: &MultiLayout,
|
||||||
|
loader: &SharedFontLoader,
|
||||||
|
target: W,
|
||||||
|
) -> PdfResult<usize>
|
||||||
|
{
|
||||||
let mut engine = PdfEngine::new(layout, loader, target)?;
|
let mut engine = PdfEngine::new(layout, loader, target)?;
|
||||||
engine.write()
|
engine.write()
|
||||||
}
|
}
|
||||||
@ -59,8 +66,12 @@ struct Offsets {
|
|||||||
|
|
||||||
impl<'d, W: Write> PdfEngine<'d, W> {
|
impl<'d, W: Write> PdfEngine<'d, W> {
|
||||||
/// Create a new _PDF_ engine.
|
/// Create a new _PDF_ engine.
|
||||||
fn new(layout: &'d MultiLayout, loader: &SharedFontLoader, target: W)
|
fn new(
|
||||||
-> PdfResult<PdfEngine<'d, W>> {
|
layout: &'d MultiLayout,
|
||||||
|
loader: &SharedFontLoader,
|
||||||
|
target: W,
|
||||||
|
) -> PdfResult<PdfEngine<'d, W>>
|
||||||
|
{
|
||||||
// Create a subsetted PDF font for each font in the layout.
|
// Create a subsetted PDF font for each font in the layout.
|
||||||
let mut font_remap = HashMap::new();
|
let mut font_remap = HashMap::new();
|
||||||
let fonts = {
|
let fonts = {
|
||||||
@ -71,24 +82,26 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
|||||||
for boxed in &layout.layouts {
|
for boxed in &layout.layouts {
|
||||||
for action in &boxed.actions {
|
for action in &boxed.actions {
|
||||||
match action {
|
match action {
|
||||||
LayoutAction::WriteText(string) => {
|
LayoutAction::WriteText(string) => chars
|
||||||
chars.entry(font)
|
.entry(font)
|
||||||
.or_insert_with(HashSet::new)
|
.or_insert_with(HashSet::new)
|
||||||
.extend(string.chars())
|
.extend(string.chars()),
|
||||||
},
|
|
||||||
LayoutAction::SetFont(id, _) => {
|
LayoutAction::SetFont(id, _) => {
|
||||||
font = *id;
|
font = *id;
|
||||||
let new_id = font_remap.len();
|
let new_id = font_remap.len();
|
||||||
font_remap.entry(font).or_insert(new_id);
|
font_remap.entry(font).or_insert(new_id);
|
||||||
},
|
}
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect the fonts into a vector in the order of the values in the remapping.
|
// Collect the fonts into a vector in the order of the values in the remapping.
|
||||||
let mut loader = loader.borrow_mut();
|
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);
|
order.sort_by_key(|&(_, new)| new);
|
||||||
|
|
||||||
let mut fonts = vec![];
|
let mut fonts = vec![];
|
||||||
@ -96,8 +109,10 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
|||||||
let font = loader.get_with_index(index);
|
let font = loader.get_with_index(index);
|
||||||
let subsetted = font.subsetted(
|
let subsetted = font.subsetted(
|
||||||
chars[&index].iter().cloned(),
|
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)?);
|
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 pages = (page_tree + 1, page_tree + layout.layouts.len() as Ref);
|
||||||
let contents = (pages.1 + 1, pages.1 + 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 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 {
|
Ok(PdfEngine {
|
||||||
writer: PdfWriter::new(target),
|
writer: PdfWriter::new(target),
|
||||||
@ -129,32 +150,43 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
|||||||
self.write_pages()?;
|
self.write_pages()?;
|
||||||
self.write_fonts()?;
|
self.write_fonts()?;
|
||||||
self.writer.write_xref_table()?;
|
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())
|
Ok(self.writer.written())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the document catalog and page tree.
|
/// Write the document catalog and page tree.
|
||||||
fn write_page_tree(&mut self) -> PdfResult<()> {
|
fn write_page_tree(&mut self) -> PdfResult<()> {
|
||||||
// The document catalog
|
// 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
|
// The font resources
|
||||||
let offset = self.offsets.fonts.0;
|
let offset = self.offsets.fonts.0;
|
||||||
let fonts = (0 .. self.fonts.len())
|
let fonts =
|
||||||
.map(|i| Resource::Font((i + 1) as u32, offset + 5 * i as u32));
|
(0..self.fonts.len()).map(|i| Resource::Font((i + 1) as u32, offset + 5 * i as u32));
|
||||||
|
|
||||||
// The root page tree
|
// The root page tree
|
||||||
self.writer.write_obj(self.offsets.page_tree, PageTree::new()
|
self.writer.write_obj(
|
||||||
|
self.offsets.page_tree,
|
||||||
|
PageTree::new()
|
||||||
.kids(ids(self.offsets.pages))
|
.kids(ids(self.offsets.pages))
|
||||||
.resources(fonts)
|
.resources(fonts),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// The page objects
|
// The page objects
|
||||||
for (id, page) in ids(self.offsets.pages).zip(&self.layout.layouts) {
|
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());
|
let rect = Rect::new(
|
||||||
self.writer.write_obj(id, Page::new(self.offsets.page_tree)
|
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)
|
.media_box(rect)
|
||||||
.contents(ids(self.offsets.contents))
|
.contents(ids(self.offsets.contents)),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,8 +234,8 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
|||||||
|
|
||||||
// Write the text.
|
// Write the text.
|
||||||
text.tj(self.fonts[active_font.0].encode_text(&string)?);
|
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;
|
let mut id = self.offsets.fonts.0;
|
||||||
|
|
||||||
for font in &mut self.fonts {
|
for font in &mut self.fonts {
|
||||||
let name = font.read_table::<Name>()?
|
let name = font
|
||||||
|
.read_table::<Name>()?
|
||||||
.get_decoded(NameEntry::PostScriptName)
|
.get_decoded(NameEntry::PostScriptName)
|
||||||
.unwrap_or_else(|| "unknown".to_string());
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
let base_font = format!("ABCDEF+{}", name);
|
let base_font = format!("ABCDEF+{}", name);
|
||||||
|
|
||||||
// Write the base font object referencing the CID font.
|
// Write the base font object referencing the CID font.
|
||||||
self.writer.write_obj(id,
|
self.writer.write_obj(
|
||||||
|
id,
|
||||||
Type0Font::new(
|
Type0Font::new(
|
||||||
base_font.clone(),
|
base_font.clone(),
|
||||||
CMapEncoding::Predefined("Identity-H".to_owned()),
|
CMapEncoding::Predefined("Identity-H".to_owned()),
|
||||||
id + 1
|
id + 1,
|
||||||
).to_unicode(id + 3)
|
)
|
||||||
|
.to_unicode(id + 3),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Extract information from the head table.
|
// Extract information from the head table.
|
||||||
@ -252,19 +287,22 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
|||||||
// Transform the width into PDF units.
|
// Transform the width into PDF units.
|
||||||
let widths: Vec<_> = font
|
let widths: Vec<_> = font
|
||||||
.read_table::<HorizontalMetrics>()?
|
.read_table::<HorizontalMetrics>()?
|
||||||
.metrics.iter()
|
.metrics
|
||||||
|
.iter()
|
||||||
.map(|m| font_unit_to_glyph_unit(m.advance_width as f32))
|
.map(|m| font_unit_to_glyph_unit(m.advance_width as f32))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Write the CID font referencing the font descriptor.
|
// Write the CID font referencing the font descriptor.
|
||||||
let system_info = CIDSystemInfo::new("Adobe", "Identity", 0);
|
let system_info = CIDSystemInfo::new("Adobe", "Identity", 0);
|
||||||
self.writer.write_obj(id + 1,
|
self.writer.write_obj(
|
||||||
|
id + 1,
|
||||||
CIDFont::new(
|
CIDFont::new(
|
||||||
CIDFontType::Type2,
|
CIDFontType::Type2,
|
||||||
base_font.clone(),
|
base_font.clone(),
|
||||||
system_info.clone(),
|
system_info.clone(),
|
||||||
id + 2,
|
id + 2,
|
||||||
).widths(vec![WidthRecord::start(0, widths)])
|
)
|
||||||
|
.widths(vec![WidthRecord::start(0, widths)]),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Extract information from the post table.
|
// Extract information from the post table.
|
||||||
@ -284,24 +322,31 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
|||||||
let os2 = font.read_table::<OS2>()?;
|
let os2 = font.read_table::<OS2>()?;
|
||||||
|
|
||||||
// Write the font descriptor (contains the global information about the font).
|
// 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)
|
FontDescriptor::new(base_font, flags, italic_angle)
|
||||||
.font_bbox(bounding_box)
|
.font_bbox(bounding_box)
|
||||||
.ascent(font_unit_to_glyph_unit(os2.s_typo_ascender as f32))
|
.ascent(font_unit_to_glyph_unit(os2.s_typo_ascender as f32))
|
||||||
.descent(font_unit_to_glyph_unit(os2.s_typo_descender 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)
|
.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.
|
// Write the CMap, which maps glyphs to unicode codepoints.
|
||||||
let mapping = font.read_table::<CharMap>()?
|
let mapping = font
|
||||||
.mapping.iter()
|
.read_table::<CharMap>()?
|
||||||
|
.mapping
|
||||||
|
.iter()
|
||||||
.map(|(&c, &cid)| (cid, c));
|
.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.
|
// 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;
|
id += 5;
|
||||||
}
|
}
|
||||||
@ -311,8 +356,8 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create an iterator from a reference pair.
|
/// Create an iterator from a reference pair.
|
||||||
fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item=Ref> {
|
fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item = Ref> {
|
||||||
start ..= end
|
start..=end
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The error type for _PDF_ creation.
|
/// The error type for _PDF_ creation.
|
||||||
|
50
src/func.rs
50
src/func.rs
@ -6,27 +6,27 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
|
|
||||||
use toddle::query::FontClass;
|
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::parsing::{ParseContext, ParseResult};
|
||||||
use crate::syntax::{SyntaxTree, FuncHeader};
|
use crate::syntax::{FuncHeader, SyntaxTree};
|
||||||
|
|
||||||
|
|
||||||
/// Typesetting function types.
|
/// Typesetting function types.
|
||||||
///
|
///
|
||||||
/// These types have to be able to parse tokens into themselves and store the relevant information
|
/// These types have to be able to parse tokens into themselves and store the
|
||||||
/// from the parsing to do their role in typesetting later.
|
/// 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
|
/// The trait `FunctionBounds` is automatically implemented for types which can
|
||||||
/// functions, that is they fulfill the bounds `Debug + PartialEq + 'static`.
|
/// be used as functions, that is they fulfill the bounds `Debug + PartialEq +
|
||||||
|
/// 'static`.
|
||||||
pub trait Function: FunctionBounds {
|
pub trait Function: FunctionBounds {
|
||||||
/// Parse the header and body into this function given a context.
|
/// Parse the header and body into this function given a context.
|
||||||
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext)
|
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
|
||||||
-> ParseResult<Self> where Self: Sized;
|
where Self: Sized;
|
||||||
|
|
||||||
/// Layout this function given a context.
|
/// Layout this function given a context.
|
||||||
///
|
///
|
||||||
/// Returns optionally the resulting layout and a new context if changes to the context should
|
/// Returns optionally the resulting layout and a new context if changes to
|
||||||
/// be made.
|
/// the context should be made.
|
||||||
fn layout(&self, ctx: LayoutContext) -> LayoutResult<FuncCommands>;
|
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.
|
/// A sequence of commands requested for execution by a function.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FuncCommands<'a> {
|
pub struct FuncCommands<'a> {
|
||||||
pub commands: Vec<Command<'a>>
|
pub commands: Vec<Command<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FuncCommands<'a> {
|
impl<'a> FuncCommands<'a> {
|
||||||
/// Create an empty command list.
|
/// Create an empty command list.
|
||||||
pub fn new() -> FuncCommands<'a> {
|
pub fn new() -> FuncCommands<'a> {
|
||||||
FuncCommands {
|
FuncCommands { commands: vec![] }
|
||||||
commands: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a command to the sequence.
|
/// Add a command to the sequence.
|
||||||
@ -79,10 +77,11 @@ pub enum Command<'a> {
|
|||||||
ToggleStyleClass(FontClass),
|
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 +
|
/// Automatically implemented for all types which fulfill to the bounds `Debug +
|
||||||
/// 'static`. There should be no need to implement this manually.
|
/// PartialEq + 'static`. There should be no need to implement this manually.
|
||||||
pub trait FunctionBounds: Debug {
|
pub trait FunctionBounds: Debug {
|
||||||
/// Cast self into `Any`.
|
/// Cast self into `Any`.
|
||||||
fn help_cast_as_any(&self) -> &dyn 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;
|
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 {
|
fn help_cast_as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -111,13 +112,14 @@ pub struct Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A function which parses a function invocation into a function type.
|
/// A function which parses a function invocation into a function type.
|
||||||
type ParseFunc = dyn Fn(&FuncHeader, Option<&str>, ParseContext)
|
type ParseFunc = dyn Fn(&FuncHeader, Option<&str>, ParseContext) -> ParseResult<Box<dyn Function>>;
|
||||||
-> ParseResult<Box<dyn Function>>;
|
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
/// Create a new empty scope.
|
/// Create a new empty scope.
|
||||||
pub fn new() -> Scope {
|
pub fn new() -> Scope {
|
||||||
Scope { parsers: HashMap::new() }
|
Scope {
|
||||||
|
parsers: HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new scope with the standard functions contained.
|
/// 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) {
|
pub fn add<F: Function + 'static>(&mut self, name: &str) {
|
||||||
self.parsers.insert(
|
self.parsers.insert(
|
||||||
name.to_owned(),
|
name.to_owned(),
|
||||||
Box::new(|h, b, c| {
|
Box::new(|h, b, c| F::parse(h, b, c).map(|func| Box::new(func) as Box<dyn Function>)),
|
||||||
F::parse(h, b, c).map(|func| Box::new(func) as Box<dyn Function>)
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,10 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use crate::size::Size2D;
|
|
||||||
use super::Layout;
|
use super::Layout;
|
||||||
|
use crate::size::Size2D;
|
||||||
use LayoutAction::*;
|
use LayoutAction::*;
|
||||||
|
|
||||||
|
|
||||||
/// A layouting action.
|
/// A layouting action.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum LayoutAction {
|
pub enum LayoutAction {
|
||||||
@ -30,8 +29,14 @@ impl LayoutAction {
|
|||||||
MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()),
|
MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()),
|
||||||
SetFont(i, s) => write!(f, "f {} {}", i, s),
|
SetFont(i, s) => write!(f, "f {} {}", i, s),
|
||||||
WriteText(s) => write!(f, "w {}", s),
|
WriteText(s) => write!(f, "w {}", s),
|
||||||
DebugBox(p, s) => write!(f, "b {} {} {} {}",
|
DebugBox(p, s) => write!(
|
||||||
p.x.to_pt(), p.y.to_pt(), s.x.to_pt(), s.y.to_pt())
|
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 => {
|
SetFont(index, size) if (index, size) != self.active_font => {
|
||||||
self.next_font = Some((index, size));
|
self.next_font = Some((index, size));
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(target) = self.next_pos.take() {
|
if let Some(target) = self.next_pos.take() {
|
||||||
@ -92,19 +97,21 @@ impl LayoutActionList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.actions.push(action);
|
self.actions.push(action);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a series of actions.
|
/// 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() {
|
for action in actions.into_iter() {
|
||||||
self.add(action);
|
self.add(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add all actions from a box layout at a position. A move to the position
|
/// 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) {
|
pub fn add_box(&mut self, position: Size2D, layout: Layout) {
|
||||||
if let Some(target) = self.next_pos.take() {
|
if let Some(target) = self.next_pos.take() {
|
||||||
self.actions.push(MoveAbsolute(target));
|
self.actions.push(MoveAbsolute(target));
|
||||||
@ -114,7 +121,8 @@ impl LayoutActionList {
|
|||||||
self.origin = position;
|
self.origin = position;
|
||||||
|
|
||||||
if layout.debug_render {
|
if layout.debug_render {
|
||||||
self.actions.push(LayoutAction::DebugBox(position, layout.dimensions));
|
self.actions
|
||||||
|
.push(LayoutAction::DebugBox(position, layout.dimensions));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.extend(layout.actions);
|
self.extend(layout.actions);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// Finishes a flex layout by justifying the positions of the individual boxes.
|
/// Finishes a flex layout by justifying the positions of the individual boxes.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FlexLayouter {
|
pub struct FlexLayouter {
|
||||||
@ -32,7 +31,8 @@ enum FlexUnit {
|
|||||||
/// A content unit to be arranged flexibly.
|
/// A content unit to be arranged flexibly.
|
||||||
Boxed(Layout),
|
Boxed(Layout),
|
||||||
/// A unit which acts as glue between two [`FlexUnit::Boxed`] units and
|
/// 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),
|
Glue(Layout),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,9 @@ impl FlexLayouter {
|
|||||||
|
|
||||||
/// Layout the box.
|
/// Layout the box.
|
||||||
fn boxed(&mut self, boxed: Layout) -> LayoutResult<()> {
|
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)
|
.map(|g| g.dimensions.x)
|
||||||
.unwrap_or(Size::zero());
|
.unwrap_or(Size::zero());
|
||||||
|
|
||||||
@ -157,7 +159,7 @@ impl FlexLayouter {
|
|||||||
// Right align everything by shifting it right by the
|
// Right align everything by shifting it right by the
|
||||||
// amount of space left to the right of the line.
|
// amount of space left to the right of the line.
|
||||||
cursor + remaining_space
|
cursor + remaining_space
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.actions.add_box(position, layout);
|
self.actions.add_box(position, layout);
|
||||||
@ -173,7 +175,8 @@ impl FlexLayouter {
|
|||||||
|
|
||||||
self.dimensions.y += self.line_metrics.y;
|
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.x = self.ctx.space.padding.left;
|
||||||
self.cursor.y += self.line_metrics.y + self.ctx.flex_spacing;
|
self.cursor.y += self.line_metrics.y + self.ctx.flex_spacing;
|
||||||
|
|
||||||
|
@ -4,24 +4,23 @@ use std::borrow::Cow;
|
|||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use toddle::query::{SharedFontLoader, FontClass};
|
use toddle::query::{FontClass, SharedFontLoader};
|
||||||
use toddle::Error as FontError;
|
use toddle::Error as FontError;
|
||||||
|
|
||||||
use crate::func::Command;
|
use crate::func::Command;
|
||||||
use crate::size::{Size, Size2D, SizeBox};
|
use crate::size::{Size, Size2D, SizeBox};
|
||||||
use crate::syntax::{SyntaxTree, Node, FuncCall};
|
|
||||||
use crate::style::TextStyle;
|
use crate::style::TextStyle;
|
||||||
|
use crate::syntax::{FuncCall, Node, SyntaxTree};
|
||||||
|
|
||||||
mod text;
|
|
||||||
mod stacked;
|
|
||||||
mod flex;
|
|
||||||
mod actions;
|
mod actions;
|
||||||
|
mod flex;
|
||||||
|
mod stacked;
|
||||||
|
mod text;
|
||||||
|
|
||||||
pub use actions::{LayoutAction, LayoutActionList};
|
pub use actions::{LayoutAction, LayoutActionList};
|
||||||
|
pub use flex::{FlexContext, FlexLayouter};
|
||||||
|
pub use stacked::{StackContext, StackLayouter};
|
||||||
pub use text::{layout_text, TextContext};
|
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.
|
/// A box layout has a fixed width and height and composes of actions.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -37,7 +36,12 @@ pub struct Layout {
|
|||||||
impl Layout {
|
impl Layout {
|
||||||
/// Serialize this layout into an output buffer.
|
/// Serialize this layout into an output buffer.
|
||||||
pub fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
|
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 {
|
for action in &self.actions {
|
||||||
action.serialize(f)?;
|
action.serialize(f)?;
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
@ -55,9 +59,7 @@ pub struct MultiLayout {
|
|||||||
impl MultiLayout {
|
impl MultiLayout {
|
||||||
/// Create an empty multibox layout.
|
/// Create an empty multibox layout.
|
||||||
pub fn new() -> MultiLayout {
|
pub fn new() -> MultiLayout {
|
||||||
MultiLayout {
|
MultiLayout { layouts: vec![] }
|
||||||
layouts: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract a single sublayout and panic if this layout does not have
|
/// 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),
|
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() {
|
if !self.flex_layouter.is_empty() {
|
||||||
self.layout_text(" ", true)?;
|
self.layout_text(" ", true)?;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// Finish the current flex layout and add it to the box layouter.
|
// Finish the current flex layout and add it to the box layouter.
|
||||||
Node::Newline => {
|
Node::Newline => {
|
||||||
@ -186,7 +188,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
|
|||||||
let size = Size::pt(self.style.font_size)
|
let size = Size::pt(self.style.font_size)
|
||||||
* (self.style.line_spacing * self.style.paragraph_spacing - 1.0);
|
* (self.style.line_spacing * self.style.paragraph_spacing - 1.0);
|
||||||
self.stack_layouter.add_space(size)?;
|
self.stack_layouter.add_space(size)?;
|
||||||
},
|
}
|
||||||
|
|
||||||
// Toggle the text styles.
|
// Toggle the text styles.
|
||||||
Node::ToggleItalics => self.style.to_mut().toggle_class(FontClass::Italic),
|
Node::ToggleItalics => self.style.to_mut().toggle_class(FontClass::Italic),
|
||||||
@ -208,16 +210,19 @@ impl<'a, 'p> Layouter<'a, 'p> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(MultiLayout {
|
Ok(MultiLayout {
|
||||||
layouts: vec![self.stack_layouter.finish()]
|
layouts: vec![self.stack_layouter.finish()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a piece of text into a box.
|
/// Layout a piece of text into a box.
|
||||||
fn layout_text(&mut self, text: &str, glue: bool) -> LayoutResult<()> {
|
fn layout_text(&mut self, text: &str, glue: bool) -> LayoutResult<()> {
|
||||||
let boxed = layout_text(text, TextContext {
|
let boxed = layout_text(
|
||||||
|
text,
|
||||||
|
TextContext {
|
||||||
loader: &self.ctx.loader,
|
loader: &self.ctx.loader,
|
||||||
style: &self.style,
|
style: &self.style,
|
||||||
})?;
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
if glue {
|
if glue {
|
||||||
self.flex_layouter.add_glue(boxed);
|
self.flex_layouter.add_glue(boxed);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// Layouts boxes block-style.
|
/// Layouts boxes block-style.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StackLayouter {
|
pub struct StackLayouter {
|
||||||
@ -29,10 +28,13 @@ impl StackLayouter {
|
|||||||
Alignment::Right => Size2D::with_x(space.usable().x),
|
Alignment::Right => Size2D::with_x(space.usable().x),
|
||||||
},
|
},
|
||||||
usable: space.usable(),
|
usable: space.usable(),
|
||||||
cursor: Size2D::new(match ctx.space.alignment {
|
cursor: Size2D::new(
|
||||||
|
match ctx.space.alignment {
|
||||||
Alignment::Left => space.padding.left,
|
Alignment::Left => space.padding.left,
|
||||||
Alignment::Right => space.dimensions.x - space.padding.right,
|
Alignment::Right => space.dimensions.x - space.padding.right,
|
||||||
}, space.padding.top),
|
},
|
||||||
|
space.padding.top,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use toddle::query::{FontQuery, SharedFontLoader};
|
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 super::*;
|
||||||
|
use crate::size::{Size, Size2D};
|
||||||
|
|
||||||
/// The context for text layouting.
|
/// The context for text layouting.
|
||||||
#[derive(Copy, Clone)]
|
#[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);
|
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x);
|
||||||
|
|
||||||
// Add the char width to the total box width.
|
// Add the char width to the total box width.
|
||||||
let glyph = font.read_table::<CharMap>()?
|
let glyph = font
|
||||||
|
.read_table::<CharMap>()?
|
||||||
.get(character)
|
.get(character)
|
||||||
.expect("layout text: font should have char");
|
.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>()?
|
font.read_table::<HorizontalMetrics>()?
|
||||||
.get(glyph)
|
.get(glyph)
|
||||||
.expect("layout text: font should have 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;
|
let char_width = glyph_width * ctx.style.font_size;
|
||||||
|
40
src/lib.rs
40
src/lib.rs
@ -1,26 +1,28 @@
|
|||||||
//! The compiler for the _Typst_ typesetting language.
|
//! The compiler for the _Typst_ typesetting language.
|
||||||
//!
|
//!
|
||||||
//! # Steps
|
//! # Steps
|
||||||
//! - **Parsing:** The parsing step first transforms a plain string into an [iterator of
|
//! - **Parsing:** The parsing step first transforms a plain string into an
|
||||||
//! tokens](crate::parsing::Tokens). Then parser constructs a syntax tree from the token stream.
|
//! [iterator of tokens](crate::parsing::Tokens). Then parser constructs a
|
||||||
//! The structures describing the tree can be found in the [syntax]. Dynamic functions parse
|
//! syntax tree from the token stream. The structures describing the tree can
|
||||||
//! their own bodies themselves.
|
//! be found in the [syntax]. Dynamic functions parse their own bodies
|
||||||
//! - **Layouting:** The next step is to transform the syntax tree into a portable representation of
|
//! themselves.
|
||||||
//! the typesetted document. Types for these can be found in the [layout] module.
|
//! - **Layouting:** The next step is to transform the syntax tree into a
|
||||||
//! - **Exporting:** The finished document can then be exported into supported formats. Submodules
|
//! portable representation of the typesetted document. Types for these can be
|
||||||
//! for the supported formats are located in the [export] module. Currently the only supported
|
//! found in the [layout] module.
|
||||||
//! format is _PDF_.
|
//! - **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;
|
pub extern crate toddle;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use toddle::query::{FontLoader, SharedFontLoader, FontProvider};
|
use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
|
||||||
|
|
||||||
use crate::func::Scope;
|
use crate::func::Scope;
|
||||||
use crate::parsing::{parse, ParseContext, ParseResult, ParseError};
|
|
||||||
use crate::layout::{layout_tree, LayoutContext, MultiLayout};
|
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::style::{PageStyle, TextStyle};
|
||||||
use crate::syntax::SyntaxTree;
|
use crate::syntax::SyntaxTree;
|
||||||
|
|
||||||
@ -29,12 +31,11 @@ mod macros;
|
|||||||
pub mod export;
|
pub mod export;
|
||||||
pub mod func;
|
pub mod func;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
pub mod library;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
pub mod size;
|
pub mod size;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
pub mod library;
|
|
||||||
|
|
||||||
|
|
||||||
/// Transforms source code into typesetted documents.
|
/// Transforms source code into typesetted documents.
|
||||||
///
|
///
|
||||||
@ -73,7 +74,8 @@ impl<'p> Typesetter<'p> {
|
|||||||
|
|
||||||
/// Add a font provider to the context of this typesetter.
|
/// Add a font provider to the context of this typesetter.
|
||||||
#[inline]
|
#[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);
|
self.loader.get_mut().add_provider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,12 +100,15 @@ impl<'p> Typesetter<'p> {
|
|||||||
shrink_to_fit: false,
|
shrink_to_fit: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let pages = layout_tree(&tree, LayoutContext {
|
let pages = layout_tree(
|
||||||
|
&tree,
|
||||||
|
LayoutContext {
|
||||||
loader: &self.loader,
|
loader: &self.loader,
|
||||||
style: &self.text_style,
|
style: &self.text_style,
|
||||||
space,
|
space,
|
||||||
extra_space: Some(space),
|
extra_space: Some(space),
|
||||||
})?;
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(pages)
|
Ok(pages)
|
||||||
}
|
}
|
||||||
@ -116,7 +121,6 @@ impl<'p> Typesetter<'p> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The general error type for typesetting.
|
/// The general error type for typesetting.
|
||||||
pub enum TypesetError {
|
pub enum TypesetError {
|
||||||
/// An error that occured while parsing.
|
/// An error that occured while parsing.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use crate::layout::Alignment;
|
use crate::layout::Alignment;
|
||||||
|
|
||||||
|
|
||||||
/// Allows to align content in different ways.
|
/// Allows to align content in different ways.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct AlignFunc {
|
pub struct AlignFunc {
|
||||||
@ -10,9 +9,8 @@ pub struct AlignFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Function for AlignFunc {
|
impl Function for AlignFunc {
|
||||||
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext)
|
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
|
||||||
-> ParseResult<Self> where Self: Sized {
|
where Self: Sized {
|
||||||
|
|
||||||
if header.args.len() != 1 || !header.kwargs.is_empty() {
|
if header.args.len() != 1 || !header.kwargs.is_empty() {
|
||||||
return err("expected exactly one positional argument specifying the alignment");
|
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)),
|
s => return err(format!("invalid alignment specifier: '{}'", s)),
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
let body = if let Some(body) = body {
|
||||||
|
@ -7,11 +7,11 @@ mod styles;
|
|||||||
|
|
||||||
/// Useful imports for creating your own functions.
|
/// Useful imports for creating your own functions.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::syntax::{SyntaxTree, FuncHeader, Expression};
|
pub use crate::func::{Command, FuncCommands, Function};
|
||||||
pub use crate::parsing::{parse, ParseContext, ParseResult, ParseError};
|
pub use crate::layout::{layout_tree, Layout, LayoutContext, MultiLayout};
|
||||||
pub use crate::layout::{layout_tree, LayoutContext, MultiLayout, Layout};
|
pub use crate::layout::{LayoutError, LayoutResult};
|
||||||
pub use crate::layout::{LayoutResult, LayoutError};
|
pub use crate::parsing::{parse, ParseContext, ParseError, ParseResult};
|
||||||
pub use crate::func::{Function, Command, FuncCommands};
|
pub use crate::syntax::{Expression, FuncHeader, SyntaxTree};
|
||||||
|
|
||||||
pub fn err<S: Into<String>, T>(message: S) -> ParseResult<T> {
|
pub fn err<S: Into<String>, T>(message: S) -> ParseResult<T> {
|
||||||
Err(ParseError::new(message))
|
Err(ParseError::new(message))
|
||||||
@ -19,8 +19,7 @@ pub mod prelude {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use align::AlignFunc;
|
pub use align::AlignFunc;
|
||||||
pub use styles::{ItalicFunc, BoldFunc, MonospaceFunc};
|
pub use styles::{BoldFunc, ItalicFunc, MonospaceFunc};
|
||||||
|
|
||||||
|
|
||||||
/// Create a scope with all standard functions.
|
/// Create a scope with all standard functions.
|
||||||
pub fn std() -> Scope {
|
pub fn std() -> Scope {
|
||||||
|
@ -2,7 +2,6 @@ use toddle::query::FontClass;
|
|||||||
|
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
macro_rules! style_func {
|
macro_rules! style_func {
|
||||||
(
|
(
|
||||||
$(#[$outer:meta])*
|
$(#[$outer:meta])*
|
||||||
|
@ -5,14 +5,13 @@ use std::collections::HashMap;
|
|||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
|
|
||||||
use crate::func::{Function, Scope};
|
use crate::func::{Function, Scope};
|
||||||
use crate::syntax::*;
|
|
||||||
use crate::size::Size;
|
use crate::size::Size;
|
||||||
|
use crate::syntax::*;
|
||||||
|
|
||||||
mod tokens;
|
mod tokens;
|
||||||
|
|
||||||
pub use tokens::{tokenize, Tokens};
|
pub use tokens::{tokenize, Tokens};
|
||||||
|
|
||||||
|
|
||||||
/// Parses source code into a syntax tree given a context.
|
/// Parses source code into a syntax tree given a context.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str, ctx: ParseContext) -> ParseResult<SyntaxTree> {
|
pub fn parse(src: &str, ctx: ParseContext) -> ParseResult<SyntaxTree> {
|
||||||
@ -105,10 +104,7 @@ impl<'s> Parser<'s> {
|
|||||||
let body = self.parse_func_body(&header)?;
|
let body = self.parse_func_body(&header)?;
|
||||||
|
|
||||||
// Finally this function is parsed to the end.
|
// Finally this function is parsed to the end.
|
||||||
self.append(Node::Func(FuncCall {
|
self.append(Node::Func(FuncCall { header, body }));
|
||||||
header,
|
|
||||||
body,
|
|
||||||
}));
|
|
||||||
|
|
||||||
Ok(self.switch(ParserState::Body))
|
Ok(self.switch(ParserState::Body))
|
||||||
}
|
}
|
||||||
@ -124,7 +120,7 @@ impl<'s> Parser<'s> {
|
|||||||
} else {
|
} else {
|
||||||
Err(ParseError::new(format!("invalid identifier: '{}'", word)))
|
Err(ParseError::new(format!("invalid identifier: '{}'", word)))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => Err(ParseError::new("expected identifier")),
|
_ => Err(ParseError::new("expected identifier")),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
@ -138,13 +134,17 @@ impl<'s> Parser<'s> {
|
|||||||
|
|
||||||
// Check for arguments
|
// Check for arguments
|
||||||
match self.tokens.next() {
|
match self.tokens.next() {
|
||||||
Some(Token::RightBracket) => {},
|
Some(Token::RightBracket) => {}
|
||||||
Some(Token::Colon) => {
|
Some(Token::Colon) => {
|
||||||
let (args, kwargs) = self.parse_func_args()?;
|
let (args, kwargs) = self.parse_func_args()?;
|
||||||
header.args = args;
|
header.args = args;
|
||||||
header.kwargs = kwargs;
|
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.
|
// Store the header information of the function invocation.
|
||||||
@ -164,16 +164,16 @@ impl<'s> Parser<'s> {
|
|||||||
Some(Token::Text(_)) | Some(Token::Quoted(_)) if !comma => {
|
Some(Token::Text(_)) | Some(Token::Quoted(_)) if !comma => {
|
||||||
args.push(self.parse_expression()?);
|
args.push(self.parse_expression()?);
|
||||||
comma = true;
|
comma = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
Some(Token::Comma) if comma => {
|
Some(Token::Comma) if comma => {
|
||||||
self.advance();
|
self.advance();
|
||||||
comma = false
|
comma = false
|
||||||
},
|
}
|
||||||
Some(Token::RightBracket) => {
|
Some(Token::RightBracket) => {
|
||||||
self.advance();
|
self.advance();
|
||||||
break
|
break;
|
||||||
},
|
}
|
||||||
|
|
||||||
_ if comma => return Err(ParseError::new("expected comma or closing bracket")),
|
_ if comma => return Err(ParseError::new("expected comma or closing bracket")),
|
||||||
_ => return Err(ParseError::new("expected closing bracket")),
|
_ => return Err(ParseError::new("expected closing bracket")),
|
||||||
@ -197,7 +197,7 @@ impl<'s> Parser<'s> {
|
|||||||
} else {
|
} else {
|
||||||
Expression::Ident(text.to_owned())
|
Expression::Ident(text.to_owned())
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => return Err(ParseError::new("expected expression")),
|
_ => return Err(ParseError::new("expected expression")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -211,19 +211,25 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now we want to parse this function dynamically.
|
// 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)))?;
|
.ok_or_else(|| ParseError::new(format!("unknown function: '{}'", &header.name)))?;
|
||||||
|
|
||||||
// Do the parsing dependent on whether the function has a body.
|
// Do the parsing dependent on whether the function has a body.
|
||||||
Ok(if has_body {
|
Ok(if has_body {
|
||||||
// Find out the string which makes the body of this function.
|
// Find out the string which makes the body of this function.
|
||||||
let (start, end) = self.tokens.current_index().and_then(|index| {
|
let (start, end) = self
|
||||||
find_closing_bracket(&self.src[index..])
|
.tokens
|
||||||
.map(|end| (index, index + end))
|
.current_index()
|
||||||
}).ok_or_else(|| ParseError::new("expected closing bracket"))?;
|
.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.
|
// 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)?;
|
let body = parser(&header, Some(body_string), self.ctx)?;
|
||||||
|
|
||||||
// Skip to the end of the function in the token stream.
|
// Skip to the end of the function in the token stream.
|
||||||
@ -246,12 +252,12 @@ impl<'s> Parser<'s> {
|
|||||||
Token::Newline => {
|
Token::Newline => {
|
||||||
self.append_consumed(Node::Newline);
|
self.append_consumed(Node::Newline);
|
||||||
self.switch(ParserState::WroteNewline);
|
self.switch(ParserState::WroteNewline);
|
||||||
},
|
}
|
||||||
Token::Space => self.append_space_consumed(),
|
Token::Space => self.append_space_consumed(),
|
||||||
_ => {
|
_ => {
|
||||||
self.append_space();
|
self.append_space();
|
||||||
self.switch(ParserState::Body);
|
self.switch(ParserState::Body);
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
ParserState::WroteNewline => match token {
|
ParserState::WroteNewline => match token {
|
||||||
Token::Newline | Token::Space => self.append_space_consumed(),
|
Token::Newline | Token::Space => self.append_space_consumed(),
|
||||||
@ -263,17 +269,17 @@ impl<'s> Parser<'s> {
|
|||||||
Token::Newline => {
|
Token::Newline => {
|
||||||
self.advance();
|
self.advance();
|
||||||
self.switch(ParserState::FirstNewline);
|
self.switch(ParserState::FirstNewline);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
Token::LineComment(_) | Token::BlockComment(_) => self.advance(),
|
Token::LineComment(_) | Token::BlockComment(_) => self.advance(),
|
||||||
Token::StarSlash => {
|
Token::StarSlash => {
|
||||||
return Err(ParseError::new("unexpected end of block comment"));
|
return Err(ParseError::new("unexpected end of block comment"));
|
||||||
},
|
}
|
||||||
|
|
||||||
// Anything else skips out of the function.
|
// Anything else skips out of the function.
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,8 +290,9 @@ impl<'s> Parser<'s> {
|
|||||||
fn skip_white(&mut self) {
|
fn skip_white(&mut self) {
|
||||||
while let Some(token) = self.tokens.peek() {
|
while let Some(token) = self.tokens.peek() {
|
||||||
match token {
|
match token {
|
||||||
Token::Space | Token::Newline
|
Token::Space | Token::Newline | Token::LineComment(_) | Token::BlockComment(_) => {
|
||||||
| Token::LineComment(_) | Token::BlockComment(_) => self.advance(),
|
self.advance()
|
||||||
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,19 +342,19 @@ fn find_closing_bracket(src: &str) -> Option<usize> {
|
|||||||
'\\' => {
|
'\\' => {
|
||||||
escaped = !escaped;
|
escaped = !escaped;
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
']' if !escaped && parens == 0 => return Some(index),
|
']' if !escaped && parens == 0 => return Some(index),
|
||||||
'[' if !escaped => parens += 1,
|
'[' if !escaped => parens += 1,
|
||||||
']' if !escaped => parens -= 1,
|
']' if !escaped => parens -= 1,
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
escaped = false;
|
escaped = false;
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A peekable iterator for tokens which allows access to the original iterator inside this module
|
/// A peekable iterator for tokens which allows access to the original iterator
|
||||||
/// (which is needed by the parser).
|
/// inside this module (which is needed by the parser).
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct PeekableTokens<'s> {
|
struct PeekableTokens<'s> {
|
||||||
tokens: Tokens<'s>,
|
tokens: Tokens<'s>,
|
||||||
@ -411,7 +418,6 @@ fn is_identifier(string: &str) -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The error type for parsing.
|
/// The error type for parsing.
|
||||||
pub struct ParseError(String);
|
pub struct ParseError(String);
|
||||||
|
|
||||||
@ -430,17 +436,16 @@ error_type! {
|
|||||||
show: f => f.write_str(&err.0),
|
show: f => f.write_str(&err.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::func::{Function, FuncCommands, Scope};
|
use crate::func::{FuncCommands, Function, Scope};
|
||||||
use crate::layout::{LayoutContext, LayoutResult};
|
use crate::layout::{LayoutContext, LayoutResult};
|
||||||
use Node::{Space as S, Newline as N, Func as F};
|
|
||||||
use funcs::*;
|
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
|
/// Two test functions, one which parses it's body as another syntax tree
|
||||||
/// does not expect a body.
|
/// and another one which does not expect a body.
|
||||||
mod funcs {
|
mod funcs {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -449,8 +454,8 @@ mod tests {
|
|||||||
pub struct TreeFn(pub SyntaxTree);
|
pub struct TreeFn(pub SyntaxTree);
|
||||||
|
|
||||||
impl Function for TreeFn {
|
impl Function for TreeFn {
|
||||||
fn parse(_: &FuncHeader, body: Option<&str>, ctx: ParseContext)
|
fn parse(_: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
|
||||||
-> ParseResult<Self> where Self: Sized {
|
where Self: Sized {
|
||||||
if let Some(src) = body {
|
if let Some(src) = body {
|
||||||
parse(src, ctx).map(|tree| TreeFn(tree))
|
parse(src, ctx).map(|tree| TreeFn(tree))
|
||||||
} else {
|
} else {
|
||||||
@ -468,8 +473,8 @@ mod tests {
|
|||||||
pub struct BodylessFn;
|
pub struct BodylessFn;
|
||||||
|
|
||||||
impl Function for BodylessFn {
|
impl Function for BodylessFn {
|
||||||
fn parse(_: &FuncHeader, body: Option<&str>, _: ParseContext)
|
fn parse(_: &FuncHeader, body: Option<&str>, _: ParseContext) -> ParseResult<Self>
|
||||||
-> ParseResult<Self> where Self: Sized {
|
where Self: Sized {
|
||||||
if body.is_none() {
|
if body.is_none() {
|
||||||
Ok(BodylessFn)
|
Ok(BodylessFn)
|
||||||
} else {
|
} else {
|
||||||
@ -485,7 +490,9 @@ mod tests {
|
|||||||
|
|
||||||
/// Test if the source code parses into the syntax tree.
|
/// Test if the source code parses into the syntax tree.
|
||||||
fn test(src: &str, tree: SyntaxTree) {
|
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);
|
assert_eq!(parse(src, ctx).unwrap(), tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,7 +504,9 @@ mod tests {
|
|||||||
|
|
||||||
/// Test if the source parses into the error.
|
/// Test if the source parses into the error.
|
||||||
fn test_err(src: &str, err: &str) {
|
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);
|
assert_eq!(parse(src, ctx).unwrap_err().to_string(), err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,9 +518,12 @@ mod tests {
|
|||||||
|
|
||||||
/// Create a text node.
|
/// Create a text node.
|
||||||
#[allow(non_snake_case)]
|
#[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 {
|
macro_rules! tree {
|
||||||
($($x:expr),*) => (
|
($($x:expr),*) => (
|
||||||
SyntaxTree { nodes: vec![$($x),*] }
|
SyntaxTree { nodes: vec![$($x),*] }
|
||||||
|
@ -4,7 +4,6 @@ use smallvec::SmallVec;
|
|||||||
|
|
||||||
use crate::syntax::*;
|
use crate::syntax::*;
|
||||||
|
|
||||||
|
|
||||||
/// Builds an iterator over the tokens of the source code.
|
/// Builds an iterator over the tokens of the source code.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn tokenize(src: &str) -> Tokens {
|
pub fn tokenize(src: &str) -> Tokens {
|
||||||
@ -15,7 +14,7 @@ pub fn tokenize(src: &str) -> Tokens {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Tokens<'s> {
|
pub struct Tokens<'s> {
|
||||||
src: &'s str,
|
src: &'s str,
|
||||||
pub(in super) chars: PeekableChars<'s>,
|
pub(super) chars: PeekableChars<'s>,
|
||||||
state: TokensState,
|
state: TokensState,
|
||||||
stack: SmallVec<[TokensState; 1]>,
|
stack: SmallVec<[TokensState; 1]>,
|
||||||
}
|
}
|
||||||
@ -67,7 +66,7 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
/// Returns a word containing the string bounded by the given indices.
|
/// Returns a word containing the string bounded by the given indices.
|
||||||
fn text(&self, start: usize, end: usize) -> Token<'s> {
|
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>> {
|
fn next(&mut self) -> Option<Token<'s>> {
|
||||||
use TokensState as TU;
|
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.state == TU::MaybeBody {
|
||||||
if self.chars.peek()?.1 == '[' {
|
if self.chars.peek()?.1 == '[' {
|
||||||
self.state = TU::Body;
|
self.state = TU::Body;
|
||||||
@ -97,7 +97,7 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
'[' => {
|
'[' => {
|
||||||
self.switch(TU::Function);
|
self.switch(TU::Function);
|
||||||
Token::LeftBracket
|
Token::LeftBracket
|
||||||
},
|
}
|
||||||
']' => {
|
']' => {
|
||||||
if self.state == TU::Function {
|
if self.state == TU::Function {
|
||||||
self.state = TU::MaybeBody;
|
self.state = TU::MaybeBody;
|
||||||
@ -105,7 +105,7 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
self.unswitch();
|
self.unswitch();
|
||||||
}
|
}
|
||||||
Token::RightBracket
|
Token::RightBracket
|
||||||
},
|
}
|
||||||
|
|
||||||
// Line comment
|
// Line comment
|
||||||
'/' if afterwards == Some('/') => {
|
'/' if afterwards == Some('/') => {
|
||||||
@ -121,8 +121,8 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let end = end.0 + end.1.len_utf8();
|
let end = end.0 + end.1.len_utf8();
|
||||||
Token::LineComment(&self.src[start .. end])
|
Token::LineComment(&self.src[start..end])
|
||||||
},
|
}
|
||||||
|
|
||||||
// Block comment
|
// Block comment
|
||||||
'/' if afterwards == Some('*') => {
|
'/' if afterwards == Some('*') => {
|
||||||
@ -133,17 +133,26 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
while let Some((index, c)) = self.chars.next() {
|
while let Some((index, c)) = self.chars.next() {
|
||||||
let after = self.chars.peek().map(|p| p.1);
|
let after = self.chars.peek().map(|p| p.1);
|
||||||
match (c, after) {
|
match (c, after) {
|
||||||
('*', Some('/')) if nested == 0 => { self.advance(); break },
|
('*', Some('/')) if nested == 0 => {
|
||||||
('/', Some('*')) => { self.advance(); nested += 1 },
|
self.advance();
|
||||||
('*', Some('/')) => { self.advance(); nested -= 1 },
|
break;
|
||||||
_ => {},
|
}
|
||||||
|
('/', Some('*')) => {
|
||||||
|
self.advance();
|
||||||
|
nested += 1
|
||||||
|
}
|
||||||
|
('*', Some('/')) => {
|
||||||
|
self.advance();
|
||||||
|
nested -= 1
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
end = (index, c);
|
end = (index, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
let end = end.0 + end.1.len_utf8();
|
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
|
// Unexpected end of block comment
|
||||||
'*' if afterwards == Some('/') => self.consumed(Token::StarSlash),
|
'*' 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();
|
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
|
// Escaping
|
||||||
@ -207,7 +216,7 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Token::Text("\\")
|
Token::Text("\\")
|
||||||
},
|
}
|
||||||
|
|
||||||
// Normal text
|
// Normal text
|
||||||
_ => {
|
_ => {
|
||||||
@ -241,7 +250,7 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
|
|
||||||
let end_pos = end.0 + end.1.len_utf8();
|
let end_pos = end.0 + end.1.len_utf8();
|
||||||
self.text(next_pos, end_pos)
|
self.text(next_pos, end_pos)
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,20 +337,20 @@ impl Iterator for PeekableChars<'_> {
|
|||||||
Some(value) => {
|
Some(value) => {
|
||||||
self.peek1 = self.peek2.take();
|
self.peek1 = self.peek2.take();
|
||||||
value
|
value
|
||||||
},
|
}
|
||||||
None => self.next_inner(),
|
None => self.next_inner(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use Token::{Space as S, Newline as N, LeftBracket as L, RightBracket as R,
|
use Token::{
|
||||||
Colon as C, Equals as E, Quoted as Q, Underscore as TU, Star as TS,
|
Backtick as TB, BlockComment as BC, Colon as C, Equals as E, LeftBracket as L,
|
||||||
Backtick as TB, Text as T, LineComment as LC, BlockComment as BC,
|
LineComment as LC, Newline as N, Quoted as Q, RightBracket as R, Space as S, Star as TS,
|
||||||
StarSlash as SS};
|
StarSlash as SS, Text as T, Underscore as TU,
|
||||||
|
};
|
||||||
|
|
||||||
/// Test if the source code tokenizes to the tokens.
|
/// Test if the source code tokenizes to the tokens.
|
||||||
fn test(src: &str, tokens: Vec<Token>) {
|
fn test(src: &str, tokens: Vec<Token>) {
|
||||||
|
114
src/size.rs
114
src/size.rs
@ -6,7 +6,6 @@ use std::iter::Sum;
|
|||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
|
||||||
/// A general spacing type.
|
/// A general spacing type.
|
||||||
#[derive(Copy, Clone, PartialEq, Default)]
|
#[derive(Copy, Clone, PartialEq, Default)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
@ -39,57 +38,89 @@ pub struct SizeBox {
|
|||||||
impl Size {
|
impl Size {
|
||||||
/// Create a zeroed size.
|
/// Create a zeroed size.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn zero() -> Size { Size::default() }
|
pub fn zero() -> Size {
|
||||||
|
Size::default()
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a size from an amount of points.
|
/// Create a size from an amount of points.
|
||||||
#[inline]
|
#[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.
|
/// Create a size from an amount of millimeters.
|
||||||
#[inline]
|
#[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.
|
/// Create a size from an amount of centimeters.
|
||||||
#[inline]
|
#[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.
|
/// Create a size from an amount of inches.
|
||||||
#[inline]
|
#[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.
|
/// Convert this size into points.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_pt(&self) -> f32 { self.points }
|
pub fn to_pt(&self) -> f32 {
|
||||||
|
self.points
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert this size into millimeters.
|
/// Convert this size into millimeters.
|
||||||
#[inline]
|
#[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.
|
/// Convert this size into centimeters.
|
||||||
#[inline]
|
#[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.
|
/// Convert this size into inches.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_inches(&self) -> f32 { self.points * 0.0138889 }
|
pub fn to_inches(&self) -> f32 {
|
||||||
|
self.points * 0.0138889
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Size2D {
|
impl Size2D {
|
||||||
/// Create a new vector from two sizes.
|
/// Create a new vector from two sizes.
|
||||||
#[inline]
|
#[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.
|
/// Create a vector with all set to zero.
|
||||||
#[inline]
|
#[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.
|
/// Create a new vector with `y` set to zero and `x` to a value.
|
||||||
#[inline]
|
#[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.
|
/// Create a new vector with `x` set to zero and `y` to a value.
|
||||||
#[inline]
|
#[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.
|
/// Return a [`Size2D`] padded by the paddings of the given box.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -105,7 +136,12 @@ impl SizeBox {
|
|||||||
/// Create a new box from four sizes.
|
/// Create a new box from four sizes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(left: Size, top: Size, right: Size, bottom: Size) -> SizeBox {
|
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.
|
/// Create a box with all set to zero.
|
||||||
@ -117,12 +153,20 @@ impl SizeBox {
|
|||||||
|
|
||||||
/// The maximum of two sizes.
|
/// The maximum of two sizes.
|
||||||
pub fn max(a: Size, b: Size) -> Size {
|
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.
|
/// The minimum of two sizes.
|
||||||
pub fn min(a: Size, b: Size) -> Size {
|
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);
|
return Err(ParseSizeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = src[.. src.len() - 2].parse::<f32>()
|
let value = src[..src.len() - 2]
|
||||||
|
.parse::<f32>()
|
||||||
.map_err(|_| ParseSizeError)?;
|
.map_err(|_| ParseSizeError)?;
|
||||||
|
|
||||||
Ok(match &src[src.len() - 2 ..] {
|
Ok(match &src[src.len() - 2..] {
|
||||||
"pt" => Size::pt(value),
|
"pt" => Size::pt(value),
|
||||||
"mm" => Size::mm(value),
|
"mm" => Size::mm(value),
|
||||||
"cm" => Size::cm(value),
|
"cm" => Size::cm(value),
|
||||||
@ -171,13 +216,16 @@ impl Neg for Size {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn neg(self) -> Size {
|
fn neg(self) -> Size {
|
||||||
Size { points: -self.points }
|
Size {
|
||||||
|
points: -self.points,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sum for Size {
|
impl Sum for Size {
|
||||||
#[inline]
|
#[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)
|
iter.fold(Size::zero(), Add::add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +237,9 @@ macro_rules! impl_reflexive {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn $func(self, other: Size) -> Size {
|
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]
|
#[inline]
|
||||||
fn $func(self, other: $ty) -> Size {
|
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]
|
#[inline]
|
||||||
fn $func(self, other: Size) -> Size {
|
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]
|
#[inline]
|
||||||
fn neg(self) -> Size2D {
|
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 {
|
impl Display for SizeBox {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "[left: {}, top: {}, right: {}, bottom: {}]",
|
write!(
|
||||||
self.left, self.top, self.right, self.bottom)
|
f,
|
||||||
|
"[left: {}, top: {}, right: {}, bottom: {}]",
|
||||||
|
self.left, self.top, self.right, self.bottom
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
src/style.rs
12
src/style.rs
@ -4,14 +4,13 @@ use toddle::query::FontClass;
|
|||||||
|
|
||||||
use crate::size::{Size, Size2D, SizeBox};
|
use crate::size::{Size, Size2D, SizeBox};
|
||||||
|
|
||||||
|
|
||||||
/// Default styles for text.
|
/// Default styles for text.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TextStyle {
|
pub struct TextStyle {
|
||||||
/// The classes the font we want has to be part of.
|
/// The classes the font we want has to be part of.
|
||||||
pub classes: Vec<FontClass>,
|
pub classes: Vec<FontClass>,
|
||||||
/// A sequence of classes. We need the font to be part of at least one of these
|
/// A sequence of classes. We need the font to be part of at least one of
|
||||||
/// and preferably the leftmost possible.
|
/// these and preferably the leftmost possible.
|
||||||
pub fallback: Vec<FontClass>,
|
pub fallback: Vec<FontClass>,
|
||||||
/// The font size.
|
/// The font size.
|
||||||
pub font_size: f32,
|
pub font_size: f32,
|
||||||
@ -26,13 +25,14 @@ impl TextStyle {
|
|||||||
///
|
///
|
||||||
/// If the class was one of _italic_ or _bold_, then:
|
/// If the class was one of _italic_ or _bold_, then:
|
||||||
/// - If it was not present, the _regular_ class will be removed.
|
/// - If it was not present, the _regular_ class will be removed.
|
||||||
/// - If it was present, the _regular_ class will be added in case the
|
/// - If it was present, the _regular_ class will be added in case the other
|
||||||
/// other style class is not present.
|
/// style class is not present.
|
||||||
pub fn toggle_class(&mut self, class: FontClass) {
|
pub fn toggle_class(&mut self, class: FontClass) {
|
||||||
if self.classes.contains(&class) {
|
if self.classes.contains(&class) {
|
||||||
self.classes.retain(|x| x != &class);
|
self.classes.retain(|x| x != &class);
|
||||||
if (class == FontClass::Italic && !self.classes.contains(&FontClass::Bold))
|
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);
|
self.classes.push(FontClass::Regular);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,24 +6,26 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
use crate::func::Function;
|
use crate::func::Function;
|
||||||
use crate::size::Size;
|
use crate::size::Size;
|
||||||
|
|
||||||
|
|
||||||
/// A logical unit of the incoming text stream.
|
/// A logical unit of the incoming text stream.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum Token<'s> {
|
pub enum Token<'s> {
|
||||||
/// One or more whitespace (non-newline) codepoints.
|
/// One or more whitespace (non-newline) codepoints.
|
||||||
Space,
|
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,
|
Newline,
|
||||||
/// A left bracket: `[`.
|
/// A left bracket: `[`.
|
||||||
LeftBracket,
|
LeftBracket,
|
||||||
/// A right bracket: `]`.
|
/// A right bracket: `]`.
|
||||||
RightBracket,
|
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
|
/// If a colon occurs outside of a function header, it will be tokenized as
|
||||||
/// [Word](Token::Word).
|
/// a [Word](Token::Word).
|
||||||
Colon,
|
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,
|
Equals,
|
||||||
/// A comma (`,`) separating two function arguments (Function header only).
|
/// A comma (`,`) separating two function arguments (Function header only).
|
||||||
Comma,
|
Comma,
|
||||||
@ -39,8 +41,9 @@ pub enum Token<'s> {
|
|||||||
LineComment(&'s str),
|
LineComment(&'s str),
|
||||||
/// A block comment.
|
/// A block comment.
|
||||||
BlockComment(&'s str),
|
BlockComment(&'s str),
|
||||||
/// A star followed by a slash unexpectedly ending a block comment (the comment was not started
|
/// A star followed by a slash unexpectedly ending a block comment (the
|
||||||
/// before, otherwise a [BlockComment](Token::BlockComment) would be returned).
|
/// comment was not started before, otherwise a
|
||||||
|
/// [BlockComment](Token::BlockComment) would be returned).
|
||||||
StarSlash,
|
StarSlash,
|
||||||
/// Everything else is just text.
|
/// Everything else is just text.
|
||||||
Text(&'s str),
|
Text(&'s str),
|
||||||
@ -98,7 +101,7 @@ impl PartialEq for FuncCall {
|
|||||||
pub struct FuncHeader {
|
pub struct FuncHeader {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub args: Vec<Expression>,
|
pub args: Vec<Expression>,
|
||||||
pub kwargs: HashMap<String, Expression>
|
pub kwargs: HashMap<String, Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A value expression.
|
/// A value expression.
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{Write, Read, BufWriter};
|
use std::io::{BufWriter, Read, Write};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use typst::Typesetter;
|
use typst::export::pdf::PdfExporter;
|
||||||
use typst::layout::LayoutAction;
|
use typst::layout::LayoutAction;
|
||||||
use typst::toddle::query::FileSystemFontProvider;
|
use typst::toddle::query::FileSystemFontProvider;
|
||||||
use typst::export::pdf::PdfExporter;
|
use typst::Typesetter;
|
||||||
|
|
||||||
const CACHE_DIR: &str = "test-cache";
|
const CACHE_DIR: &str = "test-cache";
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut perfect_match = false;
|
let mut perfect_match = false;
|
||||||
let mut filter = Vec::new();
|
let mut filter = Vec::new();
|
||||||
@ -31,9 +30,7 @@ fn main() {
|
|||||||
for entry in fs::read_dir("tests/layouts/").unwrap() {
|
for entry in fs::read_dir("tests/layouts/").unwrap() {
|
||||||
let path = entry.unwrap().path();
|
let path = entry.unwrap().path();
|
||||||
|
|
||||||
let name = path
|
let name = path.file_stem().unwrap().to_str().unwrap();
|
||||||
.file_stem().unwrap()
|
|
||||||
.to_str().unwrap();
|
|
||||||
|
|
||||||
let matches = if perfect_match {
|
let matches = if perfect_match {
|
||||||
filter.iter().any(|pattern| name == pattern)
|
filter.iter().any(|pattern| name == pattern)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user