typst/src/lib.rs

229 lines
8.2 KiB
Rust

//! Typeset is a library for compiling documents written in the
//! corresponding typesetting language into a typesetted document in an
//! output format like _PDF_.
//!
//! # Example
//! This is an example of compiling a really simple document into _PDF_.
//! ```
//! use typeset::Compiler;
//!
//! // Minimal source code for our document.
//! let src = "Hello World from Typeset!";
//!
//! // Create an output file.
//! # /*
//! let mut file = std::fs::File::create("hello-typeset.pdf").unwrap();
//! # */
//! # let mut file = std::fs::File::create("../target/typeset-hello.pdf").unwrap();
//!
//! // Create a compiler and write the document into a file as a PDF.
//! let compiler = Compiler::new();
//! compiler.write_pdf(src, &mut file).unwrap();
//! ```
pub mod syntax;
pub mod doc;
pub mod font;
mod parsing;
mod engine;
mod pdf;
mod utility;
pub use crate::parsing::{Tokens, ParseError};
pub use crate::engine::TypesetError;
pub use crate::pdf::PdfError;
use std::error;
use std::fmt;
use std::io::Write;
use crate::parsing::Parser;
use crate::syntax::SyntaxTree;
use crate::engine::Engine;
use crate::doc::{Document, Style};
use crate::pdf::PdfCreator;
/// Compiles source code into typesetted documents allowing to
/// retrieve results at various stages.
pub struct Compiler {
/// Style for typesetting.
style: Style,
}
impl Compiler {
/// Create a new compiler from a document.
#[inline]
pub fn new() -> Compiler {
Compiler {
style: Style::default(),
}
}
/// Set the default style for typesetting.
#[inline]
pub fn style(&mut self, style: Style) -> &mut Self {
self.style = style;
self
}
/// Return an iterator over the tokens of the document.
#[inline]
pub fn tokenize<'s>(&self, source: &'s str) -> Tokens<'s> {
Tokens::new(source)
}
/// Return the abstract syntax tree representation of the document.
#[inline]
pub fn parse<'s>(&self, source: &'s str) -> Result<SyntaxTree<'s>, ParseError> {
Parser::new(self.tokenize(source)).parse()
}
/// Return the abstract typesetted representation of the document.
#[inline]
pub fn typeset(&self, source: &str) -> Result<Document, Error> {
let tree = self.parse(source)?;
Engine::new(&tree, self.style.clone()).typeset().map_err(Into::into)
}
/// Write the document as a _PDF_, returning how many bytes were written.
pub fn write_pdf<W: Write>(&self, source: &str, target: &mut W) -> Result<usize, Error> {
let document = self.typeset(source)?;
PdfCreator::new(&document, target)?.write().map_err(Into::into)
}
}
/// The general error type for compilation.
pub enum Error {
/// An error that occured while transforming source code into
/// an abstract syntax tree.
Parse(ParseError),
/// An error that occured while typesetting into an abstract document.
Typeset(TypesetError),
/// An error that occured while writing the document as a _PDF_.
Pdf(PdfError),
}
impl error::Error for Error {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Parse(err) => Some(err),
Error::Typeset(err) => Some(err),
Error::Pdf(err) => Some(err),
}
}
}
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Parse(err) => write!(f, "parse error: {}", err),
Error::Typeset(err) => write!(f, "typeset error: {}", err),
Error::Pdf(err) => write!(f, "pdf error: {}", err),
}
}
}
impl fmt::Debug for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl From<ParseError> for Error {
#[inline]
fn from(err: ParseError) -> Error {
Error::Parse(err)
}
}
impl From<TypesetError> for Error {
#[inline]
fn from(err: TypesetError) -> Error {
Error::Typeset(err)
}
}
impl From<PdfError> for Error {
#[inline]
fn from(err: PdfError) -> Error {
Error::Pdf(err)
}
}
#[cfg(test)]
mod test {
use crate::Compiler;
/// Create a pdf with a name from the source code.
fn test(name: &str, src: &str) {
let path = format!("../target/typeset-pdf-{}.pdf", name);
let mut file = std::fs::File::create(path).unwrap();
Compiler::new().write_pdf(src, &mut file).unwrap();
}
#[test]
fn small() {
test("unicode", "∑mbe∂∂ed font with Unicode!");
test("parentheses", "Text with ) and ( or (enclosed) works.");
test("composite-glyph", "Composite character‼");
test("multiline","
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
clita kasd gubergren, no sea takimata sanctus est.
");
}
#[test]
fn long_styled() {
test("wikipedia", r#"
Typesetting is the composition of text by means of arranging physical types or the
digital equivalents. Stored letters and other symbols (called sorts in mechanical
systems and glyphs in digital systems) are retrieved and ordered according to a
language's orthography for visual display. Typesetting requires one or more fonts
(which are widely but erroneously confused with and substituted for typefaces). One
significant effect of typesetting was that authorship of works could be spotted more
easily, making it difficult for copiers who have not gained permission.
During much of the letterpress era, movable type was composed by hand for each page.
Cast metal sorts were composed into words, then lines, then paragraphs, then pages of
text and tightly bound together to make up a form, with all letter faces exactly the
same "height to paper", creating an even surface of type. The form was placed in a
press, inked, and an impression made on paper.
During typesetting, individual sorts are picked from a type case with the right hand,
and set into a composing stick held in the left hand from left to right, and as viewed
by the setter upside down. As seen in the photo of the composing stick, a lower case
'q' looks like a 'd', a lower case 'b' looks like a 'p', a lower case 'p' looks like a
'b' and a lower case 'd' looks like a 'q'. This is reputed to be the origin of the
expression "mind your p's and q's". It might just as easily have been "mind your b's
and d's".
The diagram at right illustrates a cast metal sort: a face, b body or shank, c point
size, 1 shoulder, 2 nick, 3 groove, 4 foot. Wooden printing sorts were in use for
centuries in combination with metal type. Not shown, and more the concern of the
casterman, is the “set”, or width of each sort. Set width, like body size, is measured
in points.
In order to extend the working life of type, and to account for the finite sorts in a
case of type, copies of forms were cast when anticipating subsequent printings of a
text, freeing the costly type for other work. This was particularly prevalent in book
and newspaper work where rotary presses required type forms to wrap an impression
cylinder rather than set in the bed of a press. In this process, called stereotyping,
the entire form is pressed into a fine matrix such as plaster of Paris or papier mâché
called a flong to create a positive, from which the stereotype form was electrotyped,
cast of type metal.
Advances such as the typewriter and computer would push the state of the art even
farther ahead. Still, hand composition and letterpress printing have not fallen
completely out of use, and since the introduction of digital typesetting, it has seen a
revival as an artisanal pursuit. However, it is a very small niche within the larger
typesetting market.
"#);
}
}