WIP adding rendering

This commit is contained in:
Jacob Lifshay 2025-12-29 03:18:28 -08:00
parent aba6368948
commit 9445599850
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
10 changed files with 2271 additions and 147 deletions

View file

@ -1,6 +1,6 @@
use crate::{
pdf::{
PdfObjects,
PdfObjectAndParseCache, PdfObjects,
parse::{
GetPdfInputPosition, PdfInputPosition, PdfInputPositionNoCompare, PdfParse,
PdfParseError,
@ -8,7 +8,7 @@ use crate::{
stream_filters::PdfStreamFilter,
},
pdf_parse,
util::ArcOrRef,
util::{ArcOrRef, DagDebugState},
};
use std::{
any::TypeId,
@ -892,17 +892,35 @@ impl PdfObjectIndirect {
final_id: Arc::new(OnceLock::new()),
}
}
pub fn get(&self) -> PdfObjectDirect {
let Some(objects) = self.objects.upgrade() else {
panic!("PdfObjects is no longer available");
};
pub(crate) fn cache_parse<T: 'static + Send + Sync, E>(
&self,
parse_inner: impl FnOnce(PdfObjectDirect) -> Result<Arc<T>, E>,
) -> Result<Arc<T>, E> {
self.get_object_and_parse_cache(|object, object_and_parse_cache| {
match object_and_parse_cache {
Some(object_and_parse_cache) => {
if let Some(retval) = object_and_parse_cache.parse_cache_get::<T>() {
println!("cache reused for {object:?}");
return Ok(retval);
}
parse_inner(object)
.map(|retval| object_and_parse_cache.parse_cache_get_or_insert::<T>(retval))
}
None => parse_inner(object),
}
})
}
fn get_object_and_parse_cache_inner<'a>(
&self,
objects: &'a PdfObjects,
) -> (PdfObjectDirect, Option<&'a PdfObjectAndParseCache>) {
if let Some(objects) = objects.inner.get() {
let final_id = self.final_id.get().copied();
let limit = if final_id.is_some() { 1 } else { 1000usize };
let mut id = final_id.unwrap_or(self.id);
for _ in 0..limit {
if let Some(object) = objects.objects.get(&self.id) {
let retval = match object {
if let Some(object_and_parse_cache) = objects.objects.get(&self.id) {
let object = match &object_and_parse_cache.object {
PdfObject::Boolean(v) => PdfObjectDirect::Boolean(*v),
PdfObject::Integer(v) => PdfObjectDirect::Integer(*v),
PdfObject::Real(v) => PdfObjectDirect::Real(*v),
@ -919,13 +937,26 @@ impl PdfObjectIndirect {
};
// we could be racing with another thread, so set can fail but that's not a problem
let _ = self.final_id.set(id);
return retval;
return (object, Some(object_and_parse_cache));
} else {
return PdfObjectDirect::Null(PdfNull::new(id.pos));
return (PdfNull::new(id.pos).into(), None);
}
}
}
PdfObjectDirect::Null(PdfNull::new(self.pos()))
(PdfNull::new(self.pos()).into(), None)
}
fn get_object_and_parse_cache<R>(
&self,
f: impl FnOnce(PdfObjectDirect, Option<&PdfObjectAndParseCache>) -> R,
) -> R {
let Some(objects) = self.objects.upgrade() else {
panic!("PdfObjects is no longer available");
};
let (object, object_and_parse_cache) = self.get_object_and_parse_cache_inner(&objects);
f(object, object_and_parse_cache)
}
pub fn get(&self) -> PdfObjectDirect {
self.get_object_and_parse_cache(|object, _object_and_parse_cache| object)
}
pub fn id(&self) -> PdfObjectIdentifier {
self.id
@ -1067,9 +1098,17 @@ impl<'a, T> IntoIterator for &'a PdfDictionary<T> {
}
}
impl<T: fmt::Debug> fmt::Debug for PdfDictionary<T> {
impl<T: fmt::Debug + 'static> fmt::Debug for PdfDictionary<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self).finish()
DagDebugState::scope(|state| {
state
.debug_or_id_with(
&self.fields,
|_, f| f.debug_map().entries(self).finish(),
|f| f.write_str("{...}"),
)
.fmt(f)
})
}
}
@ -1364,6 +1403,31 @@ pub struct PdfMatrix {
pub elements: [f32; 6],
}
impl PdfMatrix {
pub fn identity(pos: impl Into<PdfInputPositionNoCompare>) -> Self {
Self {
pos: pos.into(),
elements: [1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
}
}
#[must_use]
pub fn mul(self, other: PdfMatrix, new_pos: impl Into<PdfInputPositionNoCompare>) -> Self {
let [la, lb, lc, ld, le, lf] = self.elements;
let [ra, rb, rc, rd, re, rf] = other.elements;
Self {
pos: new_pos.into(),
elements: [
lb * rc + la * ra,
lb * rd + la * rb,
ld * rc + lc * ra,
ld * rd + lc * rb,
re + lf * rc + le * ra,
rf + lf * rd + le * rb,
],
}
}
}
impl fmt::Debug for PdfMatrix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { pos, elements } = *self;
@ -1582,7 +1646,7 @@ impl PdfParse for PdfFileSpecification {
pdf_parse! {
#[pdf]
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct PdfStreamDictionary<Rest = PdfDictionary> {
#[pdf(name = "Length")]
pub len: usize,
@ -1603,6 +1667,33 @@ pdf_parse! {
}
}
impl<Rest: fmt::Debug> fmt::Debug for PdfStreamDictionary<Rest> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
DagDebugState::scope(|_state| {
let Self {
len,
filters,
decode_parms,
file,
file_filters,
file_decode_parms,
decoded_len,
rest,
} = self;
f.debug_struct("PdfStreamDictionary")
.field("len", len)
.field("filters", filters)
.field("decode_parms", decode_parms)
.field("file", file)
.field("file_filters", file_filters)
.field("file_decode_parms", file_decode_parms)
.field("decoded_len", decoded_len)
.field("rest", rest)
.finish()
})
}
}
#[derive(Debug, Clone, Default)]
pub struct PdfStreamDictionaryFiltersAndParms<'a> {
filters: std::iter::Enumerate<std::slice::Iter<'a, PdfStreamFilter>>,
@ -1697,23 +1788,6 @@ impl<Rest> PdfStreamDictionary<Rest> {
}
}
pub(crate) struct UnparsedPdfStreamDictionary<Rest> {
unparsed_dictionary: PdfDictionary,
dictionary: Arc<OnceLock<PdfStreamDictionary<Rest>>>,
}
impl<Rest: PdfParse> UnparsedPdfStreamDictionary<Rest> {
pub(crate) fn finish_parsing(self) -> Result<(), PdfParseError> {
let Ok(()) = self
.dictionary
.set(PdfParse::parse(self.unparsed_dictionary.into())?)
else {
unreachable!();
};
Ok(())
}
}
pub trait PdfStreamContents: Sized + fmt::Debug + 'static {
fn parse(
data: &[u8],
@ -1786,34 +1860,45 @@ impl fmt::Display for DumpBytes<'_> {
impl<Rest: fmt::Debug, Data: PdfStreamContents> fmt::Debug for PdfStream<Rest, Data> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
pos,
objects: _,
dictionary,
encoded_data,
decoded_data,
} = self;
let mut debug_struct = f.debug_struct("PdfStream");
debug_struct.field("pos", pos);
debug_struct.field("dictionary", dictionary);
debug_struct.field("encoded_data", &DumpBytes(encoded_data));
if let Some(decoded_data) = decoded_data.get() {
match decoded_data {
Ok(decoded_data) => {
if let Some(decoded_data) =
<dyn std::any::Any>::downcast_ref::<Arc<[u8]>>(decoded_data)
{
debug_struct.field("decoded_data", &DumpBytes(&**decoded_data))
} else {
debug_struct.field("decoded_data", decoded_data)
}
}
Err(e) => debug_struct.field("decoded_data", &Err::<(), _>(e)),
};
} else {
debug_struct.field("decoded_data", &format_args!("<not-yet-decoded>"));
}
debug_struct.finish()
DagDebugState::scope(|state| {
state
.debug_or_id_with(
&self.decoded_data,
|_, f| {
let Self {
pos,
objects: _,
dictionary,
encoded_data,
decoded_data,
} = self;
let mut debug_struct = f.debug_struct("PdfStream");
debug_struct.field("pos", pos);
debug_struct.field("dictionary", dictionary);
debug_struct.field("encoded_data", &DumpBytes(encoded_data));
if let Some(decoded_data) = decoded_data.get() {
match decoded_data {
Ok(decoded_data) => {
if let Some(decoded_data) =
<dyn std::any::Any>::downcast_ref::<Arc<[u8]>>(decoded_data)
{
debug_struct
.field("decoded_data", &DumpBytes(&**decoded_data))
} else {
debug_struct.field("decoded_data", decoded_data)
}
}
Err(e) => debug_struct.field("decoded_data", &Err::<(), _>(e)),
};
} else {
debug_struct.field("decoded_data", &format_args!("<not-yet-decoded>"));
}
debug_struct.finish()
},
|f| f.write_str("PdfStream(...)"),
)
.fmt(f)
})
}
}
@ -1957,7 +2042,7 @@ pdf_parse! {
pdf_parse! {
#[pdf]
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct PdfObjectStreamDictionary {
#[pdf(name = Self::TYPE_NAME)]
pub ty: PdfObjectStreamType,