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:
parent
df58a4d89b
commit
72478946c2
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
# General
|
||||
.vscode
|
||||
_things
|
||||
desktop.ini
|
||||
|
||||
# Tests and benchmarks
|
||||
tests/png
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
11
src/diag.rs
11
src/diag.rs
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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::*;
|
||||
|
@ -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::*;
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user