Merge pull request #18825 from agrahn/rbgroups
implementing optional content radiobutton groups
This commit is contained in:
commit
424f81c4db
@ -486,17 +486,17 @@ class Catalog {
|
|||||||
return shadow(this, "optionalContentConfig", null);
|
return shadow(this, "optionalContentConfig", null);
|
||||||
}
|
}
|
||||||
const groups = [];
|
const groups = [];
|
||||||
const groupRefs = new RefSet();
|
const groupRefCache = new RefSetCache();
|
||||||
// Ensure all the optional content groups are valid.
|
// Ensure all the optional content groups are valid.
|
||||||
for (const groupRef of groupsData) {
|
for (const groupRef of groupsData) {
|
||||||
if (!(groupRef instanceof Ref) || groupRefs.has(groupRef)) {
|
if (!(groupRef instanceof Ref) || groupRefCache.has(groupRef)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
groupRefs.put(groupRef);
|
const group = this.#readOptionalContentGroup(groupRef);
|
||||||
|
groups.push(group);
|
||||||
groups.push(this.#readOptionalContentGroup(groupRef));
|
groupRefCache.put(groupRef, group);
|
||||||
}
|
}
|
||||||
config = this.#readOptionalContentConfig(defaultConfig, groupRefs);
|
config = this.#readOptionalContentConfig(defaultConfig, groupRefCache);
|
||||||
config.groups = groups;
|
config.groups = groups;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex instanceof MissingDataException) {
|
if (ex instanceof MissingDataException) {
|
||||||
@ -517,6 +517,7 @@ class Catalog {
|
|||||||
print: null,
|
print: null,
|
||||||
view: null,
|
view: null,
|
||||||
},
|
},
|
||||||
|
rbGroups: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const name = group.get("Name");
|
const name = group.get("Name");
|
||||||
@ -565,7 +566,7 @@ class Catalog {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
#readOptionalContentConfig(config, contentGroupRefs) {
|
#readOptionalContentConfig(config, groupRefCache) {
|
||||||
function parseOnOff(refs) {
|
function parseOnOff(refs) {
|
||||||
const onParsed = [];
|
const onParsed = [];
|
||||||
if (Array.isArray(refs)) {
|
if (Array.isArray(refs)) {
|
||||||
@ -573,7 +574,7 @@ class Catalog {
|
|||||||
if (!(value instanceof Ref)) {
|
if (!(value instanceof Ref)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (contentGroupRefs.has(value)) {
|
if (groupRefCache.has(value)) {
|
||||||
onParsed.push(value.toString());
|
onParsed.push(value.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -588,7 +589,7 @@ class Catalog {
|
|||||||
const order = [];
|
const order = [];
|
||||||
|
|
||||||
for (const value of refs) {
|
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.
|
parsedOrderRefs.put(value); // Handle "hidden" groups, see below.
|
||||||
|
|
||||||
order.push(value.toString());
|
order.push(value.toString());
|
||||||
@ -605,7 +606,7 @@ class Catalog {
|
|||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
const hiddenGroups = [];
|
const hiddenGroups = [];
|
||||||
for (const groupRef of contentGroupRefs) {
|
for (const [groupRef] of groupRefCache.items()) {
|
||||||
if (parsedOrderRefs.has(groupRef)) {
|
if (parsedOrderRefs.has(groupRef)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -638,10 +639,39 @@ class Catalog {
|
|||||||
return { name: stringToPDFString(nestedName), order: nestedOrder };
|
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,
|
const xref = this.xref,
|
||||||
parsedOrderRefs = new RefSet(),
|
parsedOrderRefs = new RefSet(),
|
||||||
MAX_NESTED_LEVELS = 10;
|
MAX_NESTED_LEVELS = 10;
|
||||||
|
|
||||||
|
parseRBGroups(config.get("RBGroups"));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name:
|
name:
|
||||||
typeof config.get("Name") === "string"
|
typeof config.get("Name") === "string"
|
||||||
|
|||||||
@ -33,13 +33,14 @@ class OptionalContentGroup {
|
|||||||
|
|
||||||
#visible = true;
|
#visible = true;
|
||||||
|
|
||||||
constructor(renderingIntent, { name, intent, usage }) {
|
constructor(renderingIntent, { name, intent, usage, rbGroups }) {
|
||||||
this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY);
|
this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY);
|
||||||
this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
|
this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
|
||||||
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.intent = intent;
|
this.intent = intent;
|
||||||
this.usage = usage;
|
this.usage = usage;
|
||||||
|
this.rbGroups = rbGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,12 +230,26 @@ class OptionalContentConfig {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisibility(id, visible = true) {
|
setVisibility(id, visible = true, preserveRB = true) {
|
||||||
const group = this.#groups.get(id);
|
const group = this.#groups.get(id);
|
||||||
if (!group) {
|
if (!group) {
|
||||||
warn(`Optional content group not found: ${id}`);
|
warn(`Optional content group not found: ${id}`);
|
||||||
return;
|
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);
|
group._setVisible(INTERNAL, !!visible, /* userSet = */ true);
|
||||||
|
|
||||||
this.#cachedGetHash = null;
|
this.#cachedGetHash = null;
|
||||||
@ -258,13 +273,13 @@ class OptionalContentConfig {
|
|||||||
}
|
}
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
case "ON":
|
case "ON":
|
||||||
group._setVisible(INTERNAL, true);
|
this.setVisibility(elem, true, preserveRB);
|
||||||
break;
|
break;
|
||||||
case "OFF":
|
case "OFF":
|
||||||
group._setVisible(INTERNAL, false);
|
this.setVisibility(elem, false, preserveRB);
|
||||||
break;
|
break;
|
||||||
case "Toggle":
|
case "Toggle":
|
||||||
group._setVisible(INTERNAL, !group.visible);
|
this.setVisibility(elem, !group.visible, preserveRB);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -589,6 +589,7 @@
|
|||||||
!issue15690.pdf
|
!issue15690.pdf
|
||||||
!bug1802888.pdf
|
!bug1802888.pdf
|
||||||
!issue15759.pdf
|
!issue15759.pdf
|
||||||
|
!issue18823.pdf
|
||||||
!issue15753.pdf
|
!issue15753.pdf
|
||||||
!issue15789.pdf
|
!issue15789.pdf
|
||||||
!fields_order.pdf
|
!fields_order.pdf
|
||||||
|
|||||||
BIN
test/pdfs/issue18823.pdf
Normal file
BIN
test/pdfs/issue18823.pdf
Normal file
Binary file not shown.
@ -6875,6 +6875,23 @@
|
|||||||
"7R": false
|
"7R": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "issue18823-default",
|
||||||
|
"file": "pdfs/issue18823.pdf",
|
||||||
|
"md5": "f5246c476516c96df106ced0c5839da3",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "issue18823-group-three",
|
||||||
|
"file": "pdfs/issue18823.pdf",
|
||||||
|
"md5": "f5246c476516c96df106ced0c5839da3",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"optionalContent": {
|
||||||
|
"15R": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "issue2829",
|
"id": "issue2829",
|
||||||
"file": "pdfs/issue2829.pdf",
|
"file": "pdfs/issue2829.pdf",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user