#![doc = include_str!("../../../docs/encoded-glyphs.md")]
use super::{GlyphSurfaceRecord, GlyphSurfaceRecordError};
use crate::{
PayloadLimits,
font::GlyphMap,
image::{
ColorDescription, CoverageBudget, DecodeError, EncodedImageError, EncodedImageView,
EncodedSections, ImageDecodePlan, ImageGroups, SurfaceDescriptor, SurfaceRequirements,
UnitGroup,
},
media::MediaPayload,
};
impl GlyphSurfaceRecord {
pub(crate) fn encoded_glyphs<'map, 'data>(
self,
media: MediaPayload<'data>,
map: GlyphMap<'map>,
) -> Result<EncodedGlyphs<'map, 'data>, EncodedGlyphError> {
if self.codings_section().is_none() {
return Err(EncodedGlyphError::ExpectedEncodedStorage);
}
self.validate_sections(media)
.map_err(EncodedGlyphError::Record)?;
self.validate_map(map).map_err(EncodedGlyphError::Record)?;
let layout = self.sample_layout();
if !layout.is_alpha() {
return Err(EncodedGlyphError::Record(
GlyphSurfaceRecordError::UnsupportedLayout(layout),
));
}
let surface =
SurfaceDescriptor::new(map.width(), map.height(), layout, ColorDescription::NONE)
.expect("scalar glyph surface");
let image = EncodedImageView::from_sections(
media,
surface,
EncodedSections {
codings: self
.codings_section()
.and_then(|index| media.get(usize::from(index))),
data: media.get(usize::from(self.data_section())),
records: self
.groups_section()
.and_then(|index| media.get(usize::from(index))),
indexes: self
.index_section()
.and_then(|index| media.get(usize::from(index))),
color_table: None,
},
None,
)
.map_err(EncodedGlyphError::Encoding)?;
Ok(EncodedGlyphs { map, image })
}
}
#[derive(Clone, Copy, Debug)]
pub struct EncodedGlyphs<'map, 'data> {
map: GlyphMap<'map>,
image: EncodedImageView<'data>,
}
impl<'map, 'data> EncodedGlyphs<'map, 'data> {
pub(crate) fn preflight_in(
self,
preflight: &mut crate::image::RasterPreflight<'_>,
) -> Result<(), EncodedImageError> {
preflight.image(self.image)
}
pub const fn map(self) -> GlyphMap<'map> {
self.map
}
pub fn len(self) -> usize {
self.map.len()
}
pub fn is_empty(self) -> bool {
self.map.is_empty()
}
pub fn group_count(self) -> usize {
self.image.group_count()
}
pub fn input_alignment(self) -> Result<crate::ByteAlignment, EncodedGlyphError> {
self.image
.input_alignment()
.map_err(EncodedGlyphError::Encoding)
}
pub const fn with_file_offset(mut self, offset: u32) -> Self {
self.image = self.image.with_file_offset(offset);
self
}
pub fn preflight(self, limits: &PayloadLimits) -> Result<(), EncodedGlyphError> {
if self.len() > limits.max_font_glyphs() as usize {
return Err(EncodedGlyphError::TooManyGlyphs {
limit: limits.max_font_glyphs(),
actual: self.len(),
});
}
self.image
.preflight(limits)
.map_err(EncodedGlyphError::Encoding)
}
pub fn groups_into<'g>(
self,
workspace: &'g mut [Option<UnitGroup<'data>>],
budget: &mut CoverageBudget,
) -> Result<GlyphGroups<'map, 'data, 'g>, EncodedGlyphError> {
let groups = self
.image
.groups_into(workspace, budget)
.map_err(EncodedGlyphError::Encoding)?;
Ok(GlyphGroups {
map: self.map,
groups,
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct GlyphGroups<'map, 'data, 'g> {
map: GlyphMap<'map>,
groups: ImageGroups<'data, 'g>,
}
impl<'data, 'g> GlyphGroups<'_, 'data, 'g> {
pub fn len(self) -> usize {
self.map.len()
}
pub fn is_empty(self) -> bool {
self.map.is_empty()
}
pub const fn group_count(self) -> usize {
self.groups.len()
}
pub fn decode_surface_plan(
self,
requirements: SurfaceRequirements,
limits: &PayloadLimits,
) -> Result<ImageDecodePlan<'data, 'g>, EncodedGlyphError> {
self.groups
.decode_plan(requirements, limits)
.map_err(EncodedGlyphError::Decode)
}
pub fn decode_plan(
self,
index: usize,
requirements: SurfaceRequirements,
limits: &PayloadLimits,
) -> Result<ImageDecodePlan<'data, 'g>, EncodedGlyphError> {
let region = self
.map
.get(index)
.ok_or(EncodedGlyphError::GlyphOutOfBounds {
index,
count: self.len(),
})?;
self.groups
.decode_region_plan(region, requirements, limits)
.map_err(EncodedGlyphError::Decode)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum EncodedGlyphError {
ExpectedEncodedStorage,
Record(GlyphSurfaceRecordError),
Encoding(EncodedImageError),
Decode(DecodeError),
TooManyGlyphs { limit: u32, actual: usize },
GlyphOutOfBounds { index: usize, count: usize },
}
#[cfg(test)]
mod tests;