use super::{
FontAsset, FontError, GlyphMap, GlyphSurfaceAsset, Plan, RepresentationAsset, Storage,
};
use crate::{PayloadLimits, image::RasterPreflight, media::INTEGRITY_RECORD_LEN};
pub(in crate::font) trait Source: Copy {
fn codepoints(&self) -> &[char];
fn representation_count(&self) -> usize;
fn surface_count(&self) -> usize;
fn map_count(&self) -> usize;
fn representation(&self, index: usize) -> Option<RepresentationAsset<'_>>;
fn surface(&self, index: usize) -> Option<GlyphSurfaceAsset<'_>>;
fn map(&self, index: usize) -> Option<GlyphMap<'_>>;
fn representations(&self) -> impl ExactSizeIterator<Item = RepresentationAsset<'_>> {
(0..self.representation_count())
.map(|index| self.representation(index).expect("representation ordinal"))
}
fn surfaces(&self) -> impl ExactSizeIterator<Item = GlyphSurfaceAsset<'_>> {
(0..self.surface_count()).map(|index| self.surface(index).expect("surface ordinal"))
}
fn maps(&self) -> impl ExactSizeIterator<Item = GlyphMap<'_>> {
(0..self.map_count()).map(|index| self.map(index).expect("map ordinal"))
}
fn preflight(self, limits: &PayloadLimits) -> Result<(), FontError> {
if self.codepoints().len() > limits.max_font_glyphs() as usize {
return Err(FontError::TooManyGlyphs {
limit: limits.max_font_glyphs(),
actual: self.codepoints().len(),
});
}
if self.representation_count() > limits.max_font_representations() as usize {
return Err(FontError::TooManyRepresentations {
limit: limits.max_font_representations(),
actual: self.representation_count(),
});
}
Plan::table_size(self)?;
let mut preflight = RasterPreflight::new(limits, 0).map_err(FontError::Raster)?;
let charge = |p: &mut RasterPreflight<'_>, n| p.spend(n).map_err(FontError::Raster);
let count = self.representation_count() as u64;
let metadata_work = count
.checked_mul(
count
+ self.codepoints().len() as u64
+ self.surface_count() as u64
+ self.map_count() as u64,
)
.and_then(|n| n.checked_add(self.codepoints().len() as u64))
.ok_or(FontError::SizeOverflow)?;
charge(&mut preflight, metadata_work)?;
for surface in self.surfaces() {
charge(
&mut preflight,
surface
.integrity()
.partitions()
.map_or(0, |p| p.len() as u64 * INTEGRITY_RECORD_LEN as u64),
)?;
if let GlyphSurfaceAsset::Encoded { image, .. } = surface {
charge(&mut preflight, image.codings().len() as u64 * 8)?;
let bytes = image
.codings()
.try_fold(0u64, |n, c| n.checked_add(c.params().len() as u64))
.ok_or(FontError::SizeOverflow)?;
charge(
&mut preflight,
bytes
+ image.groups().map_or(0, |g| g.len() as u64 * 36)
+ image.unit_index().len() as u64,
)?;
}
}
let plan = Plan::metadata(self)?;
charge(&mut preflight, plan.len as u64)?;
for surface in self.surfaces() {
if let Storage::Encoded(storage) = surface.plan()? {
storage
.preflight_in(&mut preflight)
.map_err(FontError::Image)?;
}
}
Ok(())
}
}
impl Source for FontAsset<'_> {
fn codepoints(&self) -> &[char] {
self.codepoints
}
fn representation_count(&self) -> usize {
self.representations.len()
}
fn surface_count(&self) -> usize {
self.surfaces.len()
}
fn map_count(&self) -> usize {
self.maps.len()
}
fn representation(&self, index: usize) -> Option<RepresentationAsset<'_>> {
self.representations.get(index).copied()
}
fn surface(&self, index: usize) -> Option<GlyphSurfaceAsset<'_>> {
self.surfaces.get(index).copied()
}
fn map(&self, index: usize) -> Option<GlyphMap<'_>> {
self.maps.get(index).copied()
}
}