#[cfg(feature = "document-write")]
use std::collections::BTreeSet;
#[cfg(feature = "document-read")]
use super::codec::{Decode, Reader};
#[cfg(feature = "document-write")]
use super::codec::{Encode, Writer};
#[cfg(feature = "document-read")]
use super::DocumentError;
use super::codec::impl_codec;
#[cfg(feature = "document-write")]
use crate::plot::theme::{FontFamily, Theme};
#[cfg(feature = "document-write")]
use crate::plot::PlotComposition;
use crate::text::GenericFamilyKind;
#[cfg(any(feature = "document-read", feature = "document-write"))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct FontFace {
pub(crate) family: String,
pub(crate) index: u32,
pub(crate) bytes: Vec<u8>,
}
#[cfg(feature = "document-write")]
impl Encode for FontFace {
fn encode(&self, w: &mut Writer) {
self.family.encode(w);
self.index.encode(w);
w.bytes(&self.bytes);
}
}
#[cfg(feature = "document-read")]
impl Decode for FontFace {
fn decode(r: &mut Reader<'_>) -> Result<Self, DocumentError> {
Ok(FontFace {
family: String::decode(r)?,
index: u32::decode(r)?,
bytes: r.bytes()?.to_vec(),
})
}
}
#[cfg(any(feature = "document-read", feature = "document-write"))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct GenericMapping {
pub(crate) kind: GenericFamilyKind,
pub(crate) families: Vec<String>,
}
impl_codec! {
struct GenericMapping { kind, families }
}
#[cfg(any(feature = "document-read", feature = "document-write"))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct EmbeddedFonts {
pub(crate) faces: Vec<FontFace>,
pub(crate) generics: Vec<GenericMapping>,
}
impl_codec! {
struct EmbeddedFonts { faces, generics }
}
#[cfg(feature = "document-write")]
pub(crate) fn referenced_families(comp: &PlotComposition) -> (Vec<String>, Vec<GenericFamilyKind>) {
let mut named = BTreeSet::new();
let mut generic: Vec<GenericFamilyKind> = Vec::new();
collect_theme_families(&comp.theme, &mut named, &mut generic);
for plot in comp.plots.values().flatten() {
if let Some(part) = plot.theme_override_ref() {
if let Some(text) = &part.text {
collect_font_spec(&text.font.family, &mut named, &mut generic);
}
}
for (_, geom) in plot.geoms() {
if let Some(channel) = geom.state().channels.get("family") {
collect_channel_families(channel, &mut named);
}
}
}
push_generic(&mut generic, GenericFamilyKind::SansSerif);
(named.into_iter().collect(), generic)
}
#[cfg(feature = "document-write")]
fn push_generic(out: &mut Vec<GenericFamilyKind>, kind: GenericFamilyKind) {
if !out.contains(&kind) {
out.push(kind);
}
}
#[cfg(feature = "document-write")]
fn collect_theme_families(
theme: &Theme,
named: &mut BTreeSet<String>,
generic: &mut Vec<GenericFamilyKind>,
) {
collect_font_spec(&theme.text.font.family, named, generic);
for (_, delta) in theme.rich_text.iter() {
if let Some(family) = &delta.family {
named.insert(family.clone());
}
}
}
#[cfg(feature = "document-write")]
fn collect_font_spec(
family: &Option<FontFamily>,
named: &mut BTreeSet<String>,
generic: &mut Vec<GenericFamilyKind>,
) {
match family {
Some(FontFamily::Named(names)) => named.extend(names.iter().cloned()),
Some(FontFamily::Serif) => {
push_generic(generic, GenericFamilyKind::Serif);
}
Some(FontFamily::SansSerif) => {
push_generic(generic, GenericFamilyKind::SansSerif);
}
Some(FontFamily::Mono) => {
push_generic(generic, GenericFamilyKind::Mono);
}
Some(FontFamily::Cursive) => {
push_generic(generic, GenericFamilyKind::Cursive);
}
Some(FontFamily::Fantasy) => {
push_generic(generic, GenericFamilyKind::Fantasy);
}
Some(FontFamily::SystemUi) => {
push_generic(generic, GenericFamilyKind::SystemUi);
}
None => {}
}
}
#[cfg(feature = "document-write")]
fn collect_channel_families(channel: &crate::plot::Channel, out: &mut BTreeSet<String>) {
use crate::plot::Channel;
use crate::scales::value::{DataColumn, Value};
let mut push = |v: &Value| {
if let Value::String(s) = v {
out.insert(s.to_string());
}
};
match channel {
Channel::Constant(v) | Channel::RawConstant(v) => push(v),
Channel::Data(col) | Channel::RawData(col) => {
if let DataColumn::String(names) = col {
for n in names {
out.insert(n.to_string());
}
}
}
}
}
#[cfg(feature = "document-write")]
pub(crate) fn collect(named: &[String], generics: &[GenericFamilyKind]) -> EmbeddedFonts {
let mut generic_mappings = Vec::new();
let mut wanted: BTreeSet<String> = named.iter().cloned().collect();
for &kind in generics {
let families = crate::text::generic_family_names(kind);
if families.is_empty() {
continue;
}
wanted.extend(families.first().cloned());
generic_mappings.push(GenericMapping { kind, families });
}
let mut faces = Vec::new();
for family in &wanted {
for (bytes, index) in crate::text::font_faces_for_family(family) {
faces.push(FontFace {
family: family.clone(),
index,
bytes,
});
}
}
EmbeddedFonts {
faces,
generics: generic_mappings,
}
}
#[cfg(feature = "document-read")]
pub(crate) fn register(fonts: &EmbeddedFonts) {
for face in &fonts.faces {
crate::text::register_font_bytes(face.bytes.clone());
}
for mapping in &fonts.generics {
crate::text::set_generic_family(mapping.kind, &mapping.families);
}
}