Make frames serializable 📚

This also makes serialization support non-optional since it's too much feature-management for too little benefit.
This commit is contained in:
Laurenz 2021-04-21 21:17:25 +02:00
parent df58a4d89b
commit 72478946c2
18 changed files with 71 additions and 61 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
# General # General
.vscode .vscode
_things _things
desktop.ini
# Tests and benchmarks # Tests and benchmarks
tests/png tests/png

View File

@ -8,7 +8,6 @@ edition = "2018"
default = ["fs"] default = ["fs"]
fs = ["fontdock/fs"] fs = ["fontdock/fs"]
cli = ["fs", "anyhow"] cli = ["fs", "anyhow"]
# serde = []
[workspace] [workspace]
members = ["bench"] members = ["bench"]
@ -22,7 +21,7 @@ debug = 0
opt-level = 2 opt-level = 2
[dependencies] [dependencies]
fontdock = { path = "../fontdock", default-features = false } fontdock = { path = "../fontdock", features = ["serde"], default-features = false }
image = { version = "0.23", default-features = false, features = ["jpeg", "png"] } image = { version = "0.23", default-features = false, features = ["jpeg", "png"] }
miniz_oxide = "0.3" miniz_oxide = "0.3"
pdf-writer = { path = "../pdf-writer" } pdf-writer = { path = "../pdf-writer" }
@ -32,7 +31,7 @@ unicode-bidi = "0.3.5"
unicode-xid = "0.2" unicode-xid = "0.2"
xi-unicode = "0.3" xi-unicode = "0.3"
anyhow = { version = "1", optional = true } anyhow = { version = "1", optional = true }
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"] }
[dev-dependencies] [dev-dependencies]
walkdir = "2" walkdir = "2"

View File

@ -3,6 +3,7 @@ unstable_features = true
overflow_delimited_expr = true overflow_delimited_expr = true
spaces_around_ranges = true spaces_around_ranges = true
use_field_init_shorthand = true use_field_init_shorthand = true
merge_derives = false
max_width = 90 max_width = 90
struct_lit_width = 40 struct_lit_width = 40

View File

