Add (basic) support for Optional Content in Annotations

Given that Annotations can also have an `OC`-entry, we need to take that into account when generating their operatorLists.

Note that in order to simplify the patch the `getOperatorList`-methods, for the Annotation-classes, were converted to be `async`.
This commit is contained in:
Jonas Jenwald 2022-06-24 14:39:08 +02:00
parent 3fab4af949
commit c48dc251e0
2 changed files with 134 additions and 76 deletions

View File

@ -447,6 +447,7 @@ class Annotation {
this.setColor(dict.getArray("C")); this.setColor(dict.getArray("C"));
this.setBorderStyle(dict); this.setBorderStyle(dict);
this.setAppearance(dict); this.setAppearance(dict);
this.setOptionalContent(dict);
const MK = dict.get("MK"); const MK = dict.get("MK");
this.setBorderAndBackgroundColors(MK); this.setBorderAndBackgroundColors(MK);
@ -842,6 +843,17 @@ class Annotation {
this.appearance = normalAppearanceState.get(as.name); this.appearance = normalAppearanceState.get(as.name);
} }
setOptionalContent(dict) {
this.oc = null;
const oc = dict.get("OC");
if (oc instanceof Name) {
warn("setOptionalContent: Support for /Name-entry is not implemented.");
} else if (oc instanceof Dict) {
this.oc = oc;
}
}
loadResources(keys, appearance) { loadResources(keys, appearance) {
return appearance.dict.getAsync("Resources").then(resources => { return appearance.dict.getAsync("Resources").then(resources => {
if (!resources) { if (!resources) {
@ -855,21 +867,27 @@ class Annotation {
}); });
} }
getOperatorList(evaluator, task, intent, renderForms, annotationStorage) { async getOperatorList(
evaluator,
task,
intent,
renderForms,
annotationStorage
) {
const data = this.data; const data = this.data;
let appearance = this.appearance; let appearance = this.appearance;
const isUsingOwnCanvas = const isUsingOwnCanvas =
this.data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY; this.data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY;
if (!appearance) { if (!appearance) {
if (!isUsingOwnCanvas) { if (!isUsingOwnCanvas) {
return Promise.resolve(new OperatorList()); return new OperatorList();
} }
appearance = new StringStream(""); appearance = new StringStream("");
appearance.dict = new Dict(); appearance.dict = new Dict();
} }
const appearanceDict = appearance.dict; const appearanceDict = appearance.dict;
const resourcesPromise = this.loadResources( const resources = await this.loadResources(
["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"], ["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"],
appearance appearance
); );
@ -877,8 +895,19 @@ class Annotation {
const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0]; const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0];
const transform = getTransformMatrix(data.rect, bbox, matrix); const transform = getTransformMatrix(data.rect, bbox, matrix);
return resourcesPromise.then(resources => {
const opList = new OperatorList(); const opList = new OperatorList();
let optionalContent;
if (this.oc) {
optionalContent = await evaluator.parseMarkedContentProps(
this.oc,
/* resources = */ null
);
}
if (optionalContent !== undefined) {
opList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
}
opList.addOp(OPS.beginAnnotation, [ opList.addOp(OPS.beginAnnotation, [
data.id, data.id,
data.rect, data.rect,
@ -887,20 +916,20 @@ class Annotation {
isUsingOwnCanvas, isUsingOwnCanvas,
]); ]);
return evaluator await evaluator.getOperatorList({
.getOperatorList({
stream: appearance, stream: appearance,
task, task,
resources, resources,
operatorList: opList, operatorList: opList,
fallbackFontDict: this._fallbackFontDict, fallbackFontDict: this._fallbackFontDict,
}) });
.then(() => {
opList.addOp(OPS.endAnnotation, []); opList.addOp(OPS.endAnnotation, []);
if (optionalContent !== undefined) {
opList.addOp(OPS.endMarkedContent, []);
}
this.reset(); this.reset();
return opList; return opList;
});
});
} }
async save(evaluator, task, annotationStorage) { async save(evaluator, task, annotationStorage) {
@ -1575,11 +1604,17 @@ class WidgetAnnotation extends Annotation {
return str; return str;
} }
getOperatorList(evaluator, task, intent, renderForms, annotationStorage) { async getOperatorList(
evaluator,
task,
intent,
renderForms,
annotationStorage
) {
// Do not render form elements on the canvas when interactive forms are // Do not render form elements on the canvas when interactive forms are
// enabled. The display layer is responsible for rendering them instead. // enabled. The display layer is responsible for rendering them instead.
if (renderForms && !(this instanceof SignatureWidgetAnnotation)) { if (renderForms && !(this instanceof SignatureWidgetAnnotation)) {
return Promise.resolve(new OperatorList()); return new OperatorList();
} }
if (!this._hasText) { if (!this._hasText) {
@ -1592,8 +1627,11 @@ class WidgetAnnotation extends Annotation {
); );
} }
return this._getAppearance(evaluator, task, annotationStorage).then( const content = await this._getAppearance(
content => { evaluator,
task,
annotationStorage
);
if (this.appearance && content === null) { if (this.appearance && content === null) {
return super.getOperatorList( return super.getOperatorList(
evaluator, evaluator,
@ -1619,8 +1657,19 @@ class WidgetAnnotation extends Annotation {
this.data.rect[2] - this.data.rect[0], this.data.rect[2] - this.data.rect[0],
this.data.rect[3] - this.data.rect[1], this.data.rect[3] - this.data.rect[1],
]; ];
const transform = getTransformMatrix(this.data.rect, bbox, matrix); const transform = getTransformMatrix(this.data.rect, bbox, matrix);
let optionalContent;
if (this.oc) {
optionalContent = await evaluator.parseMarkedContentProps(
this.oc,
/* resources = */ null
);
}
if (optionalContent !== undefined) {
operatorList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
}
operatorList.addOp(OPS.beginAnnotation, [ operatorList.addOp(OPS.beginAnnotation, [
this.data.id, this.data.id,
this.data.rect, this.data.rect,
@ -1629,19 +1678,18 @@ class WidgetAnnotation extends Annotation {
]); ]);
const stream = new StringStream(content); const stream = new StringStream(content);
return evaluator await evaluator.getOperatorList({
.getOperatorList({
stream, stream,
task, task,
resources: this._fieldResources.mergedResources, resources: this._fieldResources.mergedResources,
operatorList, operatorList,
})
.then(function () {
operatorList.addOp(OPS.endAnnotation, []);
return operatorList;
}); });
operatorList.addOp(OPS.endAnnotation, []);
if (optionalContent !== undefined) {
operatorList.addOp(OPS.endMarkedContent, []);
} }
); return operatorList;
} }
_getMKDict(rotation) { _getMKDict(rotation) {

View File

@ -6561,6 +6561,16 @@
} }
} }
}, },
{ "id": "bug1737260-oc",
"file": "pdfs/bug1737260.pdf",
"md5": "8bd4f810d30972764b07ae141a4afbc4",
"rounds": 1,
"link": true,
"type": "eq",
"optionalContent": {
"191R": false
}
},
{ "id": "bug1737260", { "id": "bug1737260",
"file": "pdfs/bug1737260.pdf", "file": "pdfs/bug1737260.pdf",
"md5": "8bd4f810d30972764b07ae141a4afbc4", "md5": "8bd4f810d30972764b07ae141a4afbc4",