use super::payload::resolve_node_payload;
use super::{Document, DocumentChunkRef, DocumentState, EditError};
use crate::font::{Font, FontAccessError, FontError, FontView};
use crate::payload::image::ImagePayloadError;
use crate::{ChunkFlags, ChunkId, ChunkType};
impl<'a> DocumentChunkRef<'a> {
pub fn font(&self) -> Result<FontView<'a>, FontAccessError> {
if self.chunk_type() != ChunkType::FONT {
return Err(FontAccessError::UnexpectedChunkType {
actual: self.chunk_type(),
});
}
resolve_node_payload(self.document(), self.node())
.map_err(font_access_resolution_error)?
.font_view(&self.document().payload_limits)
.map_err(Into::into)
}
pub fn decode_font(&self) -> Result<Font, FontAccessError> {
if self.chunk_type() != ChunkType::FONT {
return Err(FontAccessError::UnexpectedChunkType {
actual: self.chunk_type(),
});
}
self.font()?;
let payload = resolve_node_payload(self.document(), self.node())
.map_err(font_access_resolution_error)?;
let bytes = payload
.bytes()
.ok_or(FontAccessError::NonContiguousPayload)?;
Font::decode_with_limits(bytes, &self.document().payload_limits).map_err(Into::into)
}
}
impl Document<'_> {
pub(super) fn decode_font_at(&self, id: ChunkId) -> Result<Font, FontAccessError> {
let DocumentState::Chunk(chunks) = &self.state else {
return Err(FontAccessError::ChunkLayoutRequired);
};
let node = chunks
.chunks
.iter()
.find(|node| node.id == id)
.ok_or(FontAccessError::InvalidChunkId)?;
if node.chunk_type != ChunkType::FONT {
return Err(FontAccessError::UnexpectedChunkType {
actual: node.chunk_type,
});
}
let payload = resolve_node_payload(self, node).map_err(font_access_resolution_error)?;
let bytes = payload
.bytes()
.ok_or(FontAccessError::NonContiguousPayload)?;
payload
.font_view(&self.payload_limits)
.map_err(FontAccessError::from)?;
Font::decode_with_limits(bytes, &self.payload_limits).map_err(Into::into)
}
pub fn push_font(&mut self, font: &Font) -> Result<ChunkId, EditError> {
self.push_font_with_flags(font, ChunkFlags::NONE)
}
pub fn push_font_with_flags(
&mut self,
font: &Font,
flags: ChunkFlags,
) -> Result<ChunkId, EditError> {
let limits = self.payload_limits;
self.push_typed_owned_with(ChunkType::FONT, flags, || {
font.preflight(&limits)
.map_err(font_encode_error_for_edit)?;
font.encode().map_err(font_encode_error_for_edit)
})
}
pub(super) fn replace_font(&mut self, id: ChunkId, font: &Font) -> Result<(), EditError> {
let limits = self.payload_limits;
self.replace_typed_owned_with(
id,
ChunkType::FONT,
|| {
font.preflight(&limits)
.map_err(font_encode_error_for_edit)?;
Ok(font)
},
|plan, existing| {
let Some(bytes) = existing.bytes() else {
return Ok(false);
};
if !plan
.matches_payload(bytes)
.map_err(font_encode_error_for_edit)?
{
return Ok(false);
}
Ok(existing.font_view(&limits).is_ok())
},
|plan| plan.encode().map_err(font_encode_error_for_edit),
font_edit_resolution_error,
)
}
pub(super) fn begin_font_edit(&mut self, id: ChunkId) -> Result<Font, EditError> {
self.ensure_mutable()?;
self.decode_font_at(id).map_err(font_access_error_for_edit)
}
}
fn font_encode_error_for_edit(error: FontError) -> EditError {
match error {
FontError::AllocationFailed => EditError::AllocationFailed,
error => EditError::InvalidFont(error),
}
}
fn font_access_resolution_error(_: ImagePayloadError) -> FontAccessError {
FontAccessError::InvalidPayload(FontError::SizeOverflow)
}
fn font_edit_resolution_error(_: ImagePayloadError) -> EditError {
font_encode_error_for_edit(FontError::SizeOverflow)
}
fn font_access_error_for_edit(error: FontAccessError) -> EditError {
match error {
FontAccessError::ChunkLayoutRequired => EditError::ChunkLayoutRequired,
FontAccessError::InvalidChunkId => EditError::InvalidChunkId,
FontAccessError::UnexpectedChunkType { .. } => EditError::InvalidChunkType,
FontAccessError::NonContiguousPayload => EditError::NonContiguousPayload {
chunk_type: ChunkType::FONT,
},
FontAccessError::InvalidPayload(error) => font_encode_error_for_edit(error),
FontAccessError::AllocationFailed => EditError::AllocationFailed,
}
}
#[cfg(test)]
mod tests;