Merge pull request #13243 from janpe2/ocg-ve
Implement visibility expressions for optional content
This commit is contained in:
commit
ae48d07582
@ -1306,6 +1306,43 @@ class PartialEvaluator {
|
|||||||
throw new FormatError(`Unknown PatternName: ${patternName}`);
|
throw new FormatError(`Unknown PatternName: ${patternName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_parseVisibilityExpression(array, nestingCounter, currentResult) {
|
||||||
|
const MAX_NESTING = 10;
|
||||||
|
if (++nestingCounter > MAX_NESTING) {
|
||||||
|
warn("Visibility expression is too deeply nested");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const length = array.length;
|
||||||
|
const operator = this.xref.fetchIfRef(array[0]);
|
||||||
|
if (length < 2 || !isName(operator)) {
|
||||||
|
warn("Invalid visibility expression");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (operator.name) {
|
||||||
|
case "And":
|
||||||
|
case "Or":
|
||||||
|
case "Not":
|
||||||
|
currentResult.push(operator.name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warn(`Invalid operator ${operator.name} in visibility expression`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 1; i < length; i++) {
|
||||||
|
const raw = array[i];
|
||||||
|
const object = this.xref.fetchIfRef(raw);
|
||||||
|
if (Array.isArray(object)) {
|
||||||
|
const nestedResult = [];
|
||||||
|
currentResult.push(nestedResult);
|
||||||
|
// Recursively parse a subarray.
|
||||||
|
this._parseVisibilityExpression(object, nestingCounter, nestedResult);
|
||||||
|
} else if (isRef(raw)) {
|
||||||
|
// Reference to an OCG dictionary.
|
||||||
|
currentResult.push(raw.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async parseMarkedContentProps(contentProperties, resources) {
|
async parseMarkedContentProps(contentProperties, resources) {
|
||||||
let optionalContent;
|
let optionalContent;
|
||||||
if (isName(contentProperties)) {
|
if (isName(contentProperties)) {
|
||||||
@ -1324,6 +1361,18 @@ class PartialEvaluator {
|
|||||||
id: optionalContent.objId,
|
id: optionalContent.objId,
|
||||||
};
|
};
|
||||||
} else if (optionalContentType === "OCMD") {
|
} else if (optionalContentType === "OCMD") {
|
||||||
|
const expression = optionalContent.get("VE");
|
||||||
|
if (Array.isArray(expression)) {
|
||||||
|
const result = [];
|
||||||
|
this._parseVisibilityExpression(expression, 0, result);
|
||||||
|
if (result.length > 0) {
|
||||||
|
return {
|
||||||
|
type: "OCMD",
|
||||||
|
expression: result,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const optionalContentGroups = optionalContent.get("OCGs");
|
const optionalContentGroups = optionalContent.get("OCGs");
|
||||||
if (
|
if (
|
||||||
Array.isArray(optionalContentGroups) ||
|
Array.isArray(optionalContentGroups) ||
|
||||||
@ -1339,19 +1388,13 @@ class PartialEvaluator {
|
|||||||
groupIds.push(optionalContentGroups.objId);
|
groupIds.push(optionalContentGroups.objId);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expression = null;
|
|
||||||
if (optionalContent.get("VE")) {
|
|
||||||
// TODO support visibility expression.
|
|
||||||
expression = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: optionalContentType,
|
type: optionalContentType,
|
||||||
ids: groupIds,
|
ids: groupIds,
|
||||||
policy: isName(optionalContent.get("P"))
|
policy: isName(optionalContent.get("P"))
|
||||||
? optionalContent.get("P").name
|
? optionalContent.get("P").name
|
||||||
: null,
|
: null,
|
||||||
expression,
|
expression: null,
|
||||||
};
|
};
|
||||||
} else if (isRef(optionalContentGroups)) {
|
} else if (isRef(optionalContentGroups)) {
|
||||||
return {
|
return {
|
||||||
|
@ -57,6 +57,43 @@ class OptionalContentConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_evaluateVisibilityExpression(array) {
|
||||||
|
const length = array.length;
|
||||||
|
if (length < 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const operator = array[0];
|
||||||
|
for (let i = 1; i < length; i++) {
|
||||||
|
const element = array[i];
|
||||||
|
let state;
|
||||||
|
if (Array.isArray(element)) {
|
||||||
|
state = this._evaluateVisibilityExpression(element);
|
||||||
|
} else if (this._groups.has(element)) {
|
||||||
|
state = this._groups.get(element).visible;
|
||||||
|
} else {
|
||||||
|
warn(`Optional content group not found: ${element}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (operator) {
|
||||||
|
case "And":
|
||||||
|
if (!state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Or":
|
||||||
|
if (state) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Not":
|
||||||
|
return !state;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return operator === "And";
|
||||||
|
}
|
||||||
|
|
||||||
isVisible(group) {
|
isVisible(group) {
|
||||||
if (group.type === "OCG") {
|
if (group.type === "OCG") {
|
||||||
if (!this._groups.has(group.id)) {
|
if (!this._groups.has(group.id)) {
|
||||||
@ -65,10 +102,9 @@ class OptionalContentConfig {
|
|||||||
}
|
}
|
||||||
return this._groups.get(group.id).visible;
|
return this._groups.get(group.id).visible;
|
||||||
} else if (group.type === "OCMD") {
|
} else if (group.type === "OCMD") {
|
||||||
// Per the spec, the expression should be preferred if available. Until
|
// Per the spec, the expression should be preferred if available.
|
||||||
// we implement this, just fallback to using the group policy for now.
|
|
||||||
if (group.expression) {
|
if (group.expression) {
|
||||||
warn("Visibility expression not supported yet.");
|
return this._evaluateVisibilityExpression(group.expression);
|
||||||
}
|
}
|
||||||
if (!group.policy || group.policy === "AnyOn") {
|
if (!group.policy || group.policy === "AnyOn") {
|
||||||
// Default
|
// Default
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -354,6 +354,7 @@
|
|||||||
!issue2128r.pdf
|
!issue2128r.pdf
|
||||||
!issue5540.pdf
|
!issue5540.pdf
|
||||||
!issue5549.pdf
|
!issue5549.pdf
|
||||||
|
!visibility_expressions.pdf
|
||||||
!issue5475.pdf
|
!issue5475.pdf
|
||||||
!issue10519_reduced.pdf
|
!issue10519_reduced.pdf
|
||||||
!annotation-border-styles.pdf
|
!annotation-border-styles.pdf
|
||||||
|
BIN
test/pdfs/visibility_expressions.pdf
Normal file
BIN
test/pdfs/visibility_expressions.pdf
Normal file
Binary file not shown.
@ -2635,6 +2635,12 @@
|
|||||||
"link": false,
|
"link": false,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "visibility_expressions",
|
||||||
|
"file": "pdfs/visibility_expressions.pdf",
|
||||||
|
"md5": "bc530d90984ddaa2cc7e0cd53fc2cf34",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue7580-text",
|
{ "id": "issue7580-text",
|
||||||
"file": "pdfs/issue7580.pdf",
|
"file": "pdfs/issue7580.pdf",
|
||||||
"md5": "44dd5a9b4373fcab9890cf567722a766",
|
"md5": "44dd5a9b4373fcab9890cf567722a766",
|
||||||
|
Loading…
Reference in New Issue
Block a user