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
.vscode
_things
desktop.ini
# Tests and benchmarks
tests/png

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -69,7 +69,7 @@ impl Display for Angle {
impl Debug for Angle {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
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.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum AngularUnit {
/// Radians.
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)]
mod tests {
use super::*;

View File

@ -1,7 +1,10 @@
use super::*;
use serde::{Deserialize, Serialize};
/// An absolute length.
#[derive(Default, Copy, Clone, PartialEq, PartialOrd)]
#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Length {
/// The length in raw units.
raw: f64,
@ -192,7 +195,7 @@ impl Sum for Length {
}
/// Different units of length measurement.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum LengthUnit {
/// Points.
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)]
mod tests {
use super::*;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -37,15 +37,15 @@ impl Node {
/// Desugar markup into a function call.
pub fn desugar(&self) -> Option<CallExpr> {
match *self {
Node::Text(_) => None,
Node::Space => None,
Node::Linebreak(span) => Some(call(span, Self::LINEBREAK)),
Node::Parbreak(span) => Some(call(span, Self::PARBREAK)),
Node::Strong(span) => Some(call(span, Self::STRONG)),
Node::Emph(span) => Some(call(span, Self::EMPH)),
Self::Text(_) => None,
Self::Space => None,
Self::Linebreak(span) => Some(call(span, Self::LINEBREAK)),
Self::Parbreak(span) => Some(call(span, Self::PARBREAK)),
Self::Strong(span) => Some(call(span, Self::STRONG)),
Self::Emph(span) => Some(call(span, Self::EMPH)),
Self::Heading(ref heading) => Some(heading.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::ops::{Add, Range};
use serde::{Deserialize, Serialize};
thread_local! {
static CMP_SPANS: Cell<bool> = Cell::new(true);
}
/// A value with the span it corresponds to in the source code.
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Serialize, Deserialize)]
pub struct Spanned<T> {
/// The spanned value.
pub v: T,
@ -53,8 +55,7 @@ impl<T: Debug> Debug for Spanned<T> {
}
/// Bounds of a slice of source code.
#[derive(Copy, Clone, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Copy, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Span {
/// The inclusive start position.
pub start: Pos,
@ -158,8 +159,7 @@ impl Debug for Span {
}
/// A byte position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Pos(pub u32);
impl Pos {
@ -208,8 +208,7 @@ impl Debug for Pos {
}
/// A one-indexed line-column position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Location {
/// The one-indexed line.
pub line: u32,

View File

@ -11,7 +11,7 @@ use tiny_skia::{
Color, ColorU8, FillRule, FilterQuality, Paint, Pattern, Pixmap, Rect, SpreadMode,
Transform,
};
use ttf_parser::OutlineBuilder;
use ttf_parser::{GlyphId, OutlineBuilder};
use walkdir::WalkDir;
use typst::color;
@ -425,7 +425,7 @@ fn draw_text(canvas: &mut Pixmap, env: &Env, ts: Transform, shaped: &Text) {
// Try drawing SVG if present.
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())
.map(|svg| {
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 {
// Otherwise, draw normal outline.
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 ts = ts.pre_scale(s, -s);
let mut paint = convert_typst_fill(shaped.color);