From 441efe456ebf141fe32dfe2b06ba3140d22b6006 Mon Sep 17 00:00:00 2001 From: Alexander Grahn Date: Mon, 14 Oct 2024 14:18:29 +0200 Subject: [PATCH] Optional Content (OC) radiobutton (RB) groups implemented. Resolves #18823. The code parses the /RBGroups entry in the OC configuration dict and adds the property `rbGroups' to instances of the OptionalContentGroup class. rbGroups takes an array of Sets, where each Set instance represents an RB group the OptionalContentGroup instance is a member of. Such a Set instance contains all OCG ids within the corresponding RB group. RB groups an OCG is associated with are processed when its visibility is set to true, as required by the PDF spec. --- src/core/catalog.js | 50 ++++++++++++++++++++----- src/display/optional_content_config.js | 25 ++++++++++--- test/pdfs/.gitignore | 1 + test/pdfs/issue18823.pdf | Bin 0 -> 26596 bytes test/test_manifest.json | 17 +++++++++ 5 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 test/pdfs/issue18823.pdf diff --git a/src/core/catalog.js b/src/core/catalog.js index 9dd20e0e7..d566c2d04 100644 --- a/src/core/catalog.js +++ b/src/core/catalog.js @@ -486,17 +486,17 @@ class Catalog { return shadow(this, "optionalContentConfig", null); } const groups = []; - const groupRefs = new RefSet(); + const groupRefCache = new RefSetCache(); // Ensure all the optional content groups are valid. for (const groupRef of groupsData) { - if (!(groupRef instanceof Ref) || groupRefs.has(groupRef)) { + if (!(groupRef instanceof Ref) || groupRefCache.has(groupRef)) { continue; } - groupRefs.put(groupRef); - - groups.push(this.#readOptionalContentGroup(groupRef)); + const group = this.#readOptionalContentGroup(groupRef); + groups.push(group); + groupRefCache.put(groupRef, group); } - config = this.#readOptionalContentConfig(defaultConfig, groupRefs); + config = this.#readOptionalContentConfig(defaultConfig, groupRefCache); config.groups = groups; } catch (ex) { if (ex instanceof MissingDataException) { @@ -517,6 +517,7 @@ class Catalog { print: null, view: null, }, + rbGroups: [], }; const name = group.get("Name"); @@ -565,7 +566,7 @@ class Catalog { return obj; } - #readOptionalContentConfig(config, contentGroupRefs) { + #readOptionalContentConfig(config, groupRefCache) { function parseOnOff(refs) { const onParsed = []; if (Array.isArray(refs)) { @@ -573,7 +574,7 @@ class Catalog { if (!(value instanceof Ref)) { continue; } - if (contentGroupRefs.has(value)) { + if (groupRefCache.has(value)) { onParsed.push(value.toString()); } } @@ -588,7 +589,7 @@ class Catalog { const order = []; for (const value of refs) { - if (value instanceof Ref && contentGroupRefs.has(value)) { + if (value instanceof Ref && groupRefCache.has(value)) { parsedOrderRefs.put(value); // Handle "hidden" groups, see below. order.push(value.toString()); @@ -605,7 +606,7 @@ class Catalog { return order; } const hiddenGroups = []; - for (const groupRef of contentGroupRefs) { + for (const [groupRef] of groupRefCache.items()) { if (parsedOrderRefs.has(groupRef)) { continue; } @@ -638,10 +639,39 @@ class Catalog { return { name: stringToPDFString(nestedName), order: nestedOrder }; } + function parseRBGroups(rbGroups) { + if (!Array.isArray(rbGroups)) { + return; + } + + for (const value of rbGroups) { + const rbGroup = xref.fetchIfRef(value); + if (!Array.isArray(rbGroup) || !rbGroup.length) { + continue; + } + + const parsedRbGroup = new Set(); + + for (const ref of rbGroup) { + if ( + ref instanceof Ref && + groupRefCache.has(ref) && + !parsedRbGroup.has(ref.toString()) + ) { + parsedRbGroup.add(ref.toString()); + // Keep a record of which RB groups the current OCG belongs to. + groupRefCache.get(ref).rbGroups.push(parsedRbGroup); + } + } + } + } + const xref = this.xref, parsedOrderRefs = new RefSet(), MAX_NESTED_LEVELS = 10; + parseRBGroups(config.get("RBGroups")); + return { name: typeof config.get("Name") === "string" diff --git a/src/display/optional_content_config.js b/src/display/optional_content_config.js index 366da2212..b31bd526e 100644 --- a/src/display/optional_content_config.js +++ b/src/display/optional_content_config.js @@ -33,13 +33,14 @@ class OptionalContentGroup { #visible = true; - constructor(renderingIntent, { name, intent, usage }) { + constructor(renderingIntent, { name, intent, usage, rbGroups }) { this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY); this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); this.name = name; this.intent = intent; this.usage = usage; + this.rbGroups = rbGroups; } /** @@ -229,12 +230,26 @@ class OptionalContentConfig { return true; } - setVisibility(id, visible = true) { + setVisibility(id, visible = true, preserveRB = true) { const group = this.#groups.get(id); if (!group) { warn(`Optional content group not found: ${id}`); return; } + + // If the visibility is about to be set to `true` and the group belongs to + // any radiobutton groups, hide all other OCGs in these radiobutton groups, + // provided that radiobutton state relationships are to be preserved. + if (preserveRB && visible && group.rbGroups.length) { + for (const rbGroup of group.rbGroups) { + for (const otherId of rbGroup) { + if (otherId !== id) { + this.#groups.get(otherId)?._setVisible(INTERNAL, false, true); + } + } + } + } + group._setVisible(INTERNAL, !!visible, /* userSet = */ true); this.#cachedGetHash = null; @@ -258,13 +273,13 @@ class OptionalContentConfig { } switch (operator) { case "ON": - group._setVisible(INTERNAL, true); + this.setVisibility(elem, true, preserveRB); break; case "OFF": - group._setVisible(INTERNAL, false); + this.setVisibility(elem, false, preserveRB); break; case "Toggle": - group._setVisible(INTERNAL, !group.visible); + this.setVisibility(elem, !group.visible, preserveRB); break; } } diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index fa61a789c..3126d12cf 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -589,6 +589,7 @@ !issue15690.pdf !bug1802888.pdf !issue15759.pdf +!issue18823.pdf !issue15753.pdf !issue15789.pdf !fields_order.pdf diff --git a/test/pdfs/issue18823.pdf b/test/pdfs/issue18823.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fcf623bf64930c9bde498d21cbaece4a6fa67a99 GIT binary patch literal 26596 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