use std::{
collections::HashMap,
fmt::Debug,
fs::File,
hash::Hash,
io::{self, BufReader, BufWriter, Read, Write},
sync::Arc,
};
use crate::{error::Error, ir, paths::Paths};
use bitflags::bitflags;
use fontdrasil::{
coords::NormalizedLocation,
orchestration::{Access, AccessControlList, Identifier, IdentifierDiscriminant, Work},
types::GlyphName,
};
use parking_lot::RwLock;
use write_fonts::{FontWrite, read::FontRead, validate::Validate};
bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct Flags: u32 {
const EMIT_IR = 0b00000001;
const EMIT_DEBUG = 0b00000010;
const PREFER_SIMPLE_GLYPHS = 0b00000100;
const FLATTEN_COMPONENTS = 0b00001000;
const DECOMPOSE_TRANSFORMED_COMPONENTS = 0b00010000;
const EMIT_TIMING = 0b00100000;
const KEEP_DIRECTION = 0b01000000;
const PRODUCTION_NAMES = 0b10000000;
const DECOMPOSE_COMPONENTS = 0b100000000;
const ERASE_OPEN_CORNERS = 0b1000000000;
}
}
impl Default for Flags {
fn default() -> Self {
Flags::PREFER_SIMPLE_GLYPHS | Flags::PRODUCTION_NAMES
}
}
#[derive(Default)]
pub struct ContextItem<I, T, P>
where
I: Identifier,
{
id: I,
acl: Arc<AccessControlList<I>>,
persistent_storage: Arc<P>,
value: Arc<RwLock<Option<Arc<T>>>>,
}
impl<I, T, P> ContextItem<I, T, P>
where
I: Identifier,
P: PersistentStorage<I>,
T: Persistable,
{
pub fn new(id: I, acl: Arc<AccessControlList<I>>, persistent_storage: Arc<P>) -> Self {
ContextItem {
id,
acl,
persistent_storage,
value: Default::default(),
}
}
pub fn clone_with_acl(&self, acl: Arc<AccessControlList<I>>) -> Self {
ContextItem {
id: self.id.clone(),
acl,
persistent_storage: self.persistent_storage.clone(),
value: self.value.clone(),
}
}
pub fn get(&self) -> Arc<T> {
if let Some(in_memory) = self.try_get() {
return in_memory;
}
if self.persistent_storage.active()
&& let Some(mut reader) = self.persistent_storage.reader(&self.id)
{
let restored = T::read(&mut reader);
*self.value.write() = Some(Arc::from(restored));
}
self.try_get()
.unwrap_or_else(|| panic!("{:?} is not available", self.id))
}
pub fn try_get(&self) -> Option<Arc<T>> {
self.acl.assert_read_access(&self.id);
self.value.read().as_ref().cloned()
}
}
impl<I, T, P> ContextItem<I, T, P>
where
I: Identifier,
T: PartialEq + Persistable,
P: PersistentStorage<I>,
{
pub fn set(&self, value: T) {
self.acl.assert_write_access(&self.id);
if self
.value
.read()
.as_ref()
.map(|arc| **arc == value)
.unwrap_or(false)
{
return;
}
if self.persistent_storage.active() {
let mut writer = self.persistent_storage.writer(&self.id);
value.write(&mut writer);
}
*self.value.write() = Some(Arc::from(value));
}
}
#[derive(Default)]
pub struct ContextMap<I, T, P>
where
I: Identifier,
T: IdAware<I>,
P: PersistentStorage<I>,
{
acl: Arc<AccessControlList<I>>,
persistent_storage: Arc<P>,
value: Arc<RwLock<HashMap<I, Arc<T>>>>,
}
impl<I, T, P> ContextMap<I, T, P>
where
I: Identifier,
T: IdAware<I> + Persistable,
P: PersistentStorage<I>,
{
pub fn new(acl: Arc<AccessControlList<I>>, persistent_storage: Arc<P>) -> Self {
ContextMap {
acl,
persistent_storage,
value: Default::default(),
}
}
pub fn clone_with_acl(&self, acl: Arc<AccessControlList<I>>) -> Self {
ContextMap {
acl,
persistent_storage: self.persistent_storage.clone(),
value: self.value.clone(),
}
}
pub fn try_get(&self, id: &I) -> Option<Arc<T>> {
self.acl.assert_read_access(id);
self.value.read().get(id).cloned()
}
pub fn all(&self) -> Vec<(I, Arc<T>)> {
self.value
.read()
.iter()
.map(|(id, v)| {
self.acl.assert_read_access(id);
(id.clone(), v.clone())
})
.collect()
}
pub fn get(&self, id: &I) -> Arc<T> {
if let Some(in_memory) = self.try_get(id) {
return in_memory;
}
if self.persistent_storage.active()
&& let Some(mut reader) = self.persistent_storage.reader(id)
{
let restored = T::read(&mut reader);
self.value.write().insert(id.clone(), Arc::from(restored));
}
self.try_get(id)
.unwrap_or_else(|| panic!("{id:?} is not available"))
}
}
impl<I, T, Ir> ContextMap<I, T, Ir>
where
I: Identifier,
T: IdAware<I> + Persistable,
Ir: PersistentStorage<I>,
{
pub fn set_unconditionally(&self, value: T) {
let key = value.id();
self.acl.assert_write_access(&key);
if self.persistent_storage.active() {
let mut writer = self.persistent_storage.writer(&key);
value.write(&mut writer);
}
self.value.write().insert(key, Arc::from(value));
}
}
impl<I, T, Ir> ContextMap<I, T, Ir>
where
I: Identifier,
T: IdAware<I> + PartialEq + Persistable,
Ir: PersistentStorage<I>,
{
pub fn set(&self, value: T) {
let key = value.id();
self.acl.assert_write_access(&key);
if self
.value
.read()
.get(&key)
.map(|arc| **arc == value)
.unwrap_or(false)
{
return;
}
self.set_unconditionally(value);
}
}
pub trait IdAware<I> {
fn id(&self) -> I;
}
pub trait Persistable {
fn read(from: &mut dyn Read) -> Self;
fn write(&self, to: &mut dyn Write);
}
pub trait PersistentStorage<I> {
fn active(&self) -> bool;
fn reader(&self, id: &I) -> Option<Box<dyn Read>>;
fn writer(&self, id: &I) -> Box<dyn Write>;
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum WorkId {
StaticMetadata,
GlobalMetrics,
Glyph(GlyphName),
PreliminaryGlyphOrder,
GlyphOrder,
Features,
KerningGroups,
KernInstance(NormalizedLocation),
Anchor(GlyphName),
ColorPalettes,
PaintGraph,
}
impl WorkId {
pub const ALL_GLYPHS: Self = WorkId::Glyph(GlyphName::NOTDEF);
pub const ALL_ANCHORS: Self = WorkId::Anchor(GlyphName::NOTDEF);
}
impl Identifier for WorkId {
fn discriminant(&self) -> IdentifierDiscriminant {
match self {
WorkId::StaticMetadata => "IrStaticMetadata",
WorkId::GlobalMetrics => "IrGlobalMetrics",
WorkId::Glyph(..) => "IrGlyph",
WorkId::PreliminaryGlyphOrder => "IrPreliminaryGlyphOrder",
WorkId::GlyphOrder => "IrGlyphOrder",
WorkId::Features => "IrFeatures",
WorkId::KerningGroups => "IrKerningGroups",
WorkId::KernInstance(..) => "IrKernInstance",
WorkId::Anchor(..) => "IrAnchor",
WorkId::ColorPalettes => "IrPalettes",
WorkId::PaintGraph => "IrPaints",
}
}
}
pub type IrWork = dyn Work<Context, WorkId, Error> + Send;
pub struct IrPersistentStorage {
active: bool,
pub(crate) paths: Paths,
}
impl PersistentStorage<WorkId> for IrPersistentStorage {
fn active(&self) -> bool {
self.active
}
fn reader(&self, id: &WorkId) -> Option<Box<dyn Read>> {
let file = self.paths.target_file(id);
if !file.exists() {
return None;
}
let raw_file = File::open(file.clone())
.map_err(|e| panic!("Unable to write {file:?} {e}"))
.unwrap();
Some(Box::from(BufReader::new(raw_file)))
}
fn writer(&self, id: &WorkId) -> Box<dyn Write> {
let file = self.paths.target_file(id);
let raw_file = File::create(file.clone())
.map_err(|e| panic!("Unable to write {file:?} {e}"))
.unwrap();
Box::from(BufWriter::new(raw_file))
}
}
impl<T> Persistable for T
where
for<'a> T: FontRead<'a> + FontWrite + Validate,
{
fn read(from: &mut dyn Read) -> Self {
let mut buf = Vec::new();
from.read_to_end(&mut buf).unwrap();
T::read(buf.as_slice().into()).expect("if we wrote it we can read it")
}
fn write(&self, to: &mut dyn io::Write) {
let bytes = write_fonts::dump_table(self).unwrap();
to.write_all(&bytes).unwrap();
}
}
type FeContextItem<T> = ContextItem<WorkId, T, IrPersistentStorage>;
type FeContextMap<T> = ContextMap<WorkId, T, IrPersistentStorage>;
pub struct Context {
pub flags: Flags,
pub(crate) persistent_storage: Arc<IrPersistentStorage>,
pub static_metadata: FeContextItem<ir::StaticMetadata>,
pub preliminary_glyph_order: FeContextItem<ir::GlyphOrder>,
pub glyph_order: FeContextItem<ir::GlyphOrder>,
pub global_metrics: FeContextItem<ir::GlobalMetrics>,
pub glyphs: FeContextMap<ir::Glyph>,
pub features: FeContextItem<ir::FeaturesSource>,
pub kerning_groups: FeContextItem<ir::KerningGroups>,
pub kerning_at: FeContextMap<ir::KerningInstance>,
pub anchors: FeContextMap<ir::GlyphAnchors>,
pub colors: FeContextItem<ir::ColorPalettes>,
pub paint_graph: FeContextItem<ir::ColorGlyphs>,
}
pub fn set_cached<T>(lock: &Arc<RwLock<Option<Arc<T>>>>, value: T) {
let mut wl = lock.write();
*wl = Some(Arc::from(value));
}
impl Context {
fn copy(&self, acl: AccessControlList<WorkId>) -> Context {
let acl = Arc::from(acl);
Context {
flags: self.flags,
persistent_storage: self.persistent_storage.clone(),
static_metadata: self.static_metadata.clone_with_acl(acl.clone()),
preliminary_glyph_order: self.preliminary_glyph_order.clone_with_acl(acl.clone()),
glyph_order: self.glyph_order.clone_with_acl(acl.clone()),
global_metrics: self.global_metrics.clone_with_acl(acl.clone()),
glyphs: self.glyphs.clone_with_acl(acl.clone()),
features: self.features.clone_with_acl(acl.clone()),
kerning_groups: self.kerning_groups.clone_with_acl(acl.clone()),
kerning_at: self.kerning_at.clone_with_acl(acl.clone()),
anchors: self.anchors.clone_with_acl(acl.clone()),
colors: self.colors.clone_with_acl(acl.clone()),
paint_graph: self.paint_graph.clone_with_acl(acl),
}
}
pub fn new_root(flags: Flags, paths: Paths) -> Context {
let acl = Arc::from(AccessControlList::read_only());
let persistent_storage = Arc::from(IrPersistentStorage {
active: flags.contains(Flags::EMIT_IR),
paths,
});
Context {
flags,
persistent_storage: persistent_storage.clone(),
static_metadata: ContextItem::new(
WorkId::StaticMetadata,
acl.clone(),
persistent_storage.clone(),
),
preliminary_glyph_order: ContextItem::new(
WorkId::PreliminaryGlyphOrder,
acl.clone(),
persistent_storage.clone(),
),
glyph_order: ContextItem::new(
WorkId::GlyphOrder,
acl.clone(),
persistent_storage.clone(),
),
global_metrics: ContextItem::new(
WorkId::GlobalMetrics,
acl.clone(),
persistent_storage.clone(),
),
glyphs: ContextMap::new(acl.clone(), persistent_storage.clone()),
features: ContextItem::new(WorkId::Features, acl.clone(), persistent_storage.clone()),
kerning_groups: ContextItem::new(
WorkId::KerningGroups,
acl.clone(),
persistent_storage.clone(),
),
kerning_at: ContextMap::new(acl.clone(), persistent_storage.clone()),
anchors: ContextMap::new(acl.clone(), persistent_storage.clone()),
colors: ContextItem::new(
WorkId::ColorPalettes,
acl.clone(),
persistent_storage.clone(),
),
paint_graph: ContextItem::new(WorkId::PaintGraph, acl, persistent_storage),
}
}
pub fn copy_for_work(
&self,
read_access: Access<WorkId>,
write_access: Access<WorkId>,
) -> Context {
self.copy(AccessControlList::read_write(read_access, write_access))
}
pub fn read_only(&self) -> Context {
self.copy(AccessControlList::read_only())
}
pub fn get_glyph(&self, name: impl Into<GlyphName>) -> Arc<ir::Glyph> {
let id = WorkId::Glyph(name.into());
self.glyphs.get(&id)
}
pub fn try_get_glyph(&self, name: impl Into<GlyphName>) -> Option<Arc<ir::Glyph>> {
let id = WorkId::Glyph(name.into());
self.glyphs.try_get(&id)
}
pub fn get_anchor(&self, name: impl Into<GlyphName>) -> Arc<ir::GlyphAnchors> {
let id = WorkId::Anchor(name.into());
self.anchors.get(&id)
}
}