Merge PR #3637: Fix named properties in forms without resources

ROADMAP.md: progress log union, newest first
This commit is contained in:
aiongg
2026-10-22 01:45:57 +00:00
6 changed files with 340 additions and 9 deletions
+6
View File
File diff suppressed because one or more lines are too long
+4 -3
View File
@@ -66,7 +66,7 @@ pub(crate) struct FormXObject<'a> {
is_isolated: bool,
is_knockout: bool,
pub(crate) dict: Dict<'a>,
resources: Dict<'a>,
resources: Option<Dict<'a>>,
}
impl<'a> FormXObject<'a> {
@@ -75,7 +75,8 @@ impl<'a> FormXObject<'a> {
// PdfCraft patch: see `context::MAX_PAGE_CONTENT`.
let decoded = crate::context::decode_content(stream)?;
let resources = dict.get::<Dict<'_>>(RESOURCES).unwrap_or_default();
// PdfCraft patch: retain absence; present malformed dictionaries stay local-empty.
let resources = dict.contains_key(RESOURCES).then(|| dict.get::<Dict<'_>>(RESOURCES).unwrap_or_default());
let matrix = Affine::new(
dict.get::<[f64; 6]>(MATRIX)
@@ -191,7 +192,7 @@ pub(crate) fn draw_form_xobject<'a, 'b>(
interpret(
iter,
&Resources::from_parent(x_object.resources.clone(), resources.clone()),
&Resources::for_form(x_object.resources.clone(), resources.clone()),
context,
device,
);
+30 -5
View File
@@ -148,8 +148,8 @@ fn resolve_pages<'a>(
ctx.rotate = Some(rotate);
}
let resources = Resources::from_parent(
pages_dict.get::<Dict<'_>>(RESOURCES).unwrap_or_default(),
let resources = Resources::for_page(
pages_dict.contains_key(RESOURCES).then(|| pages_dict.get::<Dict<'_>>(RESOURCES).unwrap_or_default()),
resources.clone(),
);
@@ -431,8 +431,8 @@ impl<'a> Page<'a> {
.map_or(1.0, |u| u.min(MAX_USER_UNIT) as f32);
let ctx = resources.ctx.clone();
let resources = Resources::from_parent(
dict.get::<Dict<'_>>(RESOURCES).unwrap_or_default(),
let resources = Resources::for_page(
dict.contains_key(RESOURCES).then(|| dict.get::<Dict<'_>>(RESOURCES).unwrap_or_default()),
resources,
);
@@ -674,11 +674,13 @@ impl<'a> Page<'a> {
pub struct Resources<'a> {
parent: Option<Box<Self>>,
ctx: ReaderContext<'a>,
// PdfCraft patch: legacy forms without Resources use the page, not their caller.
page_properties: Dict<'a>,
/// The raw dictionary of external graphics states.
pub ext_g_states: Dict<'a>,
/// The raw dictionary of fonts.
pub fonts: Dict<'a>,
/// The raw dictionary of properties.
/// The effective property dictionary selected at page or form entry.
pub properties: Dict<'a>,
/// The raw dictionary of color spaces.
pub color_spaces: Dict<'a>,
@@ -698,6 +700,26 @@ impl<'a> Resources<'a> {
Self::new(resources, Some(parent), &ctx)
}
// PdfCraft patch: page-tree inheritance stops at any present Resources dictionary.
fn for_page(resources: Option<Dict<'a>>, parent: Self) -> Self {
let properties = resources.as_ref().map_or_else(|| parent.properties.clone(), |dict| dict.get::<Dict<'_>>(PROPERTIES).unwrap_or_default());
let mut result = Self::from_parent(resources.unwrap_or_default(), parent);
result.properties = properties.clone();
result.page_properties = properties;
result
}
/// Create form resources, using page Properties only when Resources is absent.
/// Other resource categories retain their existing parent-chain lookup.
// PdfCraft patch: ISO 32000-2, 7.8.3 and Table 93 require the page origin.
pub fn for_form(resources: Option<Dict<'a>>, parent: Self) -> Self {
let properties =
resources.as_ref().map_or_else(|| parent.page_properties.clone(), |dict| dict.get::<Dict<'_>>(PROPERTIES).unwrap_or_default());
let mut result = Self::from_parent(resources.unwrap_or_default(), parent);
result.properties = properties;
result
}
/// Create a new `Resources` object.
pub(crate) fn new(resources: Dict<'a>, parent: Option<Self>, ctx: &ReaderContext<'a>) -> Self {
let ext_g_states = resources.get::<Dict<'_>>(EXT_G_STATE).unwrap_or_default();
@@ -708,6 +730,8 @@ impl<'a> Resources<'a> {
let shadings = resources.get::<Dict<'_>>(SHADING).unwrap_or_default();
let properties = resources.get::<Dict<'_>>(PROPERTIES).unwrap_or_default();
// PdfCraft patch: generic child resources retain the page origin.
let page_properties = parent.as_ref().map_or_else(|| properties.clone(), |parent| parent.page_properties.clone());
let parent = parent.map(Box::new);
Self {
@@ -716,6 +740,7 @@ impl<'a> Resources<'a> {
fonts,
color_spaces,
properties,
page_properties,
x_objects,
patterns,
shadings,