@ -3,8 +3,10 @@
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize};
/// A color in a dynamic format. /// A color in a dynamic format.
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum Color { pub enum Color {
/// An 8-bit RGBA color: `#423abaff`. /// An 8-bit RGBA color: `#423abaff`.
Rgba(RgbaColor), Rgba(RgbaColor),
@ -27,7 +29,7 @@ impl Debug for Color {
} }
/// An 8-bit RGBA color: `#423abaff`. /// An 8-bit RGBA color: `#423abaff`.
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct RgbaColor { pub struct RgbaColor {
/// Red channel. /// Red channel.
pub r: u8, pub r: u8,

View File

@ -5,10 +5,12 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::syntax::Span; use crate::syntax::Span;
/// The result of some pass: Some output `T` and diagnostics. /// The result of some pass: Some output `T` and diagnostics.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Pass<T> { pub struct Pass<T> {
/// The output of this compilation pass. /// The output of this compilation pass.
pub output: T, pub output: T,
@ -31,8 +33,7 @@ impl<T> Pass<T> {
pub type DiagSet = BTreeSet<Diag>; pub type DiagSet = BTreeSet<Diag>;
/// A diagnostic with severity level and message. /// A diagnostic with severity level and message.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Diag { pub struct Diag {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
@ -61,8 +62,8 @@ impl Display for Diag {
/// How severe / important a diagnostic is. /// How severe / important a diagnostic is.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] #[serde(rename_all = "kebab-case")]
pub enum Level { pub enum Level {
Warning, Warning,
Error, Error,

View File

@ -10,6 +10,7 @@ use std::path::{Path, PathBuf};
use fontdock::{FaceId, FontSource}; use fontdock::{FaceId, FontSource};
use image::io::Reader as ImageReader; use image::io::Reader as ImageReader;
use image::{DynamicImage, GenericImageView, ImageFormat}; use image::{DynamicImage, GenericImageView, ImageFormat};
use serde::{Deserialize, Serialize};
#[cfg(feature = "fs")] #[cfg(feature = "fs")]
use fontdock::{FsIndex, FsSource}; use fontdock::{FsIndex, FsSource};
@ -72,6 +73,7 @@ pub struct ResourceLoader {
/// A unique identifier for a resource. /// A unique identifier for a resource.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)]
pub struct ResourceId(usize); pub struct ResourceId(usize);
impl ResourceLoader { impl ResourceLoader {

View File

@ -10,7 +10,7 @@ use super::Value;
pub type Slot = Rc<RefCell<Value>>; pub type Slot = Rc<RefCell<Value>>;
/// A stack of scopes. /// A stack of scopes.
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Default, Debug, Clone, PartialEq)]
pub struct Scopes<'a> { pub struct Scopes<'a> {
/// The active scope. /// The active scope.
pub top: Scope, pub top: Scope,

View File

@ -69,7 +69,7 @@ impl Display for Angle {
impl Debug for Angle { impl Debug for Angle {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let unit = AngularUnit::Deg; let unit = AngularUnit::Deg;
write!(f, "{:?}{:?}", self.to_unit(unit), unit) write!(f, "{}{}", self.to_unit(unit), unit)
} }
} }
@ -134,7 +134,7 @@ impl Sum for Angle {
} }
} }
/// Different units of angular measurement. /// Different units of angular measurement.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum AngularUnit { pub enum AngularUnit {
/// Radians. /// Radians.
Rad, Rad,
@ -161,12 +161,6 @@ impl Display for AngularUnit {
} }
} }
impl Debug for AngularUnit {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,7 +1,10 @@
use super::*; use super::*;
use serde::{Deserialize, Serialize};
/// An absolute length. /// An absolute length.
#[derive(Default, Copy, Clone, PartialEq, PartialOrd)] #[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Length { pub struct Length {
/// The length in raw units. /// The length in raw units.
raw: f64, raw: f64,
@ -192,7 +195,7 @@ impl Sum for Length {
} }
/// Different units of length measurement. /// Different units of length measurement.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum LengthUnit { pub enum LengthUnit {
/// Points. /// Points.
Pt, Pt,
@ -227,12 +230,6 @@ impl Display for LengthUnit {
} }
} }
impl Debug for LengthUnit {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,11 +1,14 @@
use super::*; use super::*;
use serde::{Deserialize, Serialize};
/// A bezier path. /// A bezier path.
#[derive(Default, Debug, Clone, PartialEq)] #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Path(pub Vec<PathElement>); pub struct Path(pub Vec<PathElement>);
/// An element in a bezier path. /// An element in a bezier path.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PathElement { pub enum PathElement {
MoveTo(Point), MoveTo(Point),
LineTo(Point), LineTo(Point),

View File

@ -1,7 +1,9 @@
use super::*; use super::*;
use serde::{Deserialize, Serialize};
/// A point in 2D. /// A point in 2D.
#[derive(Default, Copy, Clone, PartialEq)] #[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Point { pub struct Point {
/// The x coordinate. /// The x coordinate.
pub x: Length, pub x: Length,

View File

@ -1,7 +1,9 @@
use super::*; use super::*;
use serde::{Deserialize, Serialize};
/// A size in 2D. /// A size in 2D.
#[derive(Default, Copy, Clone, PartialEq)] #[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Size { pub struct Size {
/// The width. /// The width.
pub width: Length, pub width: Length,

View File

@ -12,7 +12,7 @@ pub struct BackgroundNode {
} }
/// The kind of shape to use as a background. /// The kind of shape to use as a background.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum BackgroundShape { pub enum BackgroundShape {
Rect, Rect,
Ellipse, Ellipse,

View File

