mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Better Debug/Display and Derives 🧽
This commit is contained in:
parent
40ea35cbe7
commit
3150fd5643
@ -21,7 +21,7 @@ pub struct Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// How severe / important an error is.
|
/// How severe / important an error is.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
||||||
pub enum Severity {
|
pub enum Severity {
|
||||||
/// Something in the code is not good.
|
/// Something in the code is not good.
|
||||||
Warning,
|
Warning,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
//! Exporting of layouts into _PDF_ documents.
|
//! Exporting of layouts into _PDF_ documents.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use tide::{PdfWriter, Rect, Ref, Trailer, Version};
|
use tide::{PdfWriter, Rect, Ref, Trailer, Version};
|
||||||
@ -74,8 +76,8 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
font_loader: &GlobalFontLoader,
|
font_loader: &GlobalFontLoader,
|
||||||
target: W,
|
target: W,
|
||||||
) -> PdfResult<PdfExporter<'a, W>> {
|
) -> PdfResult<PdfExporter<'a, W>> {
|
||||||
let (fonts, font_remap) = Self::subset_fonts(layouts, font_loader)?;
|
let (fonts, font_remap) = subset_fonts(layouts, font_loader)?;
|
||||||
let offsets = Self::calculate_offsets(layouts.len(), fonts.len());
|
let offsets = calculate_offsets(layouts.len(), fonts.len());
|
||||||
|
|
||||||
Ok(PdfExporter {
|
Ok(PdfExporter {
|
||||||
writer: PdfWriter::new(target),
|
writer: PdfWriter::new(target),
|
||||||
@ -86,95 +88,6 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subsets all fonts and assign a new PDF-internal index to each one. The
|
|
||||||
/// returned hash map maps the old indices (used by the layouts) to the new
|
|
||||||
/// one used in the PDF. The new ones index into the returned vector of
|
|
||||||
/// owned fonts.
|
|
||||||
fn subset_fonts(
|
|
||||||
layouts: &'a MultiLayout,
|
|
||||||
font_loader: &GlobalFontLoader,
|
|
||||||
) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> {
|
|
||||||
let mut fonts = Vec::new();
|
|
||||||
let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new();
|
|
||||||
let mut old_to_new: HashMap<FontIndex, usize> = HashMap::new();
|
|
||||||
let mut new_to_old: HashMap<usize, FontIndex> = HashMap::new();
|
|
||||||
let mut active_font = FontIndex::MAX;
|
|
||||||
|
|
||||||
// We want to find out which fonts are used at all and which chars are
|
|
||||||
// used for those. We use this information to create subsetted fonts.
|
|
||||||
for layout in layouts {
|
|
||||||
for action in &layout.actions {
|
|
||||||
match action {
|
|
||||||
LayoutAction::WriteText(text) => {
|
|
||||||
font_chars
|
|
||||||
.entry(active_font)
|
|
||||||
.or_insert_with(HashSet::new)
|
|
||||||
.extend(text.chars());
|
|
||||||
},
|
|
||||||
|
|
||||||
LayoutAction::SetFont(index, _) => {
|
|
||||||
active_font = *index;
|
|
||||||
|
|
||||||
let next_id = old_to_new.len();
|
|
||||||
let new_id = *old_to_new
|
|
||||||
.entry(active_font)
|
|
||||||
.or_insert(next_id);
|
|
||||||
|
|
||||||
new_to_old
|
|
||||||
.entry(new_id)
|
|
||||||
.or_insert(active_font);
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_fonts = old_to_new.len();
|
|
||||||
let mut font_loader = font_loader.borrow_mut();
|
|
||||||
|
|
||||||
// All tables not listed here are dropped.
|
|
||||||
let tables: Vec<_> = [
|
|
||||||
b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp",
|
|
||||||
b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf",
|
|
||||||
].iter().map(|&s| Tag(*s)).collect();
|
|
||||||
|
|
||||||
// Do the subsetting.
|
|
||||||
for index in 0 .. num_fonts {
|
|
||||||
let old_index = new_to_old[&index];
|
|
||||||
let font = font_loader.get_with_index(old_index);
|
|
||||||
|
|
||||||
let chars = font_chars[&old_index].iter().cloned();
|
|
||||||
let subsetted = match font.subsetted(chars, tables.iter().copied()) {
|
|
||||||
Ok(data) => Font::from_bytes(data)?,
|
|
||||||
Err(_) => font.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
fonts.push(subsetted);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((fonts, old_to_new))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We need to know in advance which IDs to use for which objects to
|
|
||||||
/// cross-reference them. Therefore, we calculate the indices in the
|
|
||||||
/// beginning.
|
|
||||||
fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
|
|
||||||
let catalog = 1;
|
|
||||||
let page_tree = catalog + 1;
|
|
||||||
let pages = (page_tree + 1, page_tree + layout_count as Ref);
|
|
||||||
let contents = (pages.1 + 1, pages.1 + layout_count as Ref);
|
|
||||||
let font_offsets = (contents.1 + 1, contents.1 + 5 * font_count as Ref);
|
|
||||||
|
|
||||||
Offsets {
|
|
||||||
catalog,
|
|
||||||
page_tree,
|
|
||||||
pages,
|
|
||||||
contents,
|
|
||||||
fonts: font_offsets,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write everything (writing entry point).
|
/// Write everything (writing entry point).
|
||||||
fn write(&mut self) -> PdfResult<usize> {
|
fn write(&mut self) -> PdfResult<usize> {
|
||||||
self.writer.write_header(Version::new(1, 7))?;
|
self.writer.write_header(Version::new(1, 7))?;
|
||||||
@ -395,12 +308,100 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Subsets all fonts and assign a new PDF-internal index to each one. The
|
||||||
|
/// returned hash map maps the old indices (used by the layouts) to the new one
|
||||||
|
/// used in the PDF. The new ones index into the returned vector of owned fonts.
|
||||||
|
fn subset_fonts(
|
||||||
|
layouts: &MultiLayout,
|
||||||
|
font_loader: &GlobalFontLoader,
|
||||||
|
) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> {
|
||||||
|
let mut fonts = Vec::new();
|
||||||
|
let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new();
|
||||||
|
let mut old_to_new: HashMap<FontIndex, usize> = HashMap::new();
|
||||||
|
let mut new_to_old: HashMap<usize, FontIndex> = HashMap::new();
|
||||||
|
let mut active_font = FontIndex::MAX;
|
||||||
|
|
||||||
|
// We want to find out which fonts are used at all and which chars are used
|
||||||
|
// for those. We use this information to create subsetted fonts.
|
||||||
|
for layout in layouts {
|
||||||
|
for action in &layout.actions {
|
||||||
|
match action {
|
||||||
|
LayoutAction::WriteText(text) => {
|
||||||
|
font_chars
|
||||||
|
.entry(active_font)
|
||||||
|
.or_insert_with(HashSet::new)
|
||||||
|
.extend(text.chars());
|
||||||
|
},
|
||||||
|
|
||||||
|
LayoutAction::SetFont(index, _) => {
|
||||||
|
active_font = *index;
|
||||||
|
|
||||||
|
let next_id = old_to_new.len();
|
||||||
|
let new_id = *old_to_new
|
||||||
|
.entry(active_font)
|
||||||
|
.or_insert(next_id);
|
||||||
|
|
||||||
|
new_to_old
|
||||||
|
.entry(new_id)
|
||||||
|
.or_insert(active_font);
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_fonts = old_to_new.len();
|
||||||
|
let mut font_loader = font_loader.borrow_mut();
|
||||||
|
|
||||||
|
// All tables not listed here are dropped.
|
||||||
|
let tables: Vec<_> = [
|
||||||
|
b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp",
|
||||||
|
b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf",
|
||||||
|
].iter().map(|&s| Tag(*s)).collect();
|
||||||
|
|
||||||
|
// Do the subsetting.
|
||||||
|
for index in 0 .. num_fonts {
|
||||||
|
let old_index = new_to_old[&index];
|
||||||
|
let font = font_loader.get_with_index(old_index);
|
||||||
|
|
||||||
|
let chars = font_chars[&old_index].iter().cloned();
|
||||||
|
let subsetted = match font.subsetted(chars, tables.iter().copied()) {
|
||||||
|
Ok(data) => Font::from_bytes(data)?,
|
||||||
|
Err(_) => font.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
fonts.push(subsetted);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((fonts, old_to_new))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We need to know in advance which IDs to use for which objects to
|
||||||
|
/// cross-reference them. Therefore, we calculate the indices in the beginning.
|
||||||
|
fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
|
||||||
|
let catalog = 1;
|
||||||
|
let page_tree = catalog + 1;
|
||||||
|
let pages = (page_tree + 1, page_tree + layout_count as Ref);
|
||||||
|
let contents = (pages.1 + 1, pages.1 + layout_count as Ref);
|
||||||
|
let font_offsets = (contents.1 + 1, contents.1 + 5 * font_count as Ref);
|
||||||
|
|
||||||
|
Offsets {
|
||||||
|
catalog,
|
||||||
|
page_tree,
|
||||||
|
pages,
|
||||||
|
contents,
|
||||||
|
fonts: font_offsets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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_ exporting.
|
/// The error type for _PDF_ exporting.
|
||||||
|
#[derive(Debug)]
|
||||||
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(LoadError),
|
Font(LoadError),
|
||||||
@ -408,17 +409,34 @@ pub enum PdfExportError {
|
|||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
error_type! {
|
type PdfResult<T> = Result<T, PdfExportError>;
|
||||||
self: PdfExportError,
|
|
||||||
res: PdfResult,
|
impl Error for PdfExportError {
|
||||||
show: f => match self {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
PdfExportError::Font(err) => err.fmt(f),
|
match self {
|
||||||
PdfExportError::Io(err) => err.fmt(f),
|
PdfExportError::Font(err) => Some(err),
|
||||||
},
|
PdfExportError::Io(err) => Some(err),
|
||||||
source: match self {
|
}
|
||||||
PdfExportError::Font(err) => Some(err),
|
}
|
||||||
PdfExportError::Io(err) => Some(err),
|
}
|
||||||
},
|
|
||||||
from: (err: io::Error, PdfExportError::Io(err)),
|
impl Display for PdfExportError {
|
||||||
from: (err: LoadError, PdfExportError::Font(err)),
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
PdfExportError::Font(err) => err.fmt(f),
|
||||||
|
PdfExportError::Io(err) => err.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LoadError> for PdfExportError {
|
||||||
|
fn from(err: LoadError) -> PdfExportError {
|
||||||
|
PdfExportError::Font(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for PdfExportError {
|
||||||
|
fn from(err: io::Error) -> PdfExportError {
|
||||||
|
PdfExportError::Io(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Drawing and configuration actions composing layouts.
|
//! Drawing and configuration actions composing layouts.
|
||||||
|
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use toddle::query::FontIndex;
|
use toddle::query::FontIndex;
|
||||||
|
|
||||||
use crate::size::{Size, Size2D};
|
use crate::size::{Size, Size2D};
|
||||||
@ -11,7 +11,7 @@ use self::LayoutAction::*;
|
|||||||
|
|
||||||
/// A layouting action, which is the basic building block layouts are composed
|
/// A layouting action, which is the basic building block layouts are composed
|
||||||
/// of.
|
/// of.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum LayoutAction {
|
pub enum LayoutAction {
|
||||||
/// Move to an absolute position.
|
/// Move to an absolute position.
|
||||||
MoveAbsolute(Size2D),
|
MoveAbsolute(Size2D),
|
||||||
@ -34,20 +34,18 @@ impl Serialize for LayoutAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for LayoutAction {
|
impl Debug for LayoutAction {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
use LayoutAction::*;
|
use LayoutAction::*;
|
||||||
match self {
|
match self {
|
||||||
MoveAbsolute(s) => write!(f, "move {} {}", s.x, s.y),
|
MoveAbsolute(s) => write!(f, "move {} {}", s.x, s.y),
|
||||||
SetFont(i, s) => write!(f, "font {} {} {}", i.id, i.variant, s),
|
SetFont(i, s) => write!(f, "font {}_{} {}", i.id, i.variant, s),
|
||||||
WriteText(s) => write!(f, "write \"{}\"", s),
|
WriteText(s) => write!(f, "write \"{}\"", s),
|
||||||
DebugBox(s) => write!(f, "box {}", s),
|
DebugBox(s) => write!(f, "box {} {}", s.x, s.y),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_display!(LayoutAction);
|
|
||||||
|
|
||||||
/// A sequence of layouting actions.
|
/// A sequence of layouting actions.
|
||||||
///
|
///
|
||||||
/// The sequence of actions is optimized as the actions are added. For example,
|
/// The sequence of actions is optimized as the actions are added. For example,
|
||||||
@ -60,7 +58,7 @@ debug_display!(LayoutAction);
|
|||||||
/// `add_layout` method, which allows a layout to be added at a position,
|
/// `add_layout` method, which allows a layout to be added at a position,
|
||||||
/// effectively translating all movement actions inside the layout by the
|
/// effectively translating all movement actions inside the layout by the
|
||||||
/// position.
|
/// position.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct LayoutActions {
|
pub struct LayoutActions {
|
||||||
origin: Size2D,
|
origin: Size2D,
|
||||||
actions: Vec<LayoutAction>,
|
actions: Vec<LayoutAction>,
|
||||||
|
@ -13,7 +13,7 @@ use super::*;
|
|||||||
|
|
||||||
|
|
||||||
/// Performs the line layouting.
|
/// Performs the line layouting.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct LineLayouter {
|
pub struct LineLayouter {
|
||||||
/// The context for layouting.
|
/// The context for layouting.
|
||||||
ctx: LineContext,
|
ctx: LineContext,
|
||||||
@ -46,7 +46,7 @@ pub struct LineContext {
|
|||||||
/// A line run is a sequence of boxes with the same alignment that are arranged
|
/// A line run is a sequence of boxes with the same alignment that are arranged
|
||||||
/// in a line. A real line can consist of multiple runs with different
|
/// in a line. A real line can consist of multiple runs with different
|
||||||
/// alignments.
|
/// alignments.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
struct LineRun {
|
struct LineRun {
|
||||||
/// The so-far accumulated layouts in the line.
|
/// The so-far accumulated layouts in the line.
|
||||||
layouts: Vec<(Size, Layout)>,
|
layouts: Vec<(Size, Layout)>,
|
||||||
@ -102,7 +102,7 @@ impl LineLayouter {
|
|||||||
} else if layout.alignment.primary > alignment.primary {
|
} else if layout.alignment.primary > alignment.primary {
|
||||||
let mut rest_run = LineRun::new();
|
let mut rest_run = LineRun::new();
|
||||||
|
|
||||||
let usable = self.stack.usable().get_primary(axes);
|
let usable = self.stack.usable().primary(axes);
|
||||||
rest_run.usable = Some(match layout.alignment.primary {
|
rest_run.usable = Some(match layout.alignment.primary {
|
||||||
Alignment::Origin => unreachable!("origin > x"),
|
Alignment::Origin => unreachable!("origin > x"),
|
||||||
Alignment::Center => usable - 2 * self.run.size.x,
|
Alignment::Center => usable - 2 * self.run.size.x,
|
||||||
@ -228,7 +228,7 @@ impl LineLayouter {
|
|||||||
/// a function how much space it has to layout itself.
|
/// a function how much space it has to layout itself.
|
||||||
pub fn remaining(&self) -> LayoutSpaces {
|
pub fn remaining(&self) -> LayoutSpaces {
|
||||||
let mut spaces = self.stack.remaining();
|
let mut spaces = self.stack.remaining();
|
||||||
*spaces[0].dimensions.get_secondary_mut(self.ctx.axes)
|
*spaces[0].dimensions.secondary_mut(self.ctx.axes)
|
||||||
-= self.run.size.y;
|
-= self.run.size.y;
|
||||||
spaces
|
spaces
|
||||||
}
|
}
|
||||||
@ -262,7 +262,7 @@ impl LineLayouter {
|
|||||||
true => offset,
|
true => offset,
|
||||||
false => self.run.size.x
|
false => self.run.size.x
|
||||||
- offset
|
- offset
|
||||||
- layout.dimensions.get_primary(self.ctx.axes),
|
- layout.dimensions.primary(self.ctx.axes),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pos = Size2D::with_x(x);
|
let pos = Size2D::with_x(x);
|
||||||
|
@ -33,7 +33,7 @@ pub mod prelude {
|
|||||||
pub type MultiLayout = Vec<Layout>;
|
pub type MultiLayout = Vec<Layout>;
|
||||||
|
|
||||||
/// A finished box with content at fixed positions.
|
/// A finished box with content at fixed positions.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
/// The size of the box.
|
/// The size of the box.
|
||||||
pub dimensions: Size2D,
|
pub dimensions: Size2D,
|
||||||
@ -91,7 +91,7 @@ impl Serialize for MultiLayout {
|
|||||||
pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>;
|
pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>;
|
||||||
|
|
||||||
/// The space into which content is laid out.
|
/// The space into which content is laid out.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct LayoutSpace {
|
pub struct LayoutSpace {
|
||||||
/// The maximum size of the box to layout in.
|
/// The maximum size of the box to layout in.
|
||||||
pub dimensions: Size2D,
|
pub dimensions: Size2D,
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
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, FontProvider};
|
|
||||||
|
|
||||||
use crate::GlobalFontLoader;
|
use crate::GlobalFontLoader;
|
||||||
use crate::error::Errors;
|
use crate::error::Errors;
|
||||||
@ -19,6 +18,7 @@ use super::*;
|
|||||||
|
|
||||||
|
|
||||||
/// Performs the model layouting.
|
/// Performs the model layouting.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ModelLayouter<'a> {
|
pub struct ModelLayouter<'a> {
|
||||||
ctx: LayoutContext<'a>,
|
ctx: LayoutContext<'a>,
|
||||||
layouter: LineLayouter,
|
layouter: LineLayouter,
|
||||||
@ -52,6 +52,7 @@ pub struct LayoutContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The result of layouting: Some layouted things and a list of errors.
|
/// The result of layouting: Some layouted things and a list of errors.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Layouted<T> {
|
pub struct Layouted<T> {
|
||||||
/// The result of the layouting process.
|
/// The result of the layouting process.
|
||||||
pub output: T,
|
pub output: T,
|
||||||
@ -63,7 +64,7 @@ pub struct Layouted<T> {
|
|||||||
pub type Commands<'a> = Vec<Command<'a>>;
|
pub type Commands<'a> = Vec<Command<'a>>;
|
||||||
|
|
||||||
/// Commands issued to the layouting engine by models.
|
/// Commands issued to the layouting engine by models.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Command<'a> {
|
pub enum Command<'a> {
|
||||||
/// Layout the given model in the current context (i.e. not nested). The
|
/// Layout the given model in the current context (i.e. not nested). The
|
||||||
/// content of the model is not laid out into a separate box and then added,
|
/// content of the model is not laid out into a separate box and then added,
|
||||||
|
@ -27,7 +27,7 @@ use super::*;
|
|||||||
|
|
||||||
|
|
||||||
/// Performs the stack layouting.
|
/// Performs the stack layouting.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct StackLayouter {
|
pub struct StackLayouter {
|
||||||
/// The context for layouting.
|
/// The context for layouting.
|
||||||
ctx: StackContext,
|
ctx: StackContext,
|
||||||
@ -57,7 +57,7 @@ pub struct StackContext {
|
|||||||
|
|
||||||
/// A layout space composed of subspaces which can have different axes and
|
/// A layout space composed of subspaces which can have different axes and
|
||||||
/// alignments.
|
/// alignments.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
struct Space {
|
struct Space {
|
||||||
/// The index of this space in the list of spaces.
|
/// The index of this space in the list of spaces.
|
||||||
index: usize,
|
index: usize,
|
||||||
@ -134,7 +134,7 @@ impl StackLayouter {
|
|||||||
// A hard space is simply an empty box.
|
// A hard space is simply an empty box.
|
||||||
SpacingKind::Hard => {
|
SpacingKind::Hard => {
|
||||||
// Reduce the spacing such that it definitely fits.
|
// Reduce the spacing such that it definitely fits.
|
||||||
spacing.min_eq(self.space.usable.get_secondary(self.ctx.axes));
|
spacing.min_eq(self.space.usable.secondary(self.ctx.axes));
|
||||||
let dimensions = Size2D::with_y(spacing);
|
let dimensions = Size2D::with_y(spacing);
|
||||||
|
|
||||||
self.update_metrics(dimensions);
|
self.update_metrics(dimensions);
|
||||||
@ -179,7 +179,7 @@ impl StackLayouter {
|
|||||||
|
|
||||||
self.space.size = size.specialized(axes);
|
self.space.size = size.specialized(axes);
|
||||||
self.space.extra = extra.specialized(axes);
|
self.space.extra = extra.specialized(axes);
|
||||||
*self.space.usable.get_secondary_mut(axes) -= dimensions.y;
|
*self.space.usable.secondary_mut(axes) -= dimensions.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the rulers to account for the new layout. Returns true if a
|
/// Update the rulers to account for the new layout. Returns true if a
|
||||||
@ -328,7 +328,7 @@ impl StackLayouter {
|
|||||||
// the usable space for following layouts at it's origin by its
|
// the usable space for following layouts at it's origin by its
|
||||||
// extent along the secondary axis.
|
// extent along the secondary axis.
|
||||||
*bound.get_mut(axes.secondary, Origin)
|
*bound.get_mut(axes.secondary, Origin)
|
||||||
+= axes.secondary.factor() * layout.dimensions.get_secondary(*axes);
|
+= axes.secondary.factor() * layout.dimensions.secondary(*axes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ //
|
// ------------------------------------------------------------------ //
|
||||||
|
@ -14,6 +14,7 @@ use super::*;
|
|||||||
|
|
||||||
|
|
||||||
/// Performs the text layouting.
|
/// Performs the text layouting.
|
||||||
|
#[derive(Debug)]
|
||||||
struct TextLayouter<'a> {
|
struct TextLayouter<'a> {
|
||||||
ctx: TextContext<'a>,
|
ctx: TextContext<'a>,
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
@ -24,7 +25,7 @@ struct TextLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The context for text layouting.
|
/// The context for text layouting.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct TextContext<'a> {
|
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`].
|
||||||
|
27
src/lib.rs
27
src/lib.rs
@ -16,14 +16,10 @@
|
|||||||
//! format is [_PDF_](crate::export::pdf). Alternatively, the layout can be
|
//! format is [_PDF_](crate::export::pdf). Alternatively, the layout can be
|
||||||
//! serialized to pass it to a suitable renderer.
|
//! serialized to pass it to a suitable renderer.
|
||||||
|
|
||||||
#![allow(unused)]
|
|
||||||
|
|
||||||
pub use toddle;
|
pub use toddle;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
|
||||||
use std::io::Cursor;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
|
|
||||||
@ -35,8 +31,15 @@ use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
|||||||
use crate::syntax::{SyntaxModel, Scope, ParseContext, Parsed, parse};
|
use crate::syntax::{SyntaxModel, Scope, ParseContext, Parsed, parse};
|
||||||
use crate::syntax::span::Position;
|
use crate::syntax::span::Position;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
/// Declare a module and reexport all its contents.
|
||||||
|
macro_rules! pub_use_mod {
|
||||||
|
($name:ident) => {
|
||||||
|
mod $name;
|
||||||
|
pub use $name::*;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod export;
|
pub mod export;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -51,6 +54,7 @@ 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.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Typesetter {
|
pub struct Typesetter {
|
||||||
/// The font loader shared by all typesetting processes.
|
/// The font loader shared by all typesetting processes.
|
||||||
loader: GlobalFontLoader,
|
loader: GlobalFontLoader,
|
||||||
@ -79,16 +83,16 @@ impl Typesetter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the base page style.
|
|
||||||
pub fn set_page_style(&mut self, style: PageStyle) {
|
|
||||||
self.style.page = style;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the base text style.
|
/// Set the base text style.
|
||||||
pub fn set_text_style(&mut self, style: TextStyle) {
|
pub fn set_text_style(&mut self, style: TextStyle) {
|
||||||
self.style.text = style;
|
self.style.text = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the base page style.
|
||||||
|
pub fn set_page_style(&mut self, style: PageStyle) {
|
||||||
|
self.style.page = style;
|
||||||
|
}
|
||||||
|
|
||||||
/// A reference to the backing font loader.
|
/// A reference to the backing font loader.
|
||||||
pub fn loader(&self) -> &GlobalFontLoader {
|
pub fn loader(&self) -> &GlobalFontLoader {
|
||||||
&self.loader
|
&self.loader
|
||||||
@ -134,6 +138,7 @@ impl Typesetter {
|
|||||||
/// Wraps a font provider and transforms its errors into boxed trait objects.
|
/// 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
|
/// This enables font providers that do not return boxed errors to be used with
|
||||||
/// the typesetter.
|
/// the typesetter.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DynErrorProvider<P> {
|
pub struct DynErrorProvider<P> {
|
||||||
provider: P,
|
provider: P,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
//! 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::*;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ function! {
|
|||||||
|
|
||||||
/// The different kinds of content that can be spaced. Used as a metadata type
|
/// The different kinds of content that can be spaced. Used as a metadata type
|
||||||
/// for the [`ContentSpacingFunc`].
|
/// for the [`ContentSpacingFunc`].
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum ContentKind {
|
pub enum ContentKind {
|
||||||
Word,
|
Word,
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
//! Auxiliary macros.
|
|
||||||
|
|
||||||
|
|
||||||
/// Create trait implementations for an error type.
|
|
||||||
macro_rules! error_type {
|
|
||||||
(
|
|
||||||
$this:ident: $type:ident,
|
|
||||||
$(res: $res:ident,)*
|
|
||||||
show: $f:ident => $show:expr,
|
|
||||||
$(source: $source:expr,)*
|
|
||||||
$(from: ($err:ident: $from:path, $conv:expr),)*
|
|
||||||
) => {
|
|
||||||
// Possibly create a result type.
|
|
||||||
$(type $res<T> = std::result::Result<T, $type>;)*
|
|
||||||
|
|
||||||
impl std::fmt::Display for $type {
|
|
||||||
fn fmt(&$this, $f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
$show
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_display!($type);
|
|
||||||
|
|
||||||
impl std::error::Error for $type {
|
|
||||||
// The source method is only generated if an implementation was given.
|
|
||||||
$(fn source(&$this) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
$source
|
|
||||||
})*
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create any number of from implementations.
|
|
||||||
$(impl From<$from> for $type {
|
|
||||||
fn from($err: $from) -> $type {
|
|
||||||
$conv
|
|
||||||
}
|
|
||||||
})*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a `Debug` implementation from a `Display` implementation.
|
|
||||||
macro_rules! debug_display {
|
|
||||||
($type:ident) => (
|
|
||||||
impl std::fmt::Debug for $type {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
std::fmt::Display::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
($type:ident; $generics:tt where $($bounds:tt)*) => (
|
|
||||||
impl<$generics> std::fmt::Debug for $type<$generics> where $($bounds)* {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
std::fmt::Display::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Declare a module and reexport all its contents.
|
|
||||||
macro_rules! pub_use_mod {
|
|
||||||
($name:ident) => {
|
|
||||||
mod $name;
|
|
||||||
pub use $name::*;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether an expression matches a set of patterns.
|
|
||||||
macro_rules! matches {
|
|
||||||
($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => {
|
|
||||||
match $expression {
|
|
||||||
$( $pattern )|+ $( if $guard )? => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
92
src/size.rs
92
src/size.rs
@ -76,7 +76,11 @@ impl Display for Size {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_display!(Size);
|
impl Debug for Size {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Neg for Size {
|
impl Neg for Size {
|
||||||
type Output = Size;
|
type Output = Size;
|
||||||
@ -115,12 +119,16 @@ impl Display for ScaleSize {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ScaleSize::Absolute(size) => write!(f, "{}", size),
|
ScaleSize::Absolute(size) => write!(f, "{}", size),
|
||||||
ScaleSize::Scaled(scale) => write!(f, "x{}", scale),
|
ScaleSize::Scaled(scale) => write!(f, "{}%", scale * 100.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_display!(ScaleSize);
|
impl Debug for ScaleSize {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A scale size that is scaled by the font size.
|
/// A scale size that is scaled by the font size.
|
||||||
pub type FSize = ScaleSize;
|
pub type FSize = ScaleSize;
|
||||||
@ -129,7 +137,7 @@ pub type FSize = ScaleSize;
|
|||||||
pub type PSize = ScaleSize;
|
pub type PSize = ScaleSize;
|
||||||
|
|
||||||
/// A value in two dimensions.
|
/// A value in two dimensions.
|
||||||
#[derive(Default, Copy, Clone, PartialEq)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct Value2D<T> {
|
pub struct Value2D<T> {
|
||||||
/// The horizontal component.
|
/// The horizontal component.
|
||||||
pub x: T,
|
pub x: T,
|
||||||
@ -137,7 +145,7 @@ pub struct Value2D<T> {
|
|||||||
pub y: T,
|
pub y: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy> Value2D<T> {
|
impl<T: Clone> Value2D<T> {
|
||||||
/// Create a new 2D-value from two values.
|
/// Create a new 2D-value from two values.
|
||||||
pub fn new(x: T, y: T) -> Value2D<T> { Value2D { x, y } }
|
pub fn new(x: T, y: T) -> Value2D<T> { Value2D { x, y } }
|
||||||
|
|
||||||
@ -164,7 +172,7 @@ impl<T: Copy> Value2D<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a 2D-value with `x` and `y` set to the same value `s`.
|
/// Create a 2D-value with `x` and `y` set to the same value `s`.
|
||||||
pub fn with_all(s: T) -> Value2D<T> { Value2D { x: s, y: s } }
|
pub fn with_all(s: T) -> Value2D<T> { Value2D { x: s.clone(), y: s } }
|
||||||
|
|
||||||
/// Get the specificed component.
|
/// Get the specificed component.
|
||||||
pub fn get(self, axis: SpecificAxis) -> T {
|
pub fn get(self, axis: SpecificAxis) -> T {
|
||||||
@ -183,22 +191,22 @@ impl<T: Copy> Value2D<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the primary value of this specialized 2D-value.
|
/// Return the primary value of this specialized 2D-value.
|
||||||
pub fn get_primary(self, axes: LayoutAxes) -> T {
|
pub fn primary(self, axes: LayoutAxes) -> T {
|
||||||
if axes.primary.axis() == Horizontal { self.x } else { self.y }
|
if axes.primary.axis() == Horizontal { self.x } else { self.y }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Borrow the primary value of this specialized 2D-value mutably.
|
/// Borrow the primary value of this specialized 2D-value mutably.
|
||||||
pub fn get_primary_mut(&mut self, axes: LayoutAxes) -> &mut T {
|
pub fn primary_mut(&mut self, axes: LayoutAxes) -> &mut T {
|
||||||
if axes.primary.axis() == Horizontal { &mut self.x } else { &mut self.y }
|
if axes.primary.axis() == Horizontal { &mut self.x } else { &mut self.y }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the secondary value of this specialized 2D-value.
|
/// Return the secondary value of this specialized 2D-value.
|
||||||
pub fn get_secondary(self, axes: LayoutAxes) -> T {
|
pub fn secondary(self, axes: LayoutAxes) -> T {
|
||||||
if axes.primary.axis() == Horizontal { self.y } else { self.x }
|
if axes.primary.axis() == Horizontal { self.y } else { self.x }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Borrow the secondary value of this specialized 2D-value mutably.
|
/// Borrow the secondary value of this specialized 2D-value mutably.
|
||||||
pub fn get_secondary_mut(&mut self, axes: LayoutAxes) -> &mut T {
|
pub fn secondary_mut(&mut self, axes: LayoutAxes) -> &mut T {
|
||||||
if axes.primary.axis() == Horizontal { &mut self.y } else { &mut self.x }
|
if axes.primary.axis() == Horizontal { &mut self.y } else { &mut self.x }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,15 +235,12 @@ impl<T: Copy> Value2D<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Display for Value2D<T> where T: Display {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "[{}, {}]", self.x, self.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Debug for Value2D<T> where T: Debug {
|
impl<T> Debug for Value2D<T> where T: Debug {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "[{:?}, {:?}]", self.x, self.y)
|
f.debug_list()
|
||||||
|
.entry(&self.x)
|
||||||
|
.entry(&self.y)
|
||||||
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,6 +299,7 @@ impl Neg for Size2D {
|
|||||||
|
|
||||||
/// A value that is stretchable in an interval from a minimal through an optimal
|
/// A value that is stretchable in an interval from a minimal through an optimal
|
||||||
/// to a maximal value.
|
/// to a maximal value.
|
||||||
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct StretchValue<T> {
|
pub struct StretchValue<T> {
|
||||||
/// The minimum this value can be stretched to.
|
/// The minimum this value can be stretched to.
|
||||||
pub min: T,
|
pub min: T,
|
||||||
@ -310,23 +316,11 @@ impl<T> StretchValue<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Display for StretchValue<T> where T: Display {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "({}, {}, {})", self.min, self.opt, self.max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Debug for StretchValue<T> where T: Debug {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "({:?}, {:?}, {:?})", self.min, self.opt, self.max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A size that is stretchable.
|
/// A size that is stretchable.
|
||||||
pub type StretchSize = StretchValue<Size>;
|
pub type StretchSize = StretchValue<Size>;
|
||||||
|
|
||||||
/// A value in four dimensions.
|
/// A value in four dimensions.
|
||||||
#[derive(Default, Copy, Clone, PartialEq)]
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct ValueBox<T> {
|
pub struct ValueBox<T> {
|
||||||
/// The left extent.
|
/// The left extent.
|
||||||
pub left: T,
|
pub left: T,
|
||||||
@ -389,20 +383,6 @@ impl<T: Clone> ValueBox<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Display for ValueBox<T> where T: Display {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "[left: {}, top: {}, right: {}, bottom: {}]",
|
|
||||||
self.left, self.top, self.right, self.bottom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Debug for ValueBox<T> where T: Debug {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "[left: {:?}, top: {:?}, right: {:?}, bottom: {:?}]",
|
|
||||||
self.left, self.top, self.right, self.bottom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A size in four dimensions.
|
/// A size in four dimensions.
|
||||||
pub type SizeBox = ValueBox<Size>;
|
pub type SizeBox = ValueBox<Size>;
|
||||||
|
|
||||||
@ -416,14 +396,6 @@ impl SizeBox {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error which can be returned when parsing a size.
|
|
||||||
pub struct ParseSizeError;
|
|
||||||
|
|
||||||
error_type! {
|
|
||||||
self: ParseSizeError,
|
|
||||||
show: f => write!(f, "failed to parse size"),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Size {
|
impl FromStr for Size {
|
||||||
type Err = ParseSizeError;
|
type Err = ParseSizeError;
|
||||||
|
|
||||||
@ -433,16 +405,28 @@ impl FromStr for Size {
|
|||||||
_ if src.ends_with("mm") => Size::mm,
|
_ if src.ends_with("mm") => Size::mm,
|
||||||
_ if src.ends_with("cm") => Size::cm,
|
_ if src.ends_with("cm") => Size::cm,
|
||||||
_ if src.ends_with("in") => Size::inches,
|
_ if src.ends_with("in") => Size::inches,
|
||||||
_ => return Err(ParseSizeError),
|
_ => return Err(ParseSizeError(())),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(func(src[..src.len() - 2]
|
Ok(func(src[..src.len() - 2]
|
||||||
.parse::<f32>()
|
.parse::<f32>()
|
||||||
.map_err(|_| ParseSizeError)?))
|
.map_err(|_| ParseSizeError(()))?))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error which can be returned when parsing a size.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub struct ParseSizeError(());
|
||||||
|
|
||||||
|
impl std::error::Error for ParseSizeError {}
|
||||||
|
|
||||||
|
impl Display for ParseSizeError {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
f.write_str("invalid string for size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! implement_traits {
|
macro_rules! implement_traits {
|
||||||
($ty:ident, $t:ident, $o:ident
|
($ty:ident, $t:ident, $o:ident
|
||||||
reflexive {$(
|
reflexive {$(
|
||||||
|
29
src/style.rs
29
src/style.rs
@ -6,16 +6,16 @@ use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize};
|
|||||||
|
|
||||||
|
|
||||||
/// Defines properties of pages and text.
|
/// Defines properties of pages and text.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
pub struct LayoutStyle {
|
pub struct LayoutStyle {
|
||||||
/// The style for pages.
|
|
||||||
pub page: PageStyle,
|
|
||||||
/// The style for text.
|
/// The style for text.
|
||||||
pub text: TextStyle,
|
pub text: TextStyle,
|
||||||
|
/// The style for pages.
|
||||||
|
pub page: PageStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines which fonts to use and how to space text.
|
/// Defines which fonts to use and how to space text.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
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: FallbackTree,
|
pub fallback: FallbackTree,
|
||||||
@ -87,7 +87,7 @@ impl Default for TextStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Defines the size and margins of a page.
|
/// Defines the size and margins of a page.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct PageStyle {
|
pub struct PageStyle {
|
||||||
/// The class of this page.
|
/// The class of this page.
|
||||||
pub class: PaperClass,
|
pub class: PaperClass,
|
||||||
@ -159,12 +159,19 @@ impl PaperClass {
|
|||||||
/// The default margins for this page class.
|
/// The default margins for this page class.
|
||||||
pub fn default_margins(self) -> ValueBox<PSize> {
|
pub fn default_margins(self) -> ValueBox<PSize> {
|
||||||
use PaperClass::*;
|
use PaperClass::*;
|
||||||
|
let values = |l, t, r, b| ValueBox::new(
|
||||||
|
PSize::Scaled(l),
|
||||||
|
PSize::Scaled(t),
|
||||||
|
PSize::Scaled(r),
|
||||||
|
PSize::Scaled(b),
|
||||||
|
);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Custom => ValueBox::with_all(PSize::Scaled(0.1)),
|
Custom => values(0.1190, 0.0842, 0.1190, 0.0842),
|
||||||
Base => ValueBox::new(PSize::Scaled(0.119), PSize::Scaled(0.0569), PSize::Scaled(0.0476), PSize::Scaled(0.0569)),
|
Base => values(0.1190, 0.0842, 0.1190, 0.0842),
|
||||||
US => ValueBox::new(PSize::Scaled(0.176), PSize::Scaled(0.1092), PSize::Scaled(0.176), PSize::Scaled(0.091)),
|
US => values(0.1760, 0.1092, 0.1760, 0.0910),
|
||||||
Newspaper => ValueBox::new(PSize::Scaled(0.0455), PSize::Scaled(0.0587), PSize::Scaled(0.0455), PSize::Scaled(0.0294)),
|
Newspaper => values(0.0455, 0.0587, 0.0455, 0.0294),
|
||||||
Book => ValueBox::new(PSize::Scaled(0.12), PSize::Scaled(0.0852), PSize::Scaled(0.15), PSize::Scaled(0.0965)),
|
Book => values(0.1200, 0.0852, 0.1500, 0.0965),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,7 +189,7 @@ macro_rules! papers {
|
|||||||
};
|
};
|
||||||
|
|
||||||
(@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => {
|
(@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => {
|
||||||
#[doc = "Paper with the names `"]
|
#[doc = "Paper with name `"]
|
||||||
#[doc = $names]
|
#[doc = $names]
|
||||||
#[doc = "`."]
|
#[doc = "`."]
|
||||||
pub const $var: Paper = Paper {
|
pub const $var: Paper = Paper {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Expressions in function headers.
|
//! Expressions in function headers.
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
use crate::error::Errors;
|
use crate::error::Errors;
|
||||||
use crate::size::Size;
|
use crate::size::Size;
|
||||||
@ -44,6 +44,21 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Expr {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Ident(i) => i.fmt(f),
|
||||||
|
Str(s) => s.fmt(f),
|
||||||
|
Number(n) => n.fmt(f),
|
||||||
|
Size(s) => s.fmt(f),
|
||||||
|
Bool(b) => b.fmt(f),
|
||||||
|
Tuple(t) => t.fmt(f),
|
||||||
|
Object(o) => o.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A unicode identifier.
|
/// A unicode identifier.
|
||||||
///
|
///
|
||||||
/// The identifier must be valid! This is checked in [`Ident::new`] or
|
/// The identifier must be valid! This is checked in [`Ident::new`] or
|
||||||
@ -73,13 +88,19 @@ impl Ident {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Ident {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
f.write_str(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An untyped sequence of expressions.
|
/// An untyped sequence of expressions.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// (false, 12cm, "hi")
|
/// (false, 12cm, "hi")
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Default, Clone, PartialEq)]
|
||||||
pub struct Tuple {
|
pub struct Tuple {
|
||||||
/// The elements of the tuple.
|
/// The elements of the tuple.
|
||||||
pub items: Vec<Spanned<Expr>>,
|
pub items: Vec<Spanned<Expr>>,
|
||||||
@ -124,6 +145,16 @@ impl Tuple {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Tuple {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let mut tuple = f.debug_tuple("");
|
||||||
|
for item in &self.items {
|
||||||
|
tuple.field(item);
|
||||||
|
}
|
||||||
|
tuple.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A key-value collection of identifiers and associated expressions.
|
/// A key-value collection of identifiers and associated expressions.
|
||||||
///
|
///
|
||||||
/// The pairs themselves are not spanned, but the combined spans can easily be
|
/// The pairs themselves are not spanned, but the combined spans can easily be
|
||||||
@ -134,14 +165,14 @@ impl Tuple {
|
|||||||
/// ```typst
|
/// ```typst
|
||||||
/// { fit: false, size: 12cm, items: (1, 2, 3) }
|
/// { fit: false, size: 12cm, items: (1, 2, 3) }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Default, Clone, PartialEq)]
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
/// The key-value pairs of the object.
|
/// The key-value pairs of the object.
|
||||||
pub pairs: Vec<Pair>,
|
pub pairs: Vec<Pair>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A key-value pair in an object.
|
/// A key-value pair in an object.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Pair {
|
pub struct Pair {
|
||||||
/// The key part.
|
/// The key part.
|
||||||
/// ```typst
|
/// ```typst
|
||||||
@ -247,73 +278,10 @@ impl Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Expr {
|
impl Debug for Object {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
use Expr::*;
|
f.debug_map()
|
||||||
match self {
|
.entries(self.pairs.iter().map(|p| (&p.key.v, &p.value.v)))
|
||||||
Ident(i) => write!(f, "{}", i),
|
.finish()
|
||||||
Str(s) => write!(f, "{:?}", s),
|
|
||||||
Number(n) => write!(f, "{}", n),
|
|
||||||
Size(s) => write!(f, "{}", s),
|
|
||||||
Bool(b) => write!(f, "{}", b),
|
|
||||||
Tuple(t) => write!(f, "{}", t),
|
|
||||||
Object(o) => write!(f, "{}", o),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Ident {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Tuple {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "(")?;
|
|
||||||
|
|
||||||
let mut first = true;
|
|
||||||
for item in &self.items {
|
|
||||||
if !first {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
write!(f, "{}", item.v)?;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ")")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Object {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
if self.pairs.len() == 0 {
|
|
||||||
return write!(f, "{{}}");
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{{ ")?;
|
|
||||||
|
|
||||||
let mut first = true;
|
|
||||||
for pair in &self.pairs {
|
|
||||||
if !first {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
write!(f, "{}", pair)?;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " }}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Pair {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}: {}", self.key.v, self.value.v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_display!(Expr);
|
|
||||||
debug_display!(Ident);
|
|
||||||
debug_display!(Tuple);
|
|
||||||
debug_display!(Object);
|
|
||||||
debug_display!(Pair);
|
|
||||||
|
@ -16,7 +16,7 @@ use super::*;
|
|||||||
/// list that needs to be passed to those functions.
|
/// list that needs to be passed to those functions.
|
||||||
///
|
///
|
||||||
/// All entries need to have span information to enable the error reporting.
|
/// All entries need to have span information to enable the error reporting.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||||
pub struct DedupMap<K, V> where K: Eq {
|
pub struct DedupMap<K, V> where K: Eq {
|
||||||
map: Vec<Spanned<(K, V)>>,
|
map: Vec<Spanned<(K, V)>>,
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ pub trait Model: Debug + ModelBounds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A tree representation of source code.
|
/// A tree representation of source code.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
pub struct SyntaxModel {
|
pub struct SyntaxModel {
|
||||||
/// The syntactical elements making up this model.
|
/// The syntactical elements making up this model.
|
||||||
pub nodes: SpanVec<Node>,
|
pub nodes: SpanVec<Node>,
|
||||||
@ -97,7 +97,7 @@ impl PartialEq for Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decorations for semantic syntax highlighting.
|
/// Decorations for semantic syntax highlighting.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum Decoration {
|
pub enum Decoration {
|
||||||
/// A valid function name:
|
/// A valid function name:
|
||||||
|
@ -18,6 +18,7 @@ pub struct ParseContext<'a> {
|
|||||||
|
|
||||||
/// The result of parsing: Some parsed thing, errors and decorations for syntax
|
/// The result of parsing: Some parsed thing, errors and decorations for syntax
|
||||||
/// highlighting.
|
/// highlighting.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Parsed<T> {
|
pub struct Parsed<T> {
|
||||||
/// The result of the parsing process.
|
/// The result of the parsing process.
|
||||||
pub output: T,
|
pub output: T,
|
||||||
@ -321,9 +322,11 @@ impl<'s> FuncParser<'s> {
|
|||||||
|
|
||||||
/// Skip all whitespace/comment tokens.
|
/// Skip all whitespace/comment tokens.
|
||||||
fn skip_whitespace(&mut self) {
|
fn skip_whitespace(&mut self) {
|
||||||
self.eat_until(|t| !matches!(t,
|
self.eat_until(|t| match t {
|
||||||
Token::Space(_) | Token::LineComment(_) |
|
Token::Space(_) | Token::LineComment(_) |
|
||||||
Token::BlockComment(_)), false)
|
Token::BlockComment(_) => false,
|
||||||
|
_ => true,
|
||||||
|
}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an error about an expected `thing` which was not found, showing
|
/// Add an error about an expected `thing` which was not found, showing
|
||||||
|
@ -13,7 +13,7 @@ use super::Model;
|
|||||||
/// A map from identifiers to function parsers.
|
/// A map from identifiers to function parsers.
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
parsers: HashMap<String, Box<Parser>>,
|
parsers: HashMap<String, Box<Parser>>,
|
||||||
fallback: Box<Parser>
|
fallback: Box<Parser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
@ -63,8 +63,9 @@ impl Scope {
|
|||||||
|
|
||||||
impl Debug for Scope {
|
impl Debug for Scope {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "Scope ")?;
|
f.debug_set()
|
||||||
write!(f, "{:?}", self.parsers.keys())
|
.entries(self.parsers.keys())
|
||||||
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Spans map elements to the part of source code they originate from.
|
//! Spans map elements to the part of source code they originate from.
|
||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@ -60,6 +60,12 @@ impl Sub for Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Position {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}:{}", self.line, self.column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Locates a slice of source code.
|
/// Locates a slice of source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
@ -103,8 +109,14 @@ impl Span {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Span {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "({:?} -> {:?})", self.start, self.end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A value with the span it corresponds to in the source code.
|
/// A value with the span it corresponds to in the source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
pub struct Spanned<T> {
|
pub struct Spanned<T> {
|
||||||
/// The value.
|
/// The value.
|
||||||
pub v: T,
|
pub v: T,
|
||||||
@ -143,34 +155,3 @@ pub type SpanVec<T> = Vec<Spanned<T>>;
|
|||||||
pub fn offset_spans<T>(vec: SpanVec<T>, start: Position) -> impl Iterator<Item=Spanned<T>> {
|
pub fn offset_spans<T>(vec: SpanVec<T>, start: Position) -> impl Iterator<Item=Spanned<T>> {
|
||||||
vec.into_iter().map(move |s| s.map_span(|span| span.offset(start)))
|
vec.into_iter().map(move |s| s.map_span(|span| span.offset(start)))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Position {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}:{}", self.line, self.column)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Span {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "({}, {})", self.start, self.end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Display for Spanned<T> where T: std::fmt::Display {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "({}, {}, ", self.span.start, self.span.end)?;
|
|
||||||
self.v.fmt(f)?;
|
|
||||||
write!(f, ")")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Debug for Spanned<T> where T: std::fmt::Debug {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "({}, {}, ", self.span.start, self.span.end)?;
|
|
||||||
self.v.fmt(f)?;
|
|
||||||
write!(f, ")")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_display!(Position);
|
|
||||||
debug_display!(Span);
|
|
||||||
|
@ -122,6 +122,7 @@ impl<'s> Token<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over the tokens of a string of source code.
|
/// An iterator over the tokens of a string of source code.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Tokens<'s> {
|
pub struct Tokens<'s> {
|
||||||
src: &'s str,
|
src: &'s str,
|
||||||
mode: TokenizationMode,
|
mode: TokenizationMode,
|
||||||
@ -133,7 +134,7 @@ pub struct Tokens<'s> {
|
|||||||
/// Whether to tokenize in header mode which yields expression, comma and
|
/// Whether to tokenize in header mode which yields expression, comma and
|
||||||
/// similar tokens or in body mode which yields text and star, underscore,
|
/// similar tokens or in body mode which yields text and star, underscore,
|
||||||
/// backtick tokens.
|
/// backtick tokens.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum TokenizationMode {
|
pub enum TokenizationMode {
|
||||||
Header,
|
Header,
|
||||||
|
@ -52,7 +52,7 @@ fn main() -> DynResult<()> {
|
|||||||
for (name, src) in filtered {
|
for (name, src) in filtered {
|
||||||
panic::catch_unwind(|| {
|
panic::catch_unwind(|| {
|
||||||
if let Err(e) = test(&name, &src) {
|
if let Err(e) = test(&name, &src) {
|
||||||
println!("error: {}", e);
|
println!("error: {:?}", e);
|
||||||
}
|
}
|
||||||
}).ok();
|
}).ok();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user