Upgrade to new toddle interface 🐳

This commit is contained in:
Laurenz 2020-02-03 10:37:50 +01:00
parent 20fb4e7c37
commit 40ea35cbe7
15 changed files with 157 additions and 138 deletions

View File

@ -6,7 +6,7 @@ edition = "2018"
build = "build.rs" build = "build.rs"
[dependencies] [dependencies]
toddle = { path = "../toddle", default-features = false } toddle = { path = "../toddle", features = ["query"], default-features = false }
tide = { path = "../tide" } tide = { path = "../tide" }
byteorder = "1" byteorder = "1"
smallvec = "1" smallvec = "1"

View File

@ -3,9 +3,9 @@ use std::io::BufWriter;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use futures_executor::block_on; use futures_executor::block_on;
use typstc::Typesetter; use typstc::{Typesetter, DynErrorProvider};
use typstc::toddle::query::FileSystemFontProvider; use typstc::toddle::query::fs::EagerFsProvider;
use typstc::export::pdf::PdfExporter; use typstc::export::pdf;
fn main() { fn main() {
@ -37,15 +37,14 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
let src = read_to_string(source) let src = read_to_string(source)
.map_err(|_| "failed to read from source file")?; .map_err(|_| "failed to read from source file")?;
let mut typesetter = Typesetter::new(); let (fs, entries) = EagerFsProvider::from_index("../fonts", "index.json")?;
let provider = FileSystemFontProvider::from_index("../fonts/index.json").unwrap(); let provider = DynErrorProvider::new(fs);
typesetter.add_font_provider(provider); let typesetter = Typesetter::new((Box::new(provider), entries));
let layouts = block_on(typesetter.typeset(&src)); let layouts = block_on(typesetter.typeset(&src));
let exporter = PdfExporter::new();
let writer = BufWriter::new(File::create(&dest)?); let writer = BufWriter::new(File::create(&dest)?);
exporter.export(&layouts, typesetter.loader(), writer)?; pdf::export(&layouts, typesetter.loader(), writer)?;
Ok(()) Ok(())
} }

View File