@ -1,12 +1,13 @@
use fontdock::FaceId; use fontdock::FaceId;
use ttf_parser::GlyphId;
use crate::color::Color; use crate::color::Color;
use crate::env::ResourceId; use crate::env::ResourceId;
use crate::geom::{Length, Path, Point, Size}; use crate::geom::{Length, Path, Point, Size};
use serde::{Deserialize, Serialize};
/// A finished layout with elements at fixed positions. /// A finished layout with elements at fixed positions.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Frame { pub struct Frame {
/// The size of the frame. /// The size of the frame.
pub size: Size, pub size: Size,
@ -37,7 +38,7 @@ impl Frame {
} }
/// The building block frames are composed of. /// The building block frames are composed of.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Element { pub enum Element {
/// Shaped text. /// Shaped text.
Text(Text), Text(Text),
@ -48,7 +49,7 @@ pub enum Element {
} }
/// A run of shaped text. /// A run of shaped text.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Text { pub struct Text {
/// The font face the glyphs are contained in. /// The font face the glyphs are contained in.
pub face_id: FaceId, pub face_id: FaceId,
@ -61,10 +62,10 @@ pub struct Text {
} }
/// A glyph in a run of shaped text. /// A glyph in a run of shaped text.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Glyph { pub struct Glyph {
/// The glyph's ID in the face. /// The glyph's ID in the face.
pub id: GlyphId, pub id: u16,
/// The advance width of the glyph. /// The advance width of the glyph.
pub x_advance: Length, pub x_advance: Length,
/// The horizontal offset of the glyph. /// The horizontal offset of the glyph.
@ -76,7 +77,7 @@ impl Text {
pub fn encode_glyphs_be(&self) -> Vec<u8> { pub fn encode_glyphs_be(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(2 * self.glyphs.len()); let mut bytes = Vec::with_capacity(2 * self.glyphs.len());
for glyph in &self.glyphs { for glyph in &self.glyphs {
let id = glyph.id.0; let id = glyph.id;
bytes.push((id >> 8) as u8); bytes.push((id >> 8) as u8);
bytes.push((id & 0xff) as u8); bytes.push((id & 0xff) as u8);
} }
@ -85,7 +86,7 @@ impl Text {
} }
/// A shape with some kind of fill. /// A shape with some kind of fill.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Geometry { pub struct Geometry {
/// The shape to draw. /// The shape to draw.
pub shape: Shape, pub shape: Shape,
@ -98,7 +99,7 @@ pub struct Geometry {
} }
/// Some shape. /// Some shape.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Shape { pub enum Shape {
/// A rectangle with its origin in the topleft corner. /// A rectangle with its origin in the topleft corner.
Rect(Size), Rect(Size),
@ -109,7 +110,7 @@ pub enum Shape {
} }
/// The kind of graphic fill to be applied to a [`Shape`]. /// The kind of graphic fill to be applied to a [`Shape`].
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum Fill { pub enum Fill {
/// The fill is a color. /// The fill is a color.
Color(Color), Color(Color),
@ -118,7 +119,7 @@ pub enum Fill {
} }
/// An image element. /// An image element.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Image { pub struct Image {
/// The image resource. /// The image resource.
pub res: ResourceId, pub res: ResourceId,

View File

@ -18,6 +18,7 @@ use crate::util::SliceExt;
/// This type contains owned or borrowed shaped text runs, which can be /// This type contains owned or borrowed shaped text runs, which can be
/// measured, used to reshape substrings more quickly and converted into a /// measured, used to reshape substrings more quickly and converted into a
/// frame. /// frame.
#[derive(Clone)]
pub struct ShapedText<'a> { pub struct ShapedText<'a> {
/// The text that was shaped. /// The text that was shaped.
pub text: &'a str, pub text: &'a str,
@ -53,6 +54,7 @@ pub struct ShapedGlyph {
} }
/// A visual side. /// A visual side.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum Side { enum Side {
Left, Left,
Right, Right,
@ -77,7 +79,11 @@ impl<'a> ShapedText<'a> {
for glyph in group { for glyph in group {
let x_advance = face.convert(glyph.x_advance).scale(self.props.size); let x_advance = face.convert(glyph.x_advance).scale(self.props.size);
let x_offset = face.convert(glyph.x_offset).scale(self.props.size); let x_offset = face.convert(glyph.x_offset).scale(self.props.size);
text.glyphs.push(Glyph { id: glyph.glyph_id, x_advance, x_offset }); text.glyphs.push(Glyph {
id: glyph.glyph_id.0,
x_advance,
x_offset,
});
offset += x_advance; offset += x_advance;
} }

View File

@ -37,15 +37,15 @@ impl Node {
/// Desugar markup into a function call. /// Desugar markup into a function call.
pub fn desugar(&self) -> Option<CallExpr> { pub fn desugar(&self) -> Option<CallExpr> {
match *self { match *self {
Node::Text(_) => None, Self::Text(_) => None,
Node::Space => None, Self::Space => None,
Node::Linebreak(span) => Some(call(span, Self::LINEBREAK)), Self::Linebreak(span) => Some(call(span, Self::LINEBREAK)),
Node::Parbreak(span) => Some(call(span, Self::PARBREAK)), Self::Parbreak(span) => Some(call(span, Self::PARBREAK)),
Node::Strong(span) => Some(call(span, Self::STRONG)), Self::Strong(span) => Some(call(span, Self::STRONG)),
Node::Emph(span) => Some(call(span, Self::EMPH)), Self::Emph(span) => Some(call(span, Self::EMPH)),
Self::Heading(ref heading) => Some(heading.desugar()), Self::Heading(ref heading) => Some(heading.desugar()),
Self::Raw(ref raw) => Some(raw.desugar()), Self::Raw(ref raw) => Some(raw.desugar()),
Node::Expr(_) => None, Self::Expr(_) => None,
} }
} }
} }

View File

@ -2,13 +2,15 @@ use std::cell::Cell;
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use std::ops::{Add, Range}; use std::ops::{Add, Range};
use serde::{Deserialize, Serialize};
thread_local! { thread_local! {
static CMP_SPANS: Cell<bool> = Cell::new(true); static CMP_SPANS: Cell<bool> = Cell::new(true);
} }
/// 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(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Serialize, Deserialize)]
pub struct Spanned<T> { pub struct Spanned<T> {
/// The spanned value. /// The spanned value.
pub v: T, pub v: T,
@ -53,8 +55,7 @@ impl<T: Debug> Debug for Spanned<T> {
} }
/// Bounds of a slice of source code. /// Bounds of a slice of source code.
#[derive(Copy, Clone, Ord, PartialOrd)] #[derive(Copy, Clone, Ord, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Span { pub struct Span {
/// The inclusive start position. /// The inclusive start position.
pub start: Pos, pub start: Pos,
@ -158,8 +159,7 @@ impl Debug for Span {
} }
/// A byte position in source code. /// A byte position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Pos(pub u32); pub struct Pos(pub u32);
impl Pos { impl Pos {
@ -208,8 +208,7 @@ impl Debug for Pos {
} }
/// A one-indexed line-column position in source code. /// A one-indexed line-column position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Location { pub struct Location {
/// The one-indexed line. /// The one-indexed line.
pub line: u32, pub line: u32,

View File

@ -11,7 +11,7 @@ use tiny_skia::{
Color, ColorU8, FillRule, FilterQuality, Paint, Pattern, Pixmap, Rect, SpreadMode, Color, ColorU8, FillRule, FilterQuality, Paint, Pattern, Pixmap, Rect, SpreadMode,
Transform, Transform,
}; };
use ttf_parser::OutlineBuilder; use ttf_parser::{GlyphId, OutlineBuilder};
use walkdir::WalkDir; use walkdir::WalkDir;
use typst::color; use typst::color;
@ -425,7 +425,7 @@ fn draw_text(canvas: &mut Pixmap, env: &Env, ts: Transform, shaped: &Text) {
// Try drawing SVG if present. // Try drawing SVG if present.
if let Some(tree) = ttf if let Some(tree) = ttf
.glyph_svg_image(glyph.id) .glyph_svg_image(GlyphId(glyph.id))
.and_then(|data| std::str::from_utf8(data).ok()) .and_then(|data| std::str::from_utf8(data).ok())
.map(|svg| { .map(|svg| {
let viewbox = format!("viewBox=\"0 0 {0} {0}\" xmlns", units_per_em); let viewbox = format!("viewBox=\"0 0 {0} {0}\" xmlns", units_per_em);
@ -448,7 +448,7 @@ fn draw_text(canvas: &mut Pixmap, env: &Env, ts: Transform, shaped: &Text) {
} else { } else {
// Otherwise, draw normal outline. // Otherwise, draw normal outline.
let mut builder = WrappedPathBuilder(tiny_skia::PathBuilder::new()); let mut builder = WrappedPathBuilder(tiny_skia::PathBuilder::new());
if ttf.outline_glyph(glyph.id, &mut builder).is_some() { if ttf.outline_glyph(GlyphId(glyph.id), &mut builder).is_some() {
let path = builder.0.finish().unwrap(); let path = builder.0.finish().unwrap();
let ts = ts.pre_scale(s, -s); let ts = ts.pre_scale(s, -s);
let mut paint = convert_typst_fill(shaped.color); let mut paint = convert_typst_fill(shaped.color);