extern crate self as typst_library;
pub mod diag;
pub mod engine;
pub mod foundations;
pub mod html;
pub mod introspection;
pub mod layout;
pub mod loading;
pub mod math;
pub mod model;
pub mod pdf;
pub mod routines;
pub mod symbols;
pub mod text;
pub mod visualize;
use std::ops::{Deref, Range};
use serde::{Deserialize, Serialize};
use typst_syntax::{FileId, Source, Span};
use typst_utils::{LazyHash, SmallBitSet};
use crate::diag::FileResult;
use crate::foundations::{Array, Binding, Bytes, Datetime, Dict, Module, Scope, Styles};
use crate::layout::{Alignment, Dir};
use crate::text::{Font, FontBook};
use crate::visualize::Color;
#[comemo::track]
pub trait World: Send + Sync {
fn library(&self) -> &LazyHash<Library>;
fn book(&self) -> &LazyHash<FontBook>;
fn main(&self) -> FileId;
fn source(&self, id: FileId) -> FileResult<Source>;
fn file(&self, id: FileId) -> FileResult<Bytes>;
fn font(&self, index: usize) -> Option<Font>;
fn today(&self, offset: Option<i64>) -> Option<Datetime>;
}
macro_rules! world_impl {
($W:ident for $ptr:ty) => {
impl<$W: World> World for $ptr {
fn library(&self) -> &LazyHash<Library> {
self.deref().library()
}
fn book(&self) -> &LazyHash<FontBook> {
self.deref().book()
}
fn main(&self) -> FileId {
self.deref().main()
}
fn source(&self, id: FileId) -> FileResult<Source> {
self.deref().source(id)
}
fn file(&self, id: FileId) -> FileResult<Bytes> {
self.deref().file(id)
}
fn font(&self, index: usize) -> Option<Font> {
self.deref().font(index)
}
fn today(&self, offset: Option<i64>) -> Option<Datetime> {
self.deref().today(offset)
}
}
};
}
world_impl!(W for std::boxed::Box<W>);
world_impl!(W for std::sync::Arc<W>);
world_impl!(W for &W);
pub trait WorldExt {
fn range(&self, span: Span) -> Option<Range<usize>>;
}
impl<T: World + ?Sized> WorldExt for T {
fn range(&self, span: Span) -> Option<Range<usize>> {
span.range().or_else(|| self.source(span.id()?).ok()?.range(span))
}
}
#[derive(Debug, Clone, Hash)]
pub struct Library {
pub global: Module,
pub math: Module,
pub styles: Styles,
pub std: Binding,
pub features: Features,
}
impl Library {
pub fn builder() -> LibraryBuilder {
LibraryBuilder::default()
}
}
impl Default for Library {
fn default() -> Self {
Self::builder().build()
}
}
#[derive(Debug, Clone, Default)]
pub struct LibraryBuilder {
inputs: Option<Dict>,
features: Features,
}
impl LibraryBuilder {
pub fn with_inputs(mut self, inputs: Dict) -> Self {
self.inputs = Some(inputs);
self
}
pub fn with_features(mut self, features: Features) -> Self {
self.features = features;
self
}
pub fn build(self) -> Library {
let math = math::module();
let inputs = self.inputs.unwrap_or_default();
let global = global(math.clone(), inputs, &self.features);
Library {
global: global.clone(),
math,
styles: Styles::new(),
std: Binding::detached(global),
features: self.features,
}
}
}
#[derive(Debug, Default, Clone, Hash)]
pub struct Features(SmallBitSet);
impl Features {
pub fn is_enabled(&self, feature: Feature) -> bool {
self.0.contains(feature as usize)
}
}
impl FromIterator<Feature> for Features {
fn from_iter<T: IntoIterator<Item = Feature>>(iter: T) -> Self {
let mut set = SmallBitSet::default();
for feature in iter {
set.insert(feature as usize);
}
Self(set)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum Feature {
Html,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Category {
Foundations,
Introspection,
Layout,
DataLoading,
Math,
Model,
Symbols,
Text,
Visualize,
Pdf,
Html,
Svg,
Png,
}
impl Category {
pub fn name(&self) -> &'static str {
match self {
Self::Foundations => "foundations",
Self::Introspection => "introspection",
Self::Layout => "layout",
Self::DataLoading => "data-loading",
Self::Math => "math",
Self::Model => "model",
Self::Symbols => "symbols",
Self::Text => "text",
Self::Visualize => "visualize",
Self::Pdf => "pdf",
Self::Html => "html",
Self::Svg => "svg",
Self::Png => "png",
}
}
}
fn global(math: Module, inputs: Dict, features: &Features) -> Module {
let mut global = Scope::deduplicating();
self::foundations::define(&mut global, inputs, features);
self::model::define(&mut global);
self::text::define(&mut global);
self::layout::define(&mut global);
self::visualize::define(&mut global);
self::introspection::define(&mut global);
self::loading::define(&mut global);
self::symbols::define(&mut global);
global.define("math", math);
global.define("pdf", self::pdf::module());
if features.is_enabled(Feature::Html) {
global.define("html", self::html::module());
}
prelude(&mut global);
Module::new("global", global)
}
fn prelude(global: &mut Scope) {
global.define("black", Color::BLACK);
global.define("gray", Color::GRAY);
global.define("silver", Color::SILVER);
global.define("white", Color::WHITE);
global.define("navy", Color::NAVY);
global.define("blue", Color::BLUE);
global.define("aqua", Color::AQUA);
global.define("teal", Color::TEAL);
global.define("eastern", Color::EASTERN);
global.define("purple", Color::PURPLE);
global.define("fuchsia", Color::FUCHSIA);
global.define("maroon", Color::MAROON);
global.define("red", Color::RED);
global.define("orange", Color::ORANGE);
global.define("yellow", Color::YELLOW);
global.define("olive", Color::OLIVE);
global.define("green", Color::GREEN);
global.define("lime", Color::LIME);
global.define("luma", Color::luma_data());
global.define("oklab", Color::oklab_data());
global.define("oklch", Color::oklch_data());
global.define("rgb", Color::rgb_data());
global.define("cmyk", Color::cmyk_data());
global.define("range", Array::range_data());
global.define("ltr", Dir::LTR);
global.define("rtl", Dir::RTL);
global.define("ttb", Dir::TTB);
global.define("btt", Dir::BTT);
global.define("start", Alignment::START);
global.define("left", Alignment::LEFT);
global.define("center", Alignment::CENTER);
global.define("right", Alignment::RIGHT);
global.define("end", Alignment::END);
global.define("top", Alignment::TOP);
global.define("horizon", Alignment::HORIZON);
global.define("bottom", Alignment::BOTTOM);
}