use crate::color::Color;
use crate::layout::{Extent, Inset, Track};
use crate::plot::{FormatSpec, PlotComposition};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UnsupportedItem {
CustomFormatter {
scale: String,
},
UnnameableGeom {
patch: String,
index: usize,
},
TrackReference {
location: String,
},
}
impl std::fmt::Display for UnsupportedItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::CustomFormatter { scale } => write!(
f,
"scale {scale:?} has an anonymous label formatter; name it with \
`with_named_format` so a reader can resolve it"
),
Self::UnnameableGeom { patch, index } => write!(
f,
"geom {index} on patch {patch:?} does not implement `Geom::kind`, \
so nothing identifies how to rebuild it"
),
Self::TrackReference { location } => write!(
f,
"{location} is sized relative to another grid's track, which is \
identified by a per-solve id and cannot be carried"
),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct WriteOptions {
pub lossy: bool,
pub background: Option<Color>,
pub size_hint: Option<(f64, f64)>,
pub dpi_hint: Option<f64>,
pub embed_fonts: bool,
}
impl WriteOptions {
pub fn new() -> Self {
Self::default()
}
pub fn lossy(mut self, lossy: bool) -> Self {
self.lossy = lossy;
self
}
pub fn background(mut self, color: Color) -> Self {
self.background = Some(color);
self
}
pub fn size_hint(mut self, width: f64, height: f64) -> Self {
self.size_hint = Some((width, height));
self
}
pub fn dpi_hint(mut self, dpi: f64) -> Self {
self.dpi_hint = Some(dpi);
self
}
pub fn embed_fonts(mut self, embed: bool) -> Self {
self.embed_fonts = embed;
self
}
}
pub fn unsupported_items(comp: &PlotComposition) -> Vec<UnsupportedItem> {
let mut out = Vec::new();
for (name, scale) in comp.scales.iter() {
if scale.format_spec() == FormatSpec::Custom {
out.push(UnsupportedItem::CustomFormatter {
scale: name.to_string(),
});
}
}
for patch in &comp.plot_order {
for plot in comp.plots.get(patch).into_iter().flatten() {
for (index, (_, geom)) in plot.geoms().enumerate() {
if geom.kind().is_none() {
out.push(UnsupportedItem::UnnameableGeom {
patch: patch.clone(),
index,
});
}
}
}
}
check_template_tracks(&comp.template, &mut out);
out
}
fn check_template_tracks(
template: &crate::plot::composition::CompositionTemplate,
out: &mut Vec<UnsupportedItem>,
) {
let id = template.id.as_deref().unwrap_or("<unnamed>");
for (axis, tracks) in [("width", &template.widths), ("height", &template.heights)] {
for (i, track) in tracks.iter().enumerate() {
if let Track::Fixed(e) = track {
if extent_has_track_ref(e) {
out.push(UnsupportedItem::TrackReference {
location: format!("composition {id:?} {axis} track {i}"),
});
}
}
}
}
for (what, inset) in [("margin", &template.margin), ("padding", &template.padding)] {
if inset_has_track_ref(inset) {
out.push(UnsupportedItem::TrackReference {
location: format!("composition {id:?} {what}"),
});
}
}
for placement in &template.placements {
if let crate::plot::composition::ElementTemplate::Composition(nested) = &placement.element {
check_template_tracks(nested, out);
}
}
}
fn extent_has_track_ref(e: &Extent) -> bool {
match e {
Extent::TrackOf { .. } => true,
Extent::Min(a, b) | Extent::Max(a, b) => extent_has_track_ref(a) || extent_has_track_ref(b),
Extent::Sum { .. } => false,
}
}
fn inset_has_track_ref(i: &Inset) -> bool {
[&i.left, &i.right, &i.top, &i.bottom, &i.width, &i.height]
.into_iter()
.flatten()
.any(extent_has_track_ref)
}