Make things more consistent ♻
This commit is contained in:
parent
adfd7dd073
commit
5ca303ecad
@ -10,10 +10,10 @@ pub use size::Size;
|
||||
|
||||
|
||||
/// The core typesetting engine, transforming an abstract syntax tree into a document.
|
||||
pub(crate) struct Engine<'a> {
|
||||
pub struct Engine<'t> {
|
||||
// Immutable
|
||||
tree: &'a SyntaxTree<'a>,
|
||||
ctx: &'a Context<'a>,
|
||||
tree: &'t SyntaxTree<'t>,
|
||||
ctx: &'t Context<'t>,
|
||||
|
||||
// Mutable
|
||||
fonts: Vec<Font>,
|
||||
@ -23,22 +23,22 @@ pub(crate) struct Engine<'a> {
|
||||
current_width: Size,
|
||||
}
|
||||
|
||||
impl<'a> Engine<'a> {
|
||||
impl<'t> Engine<'t> {
|
||||
/// Create a new generator from a syntax tree.
|
||||
pub fn new(tree: &'a SyntaxTree<'a>, context: &'a Context<'a>) -> Engine<'a> {
|
||||
pub(crate) fn new(tree: &'t SyntaxTree<'t>, context: &'t Context<'t>) -> Engine<'t> {
|
||||
Engine {
|
||||
tree,
|
||||
ctx: context,
|
||||
fonts: Vec::new(),
|
||||
fonts: vec![],
|
||||
active_font: 0,
|
||||
text_commands: Vec::new(),
|
||||
text_commands: vec![],
|
||||
current_line: String::new(),
|
||||
current_width: Size::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the abstract document.
|
||||
pub fn typeset(mut self) -> TypeResult<Document> {
|
||||
pub(crate) fn typeset(mut self) -> TypeResult<Document> {
|
||||
// Load font defined by style
|
||||
let mut font = None;
|
||||
let filter = FontFilter::new(&self.ctx.style.font_families);
|
||||
|
@ -5,7 +5,7 @@ use std::ops::*;
|
||||
|
||||
|
||||
/// A general size (unit of length) type.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[derive(Copy, Clone, PartialEq, Default)]
|
||||
pub struct Size {
|
||||
/// The size in typographic points (1/72 inches).
|
||||
points: f32,
|
||||
@ -62,6 +62,7 @@ impl Debug for Size {
|
||||
}
|
||||
|
||||
impl PartialOrd for Size {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Size) -> Option<Ordering> {
|
||||
self.points.partial_cmp(&other.points)
|
||||
}
|
||||
@ -70,12 +71,14 @@ impl PartialOrd for Size {
|
||||
impl Neg for Size {
|
||||
type Output = Size;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Size {
|
||||
Size { points: -self.points }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum for Size {
|
||||
#[inline]
|
||||
fn sum<I>(iter: I) -> Size where I: Iterator<Item=Size> {
|
||||
iter.fold(Size::zero(), Add::add)
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ struct PdfEngine<'d, W: Write> {
|
||||
}
|
||||
|
||||
/// Offsets for the various groups of ids.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
struct Offsets {
|
||||
catalog: Ref,
|
||||
page_tree: Ref,
|
||||
|
55
src/font.rs
55
src/font.rs
@ -143,8 +143,8 @@ impl Font {
|
||||
loca: None,
|
||||
glyphs: Vec::with_capacity(chars.len()),
|
||||
chars,
|
||||
records: Vec::new(),
|
||||
body: Vec::new(),
|
||||
records: vec![],
|
||||
body: vec![],
|
||||
};
|
||||
|
||||
subsetter.subset(needed_tables, optional_tables)
|
||||
@ -152,7 +152,7 @@ impl Font {
|
||||
}
|
||||
|
||||
/// Font metrics relevant to the typesetting engine.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct FontMetrics {
|
||||
/// Whether the font is italic.
|
||||
pub is_italic: bool,
|
||||
@ -275,10 +275,10 @@ macro_rules! font_info {
|
||||
}
|
||||
|
||||
/// Criteria to filter fonts.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct FontFilter<'a> {
|
||||
/// A fallback list of font families we accept. The first family in this list, that also
|
||||
/// satisfies the other conditions shall be returned.
|
||||
/// A fallback list of font families to accept. The first family in this list, that also
|
||||
/// satisfies the other conditions, shall be returned.
|
||||
pub families: &'a [FontFamily],
|
||||
/// If some, matches only italic/non-italic fonts, otherwise any.
|
||||
pub italic: Option<bool>,
|
||||
@ -298,13 +298,6 @@ impl<'a> FontFilter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this filter matches the given info.
|
||||
pub fn matches(&self, info: &FontInfo) -> bool {
|
||||
self.italic.map(|i| i == info.italic).unwrap_or(true)
|
||||
&& self.bold.map(|i| i == info.bold).unwrap_or(true)
|
||||
&& self.families.iter().any(|family| info.families.contains(family))
|
||||
}
|
||||
|
||||
/// Set the italic value to something.
|
||||
pub fn italic(&mut self, italic: bool) -> &mut Self {
|
||||
self.italic = Some(italic); self
|
||||
@ -314,6 +307,13 @@ impl<'a> FontFilter<'a> {
|
||||
pub fn bold(&mut self, bold: bool) -> &mut Self {
|
||||
self.bold = Some(bold); self
|
||||
}
|
||||
|
||||
/// Whether this filter matches the given info.
|
||||
pub fn matches(&self, info: &FontInfo) -> bool {
|
||||
self.italic.map(|i| i == info.italic).unwrap_or(true)
|
||||
&& self.bold.map(|i| i == info.bold).unwrap_or(true)
|
||||
&& self.families.iter().any(|family| info.families.contains(family))
|
||||
}
|
||||
}
|
||||
|
||||
/// A family of fonts (either generic or named).
|
||||
@ -326,6 +326,7 @@ pub enum FontFamily {
|
||||
}
|
||||
|
||||
/// A font provider serving fonts from a folder on the local file system.
|
||||
#[derive(Debug)]
|
||||
pub struct FileSystemFontProvider {
|
||||
base: PathBuf,
|
||||
paths: Vec<PathBuf>,
|
||||
@ -346,29 +347,36 @@ impl FileSystemFontProvider {
|
||||
/// ("NotoSans-Italic.ttf", font_info!(["NotoSans", SansSerif], italic)),
|
||||
/// ]);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new<B, I, P>(base: B, infos: I) -> FileSystemFontProvider
|
||||
where
|
||||
B: Into<PathBuf>,
|
||||
I: IntoIterator<Item = (P, FontInfo)>,
|
||||
P: Into<PathBuf>,
|
||||
{
|
||||
let mut paths = Vec::new();
|
||||
let mut font_infos = Vec::new();
|
||||
// Find out how long the iterator is at least, to reserve the correct
|
||||
// capacity for the vectors.
|
||||
let iter = infos.into_iter();
|
||||
let min = iter.size_hint().0;
|
||||
|
||||
for (path, info) in infos.into_iter() {
|
||||
// Split the iterator into two seperated vectors.
|
||||
let mut paths = Vec::with_capacity(min);
|
||||
let mut infos = Vec::with_capacity(min);
|
||||
for (path, info) in iter {
|
||||
paths.push(path.into());
|
||||
font_infos.push(info);
|
||||
infos.push(info);
|
||||
}
|
||||
|
||||
FileSystemFontProvider {
|
||||
base: base.into(),
|
||||
paths,
|
||||
infos: font_infos,
|
||||
infos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FontProvider for FileSystemFontProvider {
|
||||
#[inline]
|
||||
fn get(&self, info: &FontInfo) -> Option<Box<dyn FontData>> {
|
||||
let index = self.infos.iter().position(|i| i == info)?;
|
||||
let path = &self.paths[index];
|
||||
@ -376,16 +384,17 @@ impl FontProvider for FileSystemFontProvider {
|
||||
Some(Box::new(file) as Box<FontData>)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn available<'a>(&'a self) -> &'a [FontInfo] {
|
||||
&self.infos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Subsetter<'p> {
|
||||
struct Subsetter<'d> {
|
||||
// Original font
|
||||
font: &'p Font,
|
||||
reader: OpenTypeReader<Cursor<&'p [u8]>>,
|
||||
font: &'d Font,
|
||||
reader: OpenTypeReader<Cursor<&'d [u8]>>,
|
||||
outlines: Outlines,
|
||||
tables: Vec<TableRecord>,
|
||||
cmap: Option<CharMap>,
|
||||
@ -399,7 +408,7 @@ struct Subsetter<'p> {
|
||||
body: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'p> Subsetter<'p> {
|
||||
impl<'d> Subsetter<'d> {
|
||||
fn subset<I1, S1, I2, S2>(mut self, needed_tables: I1, optional_tables: I2)
|
||||
-> FontResult<Font>
|
||||
where
|
||||
@ -726,7 +735,7 @@ impl<'p> Subsetter<'p> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_table_data(&self, tag: Tag) -> FontResult<&'p [u8]> {
|
||||
fn get_table_data(&self, tag: Tag) -> FontResult<&'d [u8]> {
|
||||
let record = match self.tables.binary_search_by_key(&tag, |r| r.tag) {
|
||||
Ok(index) => &self.tables[index],
|
||||
Err(_) => return Err(FontError::MissingTable(tag.to_string())),
|
||||
|
11
src/lib.rs
11
src/lib.rs
@ -2,13 +2,14 @@
|
||||
//!
|
||||
//! # Compilation
|
||||
//! - **Parsing:** The parsing step first transforms a plain string into an
|
||||
//! [iterator of tokens](crate::parsing::Tokens). Then the parser operates on that to construct
|
||||
//! a syntax tree. The structures describing the tree can be found in the [`syntax`] module.
|
||||
//! [iterator of tokens](crate::parsing::Tokens). Then the [parser](crate::parsing::Parser)
|
||||
//! operates on that to construct a syntax tree. The structures describing the tree can be found
|
||||
//! in the [syntax] module.
|
||||
//! - **Typesetting:** The next step is to transform the syntax tree into a portable representation
|
||||
//! of the typesetted document. Types for these can be found in the [`doc`] module. This
|
||||
//! of the typesetted document. Types for these can be found in the [doc] module. This
|
||||
//! representation contains already the finished layout.
|
||||
//! - **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
|
||||
//! for the supported formats are located in the [export] module. Currently the only supported
|
||||
//! format is _PDF_.
|
||||
//!
|
||||
//! # Example
|
||||
@ -82,7 +83,7 @@ impl<'p> Compiler<'p> {
|
||||
Compiler {
|
||||
context: Context {
|
||||
style: Style::default(),
|
||||
font_providers: Vec::new(),
|
||||
font_providers: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ enum TokensState<'s> {
|
||||
}
|
||||
|
||||
impl PartialEq for TokensState<'_> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &TokensState) -> bool {
|
||||
use TokensState as TS;
|
||||
|
||||
@ -184,26 +183,22 @@ impl<'s> Tokens<'s> {
|
||||
}
|
||||
|
||||
/// Advance the iterator by one step.
|
||||
#[inline]
|
||||
fn advance(&mut self) {
|
||||
self.words.next();
|
||||
}
|
||||
|
||||
/// Switch to the given state.
|
||||
#[inline]
|
||||
fn switch(&mut self, mut state: TokensState<'s>) {
|
||||
swap(&mut state, &mut self.state);
|
||||
self.stack.push(state);
|
||||
}
|
||||
|
||||
/// Go back to the top-of-stack state.
|
||||
#[inline]
|
||||
fn unswitch(&mut self) {
|
||||
self.state = self.stack.pop().unwrap_or(TokensState::Body);
|
||||
}
|
||||
|
||||
/// Advance and return the given token.
|
||||
#[inline]
|
||||
fn consumed(&mut self, token: Token<'s>) -> Token<'s> {
|
||||
self.advance();
|
||||
token
|
||||
@ -211,8 +206,8 @@ impl<'s> Tokens<'s> {
|
||||
}
|
||||
|
||||
/// Transforms token streams to syntax trees.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Parser<'s, T> where T: Iterator<Item = Token<'s>> {
|
||||
#[derive(Debug)]
|
||||
pub struct Parser<'s, T> where T: Iterator<Item=Token<'s>> {
|
||||
tokens: Peekable<T>,
|
||||
state: ParserState,
|
||||
stack: Vec<Function<'s>>,
|
||||
@ -220,7 +215,7 @@ pub struct Parser<'s, T> where T: Iterator<Item = Token<'s>> {
|
||||
}
|
||||
|
||||
/// The state the parser is in.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum ParserState {
|
||||
/// The base state of the parser.
|
||||
Body,
|
||||
@ -228,9 +223,9 @@ enum ParserState {
|
||||
Function,
|
||||
}
|
||||
|
||||
impl<'s, T> Parser<'s, T> where T: Iterator<Item = Token<'s>> {
|
||||
impl<'s, T> Parser<'s, T> where T: Iterator<Item=Token<'s>> {
|
||||
/// Create a new parser from a type that emits results of tokens.
|
||||
pub fn new(tokens: T) -> Parser<'s, T> {
|
||||
pub(crate) fn new(tokens: T) -> Parser<'s, T> {
|
||||
Parser {
|
||||
tokens: tokens.peekable(),
|
||||
state: ParserState::Body,
|
||||
@ -240,13 +235,13 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item = Token<'s>> {
|
||||
}
|
||||
|
||||
/// Parse into an abstract syntax tree.
|
||||
pub fn parse(mut self) -> ParseResult<SyntaxTree<'s>> {
|
||||
pub(crate) fn parse(mut self) -> ParseResult<SyntaxTree<'s>> {
|
||||
use ParserState as PS;
|
||||
|
||||
while let Some(token) = self.tokens.next() {
|
||||
// Comment
|
||||
if token == Token::Hashtag {
|
||||
self.skip_while(|t| *t != Token::Newline);
|
||||
self.skip_while(|&t| t != Token::Newline);
|
||||
self.advance();
|
||||
}
|
||||
|
||||
@ -314,13 +309,11 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item = Token<'s>> {
|
||||
}
|
||||
|
||||
/// Advance the iterator by one step.
|
||||
#[inline]
|
||||
fn advance(&mut self) {
|
||||
self.tokens.next();
|
||||
}
|
||||
|
||||
/// Skip tokens until the condition is met.
|
||||
#[inline]
|
||||
fn skip_while<F>(&mut self, f: F) where F: Fn(&Token) -> bool {
|
||||
while let Some(token) = self.tokens.peek() {
|
||||
if !f(token) {
|
||||
@ -331,16 +324,14 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item = Token<'s>> {
|
||||
}
|
||||
|
||||
/// Switch the state.
|
||||
#[inline]
|
||||
fn switch(&mut self, state: ParserState) {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
/// Append a node to the top-of-stack function or the main tree itself.
|
||||
#[inline]
|
||||
fn append(&mut self, node: Node<'s>) {
|
||||
let tree = match self.stack.last_mut() {
|
||||
Some(func) => func.body.get_or_insert_with(|| SyntaxTree::new()),
|
||||
Some(func) => func.body.as_mut().unwrap(),
|
||||
None => &mut self.tree,
|
||||
};
|
||||
|
||||
@ -348,7 +339,6 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item = Token<'s>> {
|
||||
}
|
||||
|
||||
/// Gives a parsing error with a message.
|
||||
#[inline]
|
||||
fn err<R, S: Into<String>>(&self, message: S) -> ParseResult<R> {
|
||||
Err(ParseError { message: message.into() })
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
/// A logical unit of the incoming text stream.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Token<'s> {
|
||||
/// One or more whitespace (non-newline) codepoints.
|
||||
Space,
|
||||
|
@ -49,7 +49,7 @@ pub struct Spline<'s, T> {
|
||||
}
|
||||
|
||||
/// Represents either a splitted substring or a splinor.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Splined<'s, T> {
|
||||
/// A substring.
|
||||
Value(&'s str),
|
||||
|
Loading…
x
Reference in New Issue
Block a user