@ -11,45 +11,35 @@ use tide::font::{
CMap, CMapEncoding, FontStream, GlyphUnit, WidthRecord, CMap, CMapEncoding, FontStream, GlyphUnit, WidthRecord,
}; };
use toddle::Error as FontError; use toddle::{Font, OwnedFont, LoadError};
use toddle::font::OwnedFont; use toddle::types::Tag;
use toddle::query::{SharedFontLoader, FontIndex}; use toddle::query::FontIndex;
use toddle::tables::{ use toddle::tables::{
CharMap, Header, HorizontalMetrics, MacStyleFlags, CharMap, Header, HorizontalMetrics, MacStyleFlags,
Name, NameEntry, Post, OS2, Name, NameEntry, Post, OS2,
}; };
use crate::GlobalFontLoader;
use crate::layout::{MultiLayout, Layout, LayoutAction}; use crate::layout::{MultiLayout, Layout, LayoutAction};
use crate::size::Size; use crate::size::Size;
/// Exports layouts into _PDFs_. /// Export a layouted list of boxes. The same font loader as used for
pub struct PdfExporter {} /// layouting needs to be passed in here since the layout only contains
/// indices referencing the loaded fonts. The raw PDF ist written into the
impl PdfExporter { /// target writable, returning the number of bytes written.
/// Create a new exporter. pub fn export<W: Write>(
pub fn new() -> PdfExporter { layout: &MultiLayout,
PdfExporter {} loader: &GlobalFontLoader,
} target: W,
) -> PdfResult<usize> {
/// Export a layouted list of boxes. The same font loader as used for PdfExporter::new(layout, loader, target)?.write()
/// layouting needs to be passed in here since the layout only contains
/// indices referencing the loaded fonts. The raw PDF ist written into the
/// target writable, returning the number of bytes written.
pub fn export<W: Write>(
&self,
layout: &MultiLayout,
loader: &SharedFontLoader,
target: W,
) -> PdfResult<usize> {
ExportProcess::new(layout, loader, target)?.write()
}
} }
/// The data relevant to the export of one document. /// The data relevant to the export of one document.
struct ExportProcess<'d, W: Write> { struct PdfExporter<'a, W: Write> {
writer: PdfWriter<W>, writer: PdfWriter<W>,
layouts: &'d MultiLayout, layouts: &'a MultiLayout,
/// Since we cross-reference pages and fonts with their IDs already in the document /// Since we cross-reference pages and fonts with their IDs already in the document
/// catalog, we need to know exactly which ID is used for what from the beginning. /// catalog, we need to know exactly which ID is used for what from the beginning.
@ -76,18 +66,18 @@ struct Offsets {
fonts: (Ref, Ref), fonts: (Ref, Ref),
} }
impl<'d, W: Write> ExportProcess<'d, W> { impl<'a, W: Write> PdfExporter<'a, W> {
/// Prepare the export. Only once [`ExportProcess::write`] is called the /// Prepare the export. Only once [`ExportProcess::write`] is called the
/// writing really happens. /// writing really happens.
fn new( fn new(
layouts: &'d MultiLayout, layouts: &'a MultiLayout,
font_loader: &SharedFontLoader, font_loader: &GlobalFontLoader,
target: W, target: W,
) -> PdfResult<ExportProcess<'d, W>> { ) -> PdfResult<PdfExporter<'a, W>> {
let (fonts, font_remap) = Self::subset_fonts(layouts, font_loader)?; let (fonts, font_remap) = Self::subset_fonts(layouts, font_loader)?;
let offsets = Self::calculate_offsets(layouts.len(), fonts.len()); let offsets = Self::calculate_offsets(layouts.len(), fonts.len());
Ok(ExportProcess { Ok(PdfExporter {
writer: PdfWriter::new(target), writer: PdfWriter::new(target),
layouts, layouts,
offsets, offsets,
@ -101,8 +91,8 @@ impl<'d, W: Write> ExportProcess<'d, W> {
/// one used in the PDF. The new ones index into the returned vector of /// one used in the PDF. The new ones index into the returned vector of
/// owned fonts. /// owned fonts.
fn subset_fonts( fn subset_fonts(
layouts: &'d MultiLayout, layouts: &'a MultiLayout,
font_loader: &SharedFontLoader font_loader: &GlobalFontLoader,
) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> { ) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> {
let mut fonts = Vec::new(); let mut fonts = Vec::new();
let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new(); let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new();
@ -144,18 +134,22 @@ impl<'d, W: Write> ExportProcess<'d, W> {
let mut font_loader = font_loader.borrow_mut(); let mut font_loader = font_loader.borrow_mut();
// All tables not listed here are dropped. // All tables not listed here are dropped.
const SUBSET_TABLES: [&str; 13] = [ let tables: Vec<_> = [
"name", "OS/2", "post", "head", "hhea", "hmtx", "maxp", b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp",
"cmap", "cvt ", "fpgm", "prep", "loca", "glyf", b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf",
]; ].iter().map(|&s| Tag(*s)).collect();
// Do the subsetting. // Do the subsetting.
for index in 0 .. num_fonts { for index in 0 .. num_fonts {
let old_index = new_to_old[&index]; let old_index = new_to_old[&index];
let font = font_loader.get_with_index(old_index); let font = font_loader.get_with_index(old_index);
let subsetted = font.subsetted(font_chars[&old_index].iter().cloned(), &SUBSET_TABLES)
.map(|bytes| OwnedFont::from_bytes(bytes)) let chars = font_chars[&old_index].iter().cloned();
.unwrap_or_else(|_| font.to_owned())?; let subsetted = match font.subsetted(chars, tables.iter().copied()) {
Ok(data) => Font::from_bytes(data)?,
Err(_) => font.clone(),
};
fonts.push(subsetted); fonts.push(subsetted);
} }
@ -302,7 +296,7 @@ impl<'d, W: Write> ExportProcess<'d, W> {
id, id,
Type0Font::new( Type0Font::new(
base_font.clone(), base_font.clone(),
CMapEncoding::Predefined("Identity-H".to_owned()), CMapEncoding::Predefined("Identity-H".to_string()),
id + 1, id + 1,
) )
.to_unicode(id + 3), .to_unicode(id + 3),
@ -409,7 +403,7 @@ fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item = Ref> {
/// The error type for _PDF_ exporting. /// The error type for _PDF_ exporting.
pub enum PdfExportError { pub enum PdfExportError {
/// An error occured while subsetting the font for the _PDF_. /// An error occured while subsetting the font for the _PDF_.
Font(FontError), Font(LoadError),
/// An I/O Error on the underlying writable. /// An I/O Error on the underlying writable.
Io(io::Error), Io(io::Error),
} }
@ -418,13 +412,13 @@ error_type! {
self: PdfExportError, self: PdfExportError,
res: PdfResult, res: PdfResult,
show: f => match self { show: f => match self {
PdfExportError::Font(err) => write!(f, "font error: {}", err), PdfExportError::Font(err) => err.fmt(f),
PdfExportError::Io(err) => write!(f, "io error: {}", err), PdfExportError::Io(err) => err.fmt(f),
}, },
source: match self { source: match self {
PdfExportError::Font(err) => Some(err), PdfExportError::Font(err) => Some(err),
PdfExportError::Io(err) => Some(err), PdfExportError::Io(err) => Some(err),
}, },
from: (err: io::Error, PdfExportError::Io(err)), from: (err: io::Error, PdfExportError::Io(err)),
from: (err: FontError, PdfExportError::Font(err)), from: (err: LoadError, PdfExportError::Font(err)),
} }

View File

@ -145,15 +145,14 @@ macro_rules! function {
(@layout($name:ident) layout($this:ident, $ctx:ident, $errors:ident) $code:block) => { (@layout($name:ident) layout($this:ident, $ctx:ident, $errors:ident) $code:block) => {
impl $crate::syntax::Model for $name { impl $crate::syntax::Model for $name {
fn layout<'a, 'b, 'c, 't>( fn layout<'a, 'b, 't>(
#[allow(unused)] &'a $this, #[allow(unused)] &'a $this,
#[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b, 'c>, #[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b>,
) -> $crate::layout::DynFuture<'t, $crate::layout::Layouted< ) -> $crate::layout::DynFuture<'t, $crate::layout::Layouted<
$crate::layout::Commands<'a>> $crate::layout::Commands<'a>>
> where > where
'a: 't, 'a: 't,
'b: 't, 'b: 't,
'c: 't,
Self: 't, Self: 't,
{ {
Box::pin(async move { Box::pin(async move {

View File

@ -5,8 +5,9 @@
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use smallvec::smallvec; use smallvec::smallvec;
use toddle::query::SharedFontLoader; use toddle::query::{SharedFontLoader, FontProvider};
use crate::GlobalFontLoader;
use crate::error::Errors; use crate::error::Errors;
use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::size::{Size, Size2D}; use crate::size::{Size, Size2D};
@ -18,8 +19,8 @@ use super::*;
/// Performs the model layouting. /// Performs the model layouting.
pub struct ModelLayouter<'a, 'p> { pub struct ModelLayouter<'a> {
ctx: LayoutContext<'a, 'p>, ctx: LayoutContext<'a>,
layouter: LineLayouter, layouter: LineLayouter,
style: LayoutStyle, style: LayoutStyle,
errors: Errors, errors: Errors,
@ -27,10 +28,10 @@ pub struct ModelLayouter<'a, 'p> {
/// The context for layouting. /// The context for layouting.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LayoutContext<'a, 'p> { pub struct LayoutContext<'a> {
/// The font loader to retrieve fonts from when typesetting text /// The font loader to retrieve fonts from when typesetting text
/// using [`layout_text`]. /// using [`layout_text`].
pub loader: &'a SharedFontLoader<'p>, pub loader: &'a GlobalFontLoader,
/// The style for pages and text. /// The style for pages and text.
pub style: &'a LayoutStyle, pub style: &'a LayoutStyle,
/// The base unpadded dimensions of this container (for relative sizing). /// The base unpadded dimensions of this container (for relative sizing).
@ -105,7 +106,7 @@ pub enum Command<'a> {
} }
/// Layout a syntax model into a list of boxes. /// Layout a syntax model into a list of boxes.
pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_, '_>) -> Layouted<MultiLayout> { pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_>) -> Layouted<MultiLayout> {
let mut layouter = ModelLayouter::new(ctx); let mut layouter = ModelLayouter::new(ctx);
layouter.layout_syntax_model(model).await; layouter.layout_syntax_model(model).await;
layouter.finish() layouter.finish()
@ -116,9 +117,9 @@ pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_, '_>) -> Layouted
/// work internally. /// work internally.
pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output=T> + 'a>>; pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output=T> + 'a>>;
impl<'a, 'p> ModelLayouter<'a, 'p> { impl<'a> ModelLayouter<'a> {
/// Create a new model layouter. /// Create a new model layouter.
pub fn new(ctx: LayoutContext<'a, 'p>) -> ModelLayouter<'a, 'p> { pub fn new(ctx: LayoutContext<'a>) -> ModelLayouter<'a> {
ModelLayouter { ModelLayouter {
layouter: LineLayouter::new(LineContext { layouter: LineLayouter::new(LineContext {
spaces: ctx.spaces.clone(), spaces: ctx.spaces.clone(),
@ -182,6 +183,7 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
Some("monospace") => { list.remove(0); }, Some("monospace") => { list.remove(0); },
_ => list.insert(0, "monospace".to_string()), _ => list.insert(0, "monospace".to_string()),
} }
self.style.text.fallback.flatten();
} }
Node::Model(model) => { Node::Model(model) => {

View File

@ -4,17 +4,18 @@
//! When the primary layouting axis horizontally inversed, the word is spelled //! When the primary layouting axis horizontally inversed, the word is spelled
//! backwards. Vertical word layout is not yet supported. //! backwards. Vertical word layout is not yet supported.
use toddle::query::{SharedFontLoader, FontQuery, FontIndex}; use toddle::query::{FontQuery, FontIndex};
use toddle::tables::{CharMap, Header, HorizontalMetrics}; use toddle::tables::{CharMap, Header, HorizontalMetrics};
use crate::GlobalFontLoader;
use crate::size::{Size, Size2D}; use crate::size::{Size, Size2D};
use crate::style::TextStyle; use crate::style::TextStyle;
use super::*; use super::*;
/// Performs the text layouting. /// Performs the text layouting.
struct TextLayouter<'a, 'p> { struct TextLayouter<'a> {
ctx: TextContext<'a, 'p>, ctx: TextContext<'a>,
text: &'a str, text: &'a str,
actions: LayoutActions, actions: LayoutActions,
buffer: String, buffer: String,
@ -24,10 +25,10 @@ struct TextLayouter<'a, 'p> {
/// The context for text layouting. /// The context for text layouting.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct TextContext<'a, 'p> { pub struct TextContext<'a> {
/// The font loader to retrieve fonts from when typesetting text /// The font loader to retrieve fonts from when typesetting text
/// using [`layout_text`]. /// using [`layout_text`].
pub loader: &'a SharedFontLoader<'p>, pub loader: &'a GlobalFontLoader,
/// The style for text: Font selection with classes, weights and variants, /// The style for text: Font selection with classes, weights and variants,
/// font sizes, spacing and so on. /// font sizes, spacing and so on.
pub style: &'a TextStyle, pub style: &'a TextStyle,
@ -39,13 +40,13 @@ pub struct TextContext<'a, 'p> {
} }
/// Layouts text into a box. /// Layouts text into a box.
pub async fn layout_text(text: &str, ctx: TextContext<'_, '_>) -> Layout { pub async fn layout_text(text: &str, ctx: TextContext<'_>) -> Layout {
TextLayouter::new(text, ctx).layout().await TextLayouter::new(text, ctx).layout().await
} }
impl<'a, 'p> TextLayouter<'a, 'p> { impl<'a> TextLayouter<'a> {
/// Create a new text layouter. /// Create a new text layouter.
fn new(text: &'a str, ctx: TextContext<'a, 'p>) -> TextLayouter<'a, 'p> { fn new(text: &'a str, ctx: TextContext<'a>) -> TextLayouter<'a> {
TextLayouter { TextLayouter {
ctx, ctx,
text, text,

View File

@ -21,8 +21,14 @@
pub use toddle; pub use toddle;
use std::cell::RefCell; use std::cell::RefCell;
use std::error::Error;
use std::fmt::{self, Debug, Formatter};
use std::io::Cursor;
use async_trait::async_trait;
use smallvec::smallvec; use smallvec::smallvec;
use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
use toddle::{Font, OwnedData};
use toddle::query::{FontLoader, FontProvider, SharedFontLoader, FontDescriptor};
use crate::layout::{Layouted, MultiLayout}; use crate::layout::{Layouted, MultiLayout};
use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::style::{LayoutStyle, PageStyle, TextStyle};
@ -45,20 +51,29 @@ pub mod syntax;
/// Transforms source code into typesetted layouts. /// Transforms source code into typesetted layouts.
/// ///
/// A typesetter can be configured through various methods. /// A typesetter can be configured through various methods.
pub struct Typesetter<'p> { pub struct Typesetter {
/// The font loader shared by all typesetting processes. /// The font loader shared by all typesetting processes.
loader: SharedFontLoader<'p>, loader: GlobalFontLoader,
/// The base layouting style. /// The base layouting style.
style: LayoutStyle, style: LayoutStyle,
/// The standard library scope. /// The standard library scope.
scope: Scope, scope: Scope,
} }
impl<'p> Typesetter<'p> { /// The font loader type used in the [`Typesetter`].
///
/// This font loader is ref-cell protected and backed by a dynamic font
/// provider.
pub type GlobalFontLoader = SharedFontLoader<GlobalProvider>;
/// The provider type of font loaders used in the [`Typesetter`].
pub type GlobalProvider = Box<dyn FontProvider<Data=OwnedData, Error=Box<dyn Error>>>;
impl Typesetter {
/// Create a new typesetter. /// Create a new typesetter.
pub fn new() -> Typesetter<'p> { pub fn new(provider: (GlobalProvider, Vec<FontDescriptor>)) -> Typesetter {
Typesetter { Typesetter {
loader: RefCell::new(FontLoader::new()), loader: RefCell::new(FontLoader::new(provider)),
style: LayoutStyle::default(), style: LayoutStyle::default(),
scope: Scope::with_std(), scope: Scope::with_std(),
} }
@ -74,14 +89,8 @@ impl<'p> Typesetter<'p> {
self.style.text = style; self.style.text = style;
} }
/// Add a font provider to the context of this typesetter.
pub fn add_font_provider<P: 'p>(&mut self, provider: P)
where P: FontProvider {
self.loader.get_mut().add_provider(provider);
}
/// A reference to the backing font loader. /// A reference to the backing font loader.
pub fn loader(&self) -> &SharedFontLoader<'p> { pub fn loader(&self) -> &GlobalFontLoader {
&self.loader &self.loader
} }
@ -121,3 +130,29 @@ impl<'p> Typesetter<'p> {
self.layout(&tree).await.output self.layout(&tree).await.output
} }
} }
/// Wraps a font provider and transforms its errors into boxed trait objects.
/// This enables font providers that do not return boxed errors to be used with
/// the typesetter.
pub struct DynErrorProvider<P> {
provider: P,
}
impl<P> DynErrorProvider<P>
where P: FontProvider, P::Error: Error + 'static {
/// Create a new dynamic error provider from any provider.
pub fn new(provider: P) -> DynErrorProvider<P> {
DynErrorProvider { provider }
}
}
#[async_trait(?Send)]
impl<P> FontProvider for DynErrorProvider<P>
where P: FontProvider, P::Error: Error + 'static {
type Data = P::Data;
type Error = Box<dyn Error>;
async fn load(&self, index: usize, variant: usize) -> Result<Font<P::Data>, Self::Error> {
Ok(self.provider.load(index, variant).await?)
}
}

View File

@ -20,7 +20,10 @@ function! {
layout(self, ctx, errors) { layout(self, ctx, errors) {
styled(&self.body, ctx, Some(&self.list), styled(&self.body, ctx, Some(&self.list),
|s, l| s.fallback.list = l.clone()) |s, list| {
s.fallback.list = list.clone();
s.fallback.flatten();
})
} }
} }

View File

@ -1,5 +1,6 @@
//! The _Typst_ standard library. //! The _Typst_ standard library.
use toddle::query::FontProvider;
use crate::syntax::Scope; use crate::syntax::Scope;
use crate::func::prelude::*; use crate::func::prelude::*;
@ -68,7 +69,7 @@ function! {
/// Layout an optional body with a change of the text style. /// Layout an optional body with a change of the text style.
fn styled<'a, T, F>( fn styled<'a, T, F>(
body: &'a Option<SyntaxModel>, body: &'a Option<SyntaxModel>,
ctx: LayoutContext, ctx: LayoutContext<'_>,
data: Option<T>, data: Option<T>,
f: F, f: F,
) -> Commands<'a> where F: FnOnce(&mut TextStyle, T) { ) -> Commands<'a> where F: FnOnce(&mut TextStyle, T) {

View File

@ -1,6 +1,7 @@
//! Styles for text and pages. //! Styles for text and pages.
use toddle::query::{FontFallbackTree, FontVariant, FontStyle, FontWeight}; use toddle::fallback;
use toddle::query::{FallbackTree, FontVariant, FontStyle, FontWeight};
use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize}; use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize};
@ -17,7 +18,7 @@ pub struct LayoutStyle {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TextStyle { pub struct TextStyle {
/// A tree of font names and generic family names. /// A tree of font names and generic family names.
pub fallback: FontFallbackTree, pub fallback: FallbackTree,
/// The selected font variant. /// The selected font variant.
pub variant: FontVariant, pub variant: FontVariant,
/// Whether the bolder toggle is active or inactive. This determines /// Whether the bolder toggle is active or inactive. This determines
@ -57,38 +58,19 @@ impl TextStyle {
} }
} }
macro_rules! fallback {
(
list: ($($f:expr),*),
classes: { $($c:expr => ($($cf:expr),*),)* },
base: ($($b:expr),*),
) => ({
let mut fallback = FontFallbackTree::new(
vec![$($f.to_string()),*],
vec![$($b.to_string()),*],
);
$(
fallback.set_class_list($c.to_string(), vec![$($cf.to_string()),*])
.expect("TextStyle::default: unexpected error \
when setting class list");
)*
fallback
});
}
impl Default for TextStyle { impl Default for TextStyle {
fn default() -> TextStyle { fn default() -> TextStyle {
TextStyle { TextStyle {
fallback: fallback! { fallback: fallback! {
list: ("sans-serif"), list: ["sans-serif"],
classes: { classes: {
"serif" => ("source serif pro", "noto serif"), "serif" => ["source serif pro", "noto serif"],
"sans-serif" => ("source sans pro", "noto sans"), "sans-serif" => ["source sans pro", "noto sans"],
"monospace" => ("source code pro", "noto sans mono"), "monospace" => ["source code pro", "noto sans mono"],
"math" => ("latin modern math", "serif"), "math" => ["latin modern math", "serif"],
}, },
base: ("source sans pro", "noto sans", base: ["source sans pro", "noto sans",
"noto emoji", "latin modern math"), "noto emoji", "latin modern math"],
}, },
variant: FontVariant { variant: FontVariant {
style: FontStyle::Normal, style: FontStyle::Normal,
@ -157,7 +139,7 @@ pub struct Paper {
impl Paper { impl Paper {
/// The paper with the given name. /// The paper with the given name.
pub fn from_str(name: &str) -> Option<Paper> { pub fn from_name(name: &str) -> Option<Paper> {
parse_paper(name) parse_paper(name)
} }
} }

View File

@ -145,7 +145,7 @@ impl Value for FontStyle {
type Output = Self; type Output = Self;
fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> {
FontStyle::from_str(Ident::parse(expr)?.as_str()) FontStyle::from_name(Ident::parse(expr)?.as_str())
.ok_or_else(|| err!("invalid font style")) .ok_or_else(|| err!("invalid font style"))
} }
} }
@ -166,7 +166,7 @@ impl Value for FontWeight {
} }
} }
Expr::Ident(id) => { Expr::Ident(id) => {
FontWeight::from_str(id.as_str()) FontWeight::from_name(id.as_str())
.ok_or_else(|| err!("invalid font weight")) .ok_or_else(|| err!("invalid font weight"))
.map(|weight| (weight, false)) .map(|weight| (weight, false))
} }
@ -180,7 +180,7 @@ impl Value for Paper {
type Output = Self; type Output = Self;
fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> {
Paper::from_str(Ident::parse(expr)?.as_str()) Paper::from_name(Ident::parse(expr)?.as_str())
.ok_or_else(|| err!("invalid paper type")) .ok_or_else(|| err!("invalid paper type"))
} }
} }

View File

@ -23,7 +23,10 @@ pub_use_mod!(tokens);
pub trait Model: Debug + ModelBounds { pub trait Model: Debug + ModelBounds {
/// Layout the model into a sequence of commands processed by a /// Layout the model into a sequence of commands processed by a
/// [`ModelLayouter`](crate::layout::ModelLayouter). /// [`ModelLayouter`](crate::layout::ModelLayouter).
async fn layout<'a>(&'a self, ctx: LayoutContext<'_, '_>) -> Layouted<Commands<'a>>; async fn layout<'a>(
&'a self,
ctx: LayoutContext<'_>,
) -> Layouted<Commands<'a>>;
} }
/// A tree representation of source code. /// A tree representation of source code.
@ -47,7 +50,10 @@ impl SyntaxModel {
#[async_trait(?Send)] #[async_trait(?Send)]
impl Model for SyntaxModel { impl Model for SyntaxModel {
async fn layout<'a>(&'a self, _: LayoutContext<'_, '_>) -> Layouted<Commands<'a>> { async fn layout<'a>(
&'a self,
_: LayoutContext<'_>,
) -> Layouted<Commands<'a>> {
Layouted { Layouted {
output: vec![Command::LayoutSyntaxModel(self)], output: vec![Command::LayoutSyntaxModel(self)],
errors: vec![], errors: vec![],

View File

@ -81,7 +81,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode
Token::Star => Node::ToggleBolder, Token::Star => Node::ToggleBolder,
Token::Underscore => Node::ToggleItalic, Token::Underscore => Node::ToggleItalic,
Token::Backtick => Node::ToggleMonospace, Token::Backtick => Node::ToggleMonospace,
Token::Text(text) => Node::Text(text.to_owned()), Token::Text(text) => Node::Text(text.to_string()),
Token::LineComment(_) | Token::BlockComment(_) => continue, Token::LineComment(_) | Token::BlockComment(_) => continue,

View File

@ -43,7 +43,7 @@ impl Scope {
pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as ParseFunc>::Meta) pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as ParseFunc>::Meta)
where F: ParseFunc + Model + 'static { where F: ParseFunc + Model + 'static {
self.parsers.insert( self.parsers.insert(
name.to_owned(), name.to_string(),
parser::<F>(metadata), parser::<F>(metadata),
); );
} }

View File

@ -8,12 +8,12 @@ use std::process::Command;
use futures_executor::block_on; use futures_executor::block_on;
use typstc::Typesetter; use typstc::{Typesetter, DynErrorProvider};
use typstc::layout::{MultiLayout, Serialize}; use typstc::layout::{MultiLayout, Serialize};
use typstc::size::{Size, Size2D, ValueBox}; use typstc::size::{Size, Size2D, ValueBox};
use typstc::style::{PageStyle, PaperClass}; use typstc::style::{PageStyle, PaperClass};
use typstc::toddle::query::FileSystemFontProvider; use typstc::export::pdf;
use typstc::export::pdf::PdfExporter; use typstc::toddle::query::fs::EagerFsProvider;
type DynResult<T> = Result<T, Box<dyn Error>>; type DynResult<T> = Result<T, Box<dyn Error>>;
@ -66,17 +66,17 @@ fn main() -> DynResult<()> {
fn test(name: &str, src: &str) -> DynResult<()> { fn test(name: &str, src: &str) -> DynResult<()> {
println!("Testing: {}.", name); println!("Testing: {}.", name);
let mut typesetter = Typesetter::new(); let (fs, entries) = EagerFsProvider::from_index("../fonts", "index.json")?;
let paths = fs.paths();
let provider = DynErrorProvider::new(fs);
let mut typesetter = Typesetter::new((Box::new(provider), entries));
typesetter.set_page_style(PageStyle { typesetter.set_page_style(PageStyle {
class: PaperClass::Custom, class: PaperClass::Custom,
dimensions: Size2D::with_all(Size::pt(250.0)), dimensions: Size2D::with_all(Size::pt(250.0)),
margins: ValueBox::with_all(None), margins: ValueBox::with_all(None),
}); });
let provider = FileSystemFontProvider::from_index("../fonts/index.json")?;
let font_paths = provider.paths();
typesetter.add_font_provider(provider);
let layouts = compile(&typesetter, src); let layouts = compile(&typesetter, src);
// Compute the font's paths. // Compute the font's paths.
@ -84,10 +84,8 @@ fn test(name: &str, src: &str) -> DynResult<()> {
let loader = typesetter.loader().borrow(); let loader = typesetter.loader().borrow();
for layout in &layouts { for layout in &layouts {
for index in layout.find_used_fonts() { for index in layout.find_used_fonts() {
fonts.entry(index).or_insert_with(|| { fonts.entry(index)
let p = loader.get_provider_and_index(index.id).1; .or_insert_with(|| &paths[index.id][index.variant]);
&font_paths[p][index.variant]
});
} }
} }
drop(loader); drop(loader);
@ -113,8 +111,7 @@ fn test(name: &str, src: &str) -> DynResult<()> {
// Write the PDF file. // Write the PDF file.
let path = format!("tests/cache/pdf/{}.pdf", name); let path = format!("tests/cache/pdf/{}.pdf", name);
let file = BufWriter::new(File::create(path)?); let file = BufWriter::new(File::create(path)?);
let exporter = PdfExporter::new(); pdf::export(&layouts, typesetter.loader(), file)?;
exporter.export(&layouts, typesetter.loader(), file)?;
Ok(()) Ok(())
} }