Merge pull request #14036 from calixteman/14021
Annotation - Some checkboxes have an empty N dictionary
This commit is contained in:
commit
3b1d547738
@ -1906,7 +1906,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getOperatorList(evaluator, task, renderForms, annotationStorage) {
|
async getOperatorList(evaluator, task, renderForms, annotationStorage) {
|
||||||
if (this.data.pushButton) {
|
if (this.data.pushButton) {
|
||||||
return super.getOperatorList(
|
return super.getOperatorList(
|
||||||
evaluator,
|
evaluator,
|
||||||
@ -1916,10 +1916,16 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let value = null;
|
||||||
if (annotationStorage) {
|
if (annotationStorage) {
|
||||||
const storageEntry = annotationStorage.get(this.data.id);
|
const storageEntry = annotationStorage.get(this.data.id);
|
||||||
const value = storageEntry && storageEntry.value;
|
value = storageEntry ? storageEntry.value : null;
|
||||||
if (value === undefined) {
|
}
|
||||||
|
|
||||||
|
if (value === null) {
|
||||||
|
// Nothing in the annotationStorage.
|
||||||
|
if (this.appearance) {
|
||||||
|
// But we've a default appearance so use it.
|
||||||
return super.getOperatorList(
|
return super.getOperatorList(
|
||||||
evaluator,
|
evaluator,
|
||||||
task,
|
task,
|
||||||
@ -1928,35 +1934,33 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let appearance;
|
// There is no default appearance so use the one derived
|
||||||
if (value) {
|
// from the field value.
|
||||||
appearance = this.checkedAppearance;
|
if (this.data.checkBox) {
|
||||||
|
value = this.data.fieldValue === this.data.exportValue;
|
||||||
} else {
|
} else {
|
||||||
appearance = this.uncheckedAppearance;
|
value = this.data.fieldValue === this.data.buttonValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appearance) {
|
|
||||||
const savedAppearance = this.appearance;
|
|
||||||
this.appearance = appearance;
|
|
||||||
const operatorList = super.getOperatorList(
|
|
||||||
evaluator,
|
|
||||||
task,
|
|
||||||
renderForms,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
this.appearance = savedAppearance;
|
|
||||||
return operatorList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No appearance
|
|
||||||
return Promise.resolve(new OperatorList());
|
|
||||||
}
|
}
|
||||||
return super.getOperatorList(
|
|
||||||
evaluator,
|
const appearance = value
|
||||||
task,
|
? this.checkedAppearance
|
||||||
renderForms,
|
: this.uncheckedAppearance;
|
||||||
annotationStorage
|
if (appearance) {
|
||||||
);
|
const savedAppearance = this.appearance;
|
||||||
|
this.appearance = appearance;
|
||||||
|
const operatorList = super.getOperatorList(
|
||||||
|
evaluator,
|
||||||
|
task,
|
||||||
|
renderForms,
|
||||||
|
annotationStorage
|
||||||
|
);
|
||||||
|
this.appearance = savedAppearance;
|
||||||
|
return operatorList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No appearance
|
||||||
|
return new OperatorList();
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(evaluator, task, annotationStorage) {
|
async save(evaluator, task, annotationStorage) {
|
||||||
@ -1982,7 +1986,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultValue = this.data.fieldValue && this.data.fieldValue !== "Off";
|
const defaultValue = this.data.fieldValue === this.data.exportValue;
|
||||||
if (defaultValue === value) {
|
if (defaultValue === value) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -2093,6 +2097,64 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
return newRefs;
|
return newRefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getDefaultCheckedAppearance(params, type) {
|
||||||
|
const width = this.data.rect[2] - this.data.rect[0];
|
||||||
|
const height = this.data.rect[3] - this.data.rect[1];
|
||||||
|
const bbox = [0, 0, width, height];
|
||||||
|
|
||||||
|
// Ratio used to have a mark slightly smaller than the bbox.
|
||||||
|
const FONT_RATIO = 0.8;
|
||||||
|
const fontSize = Math.min(width, height) * FONT_RATIO;
|
||||||
|
|
||||||
|
// Char Metrics
|
||||||
|
// Widths came from widths for ZapfDingbats.
|
||||||
|
// Heights are guessed with Fontforge and FoxitDingbats.pfb.
|
||||||
|
let metrics, char;
|
||||||
|
if (type === "check") {
|
||||||
|
// Char 33 (2713 in unicode)
|
||||||
|
metrics = {
|
||||||
|
width: 0.755 * fontSize,
|
||||||
|
height: 0.705 * fontSize,
|
||||||
|
};
|
||||||
|
char = "\x33";
|
||||||
|
} else if (type === "disc") {
|
||||||
|
// Char 6C (25CF in unicode)
|
||||||
|
metrics = {
|
||||||
|
width: 0.791 * fontSize,
|
||||||
|
height: 0.705 * fontSize,
|
||||||
|
};
|
||||||
|
char = "\x6C";
|
||||||
|
} else {
|
||||||
|
unreachable(`_getDefaultCheckedAppearance - unsupported type: ${type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values to center the glyph in the bbox.
|
||||||
|
const xShift = (width - metrics.width) / 2;
|
||||||
|
const yShift = (height - metrics.height) / 2;
|
||||||
|
|
||||||
|
const appearance = `q BT /PdfJsZaDb ${fontSize} Tf 0 g ${xShift} ${yShift} Td (${char}) Tj ET Q`;
|
||||||
|
|
||||||
|
const appearanceStreamDict = new Dict(params.xref);
|
||||||
|
appearanceStreamDict.set("FormType", 1);
|
||||||
|
appearanceStreamDict.set("Subtype", Name.get("Form"));
|
||||||
|
appearanceStreamDict.set("Type", Name.get("XObject"));
|
||||||
|
appearanceStreamDict.set("BBox", bbox);
|
||||||
|
appearanceStreamDict.set("Matrix", [1, 0, 0, 1, 0, 0]);
|
||||||
|
appearanceStreamDict.set("Length", appearance.length);
|
||||||
|
|
||||||
|
const resources = new Dict(params.xref);
|
||||||
|
const font = new Dict(params.xref);
|
||||||
|
font.set("PdfJsZaDb", this.fallbackFontDict);
|
||||||
|
resources.set("Font", font);
|
||||||
|
|
||||||
|
appearanceStreamDict.set("Resources", resources);
|
||||||
|
|
||||||
|
this.checkedAppearance = new StringStream(appearance);
|
||||||
|
this.checkedAppearance.dict = appearanceStreamDict;
|
||||||
|
|
||||||
|
this._streams.push(this.checkedAppearance);
|
||||||
|
}
|
||||||
|
|
||||||
_processCheckBox(params) {
|
_processCheckBox(params) {
|
||||||
const customAppearance = params.dict.get("AP");
|
const customAppearance = params.dict.get("AP");
|
||||||
if (!isDict(customAppearance)) {
|
if (!isDict(customAppearance)) {
|
||||||
@ -2111,27 +2173,46 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
this.data.fieldValue = asValue;
|
this.data.fieldValue = asValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const yes =
|
||||||
|
this.data.fieldValue !== null && this.data.fieldValue !== "Off"
|
||||||
|
? this.data.fieldValue
|
||||||
|
: "Yes";
|
||||||
|
|
||||||
const exportValues = normalAppearance.getKeys();
|
const exportValues = normalAppearance.getKeys();
|
||||||
if (!exportValues.includes("Off")) {
|
if (exportValues.length === 0) {
|
||||||
// The /Off appearance is optional.
|
exportValues.push("Off", yes);
|
||||||
exportValues.push("Off");
|
} else if (exportValues.length === 1) {
|
||||||
|
if (exportValues[0] === "Off") {
|
||||||
|
exportValues.push(yes);
|
||||||
|
} else {
|
||||||
|
exportValues.unshift("Off");
|
||||||
|
}
|
||||||
|
} else if (exportValues.includes(yes)) {
|
||||||
|
exportValues.length = 0;
|
||||||
|
exportValues.push("Off", yes);
|
||||||
|
} else {
|
||||||
|
const otherYes = exportValues.find(v => v !== "Off");
|
||||||
|
exportValues.length = 0;
|
||||||
|
exportValues.push("Off", otherYes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use a "V" entry pointing to a non-existent appearance state,
|
// Don't use a "V" entry pointing to a non-existent appearance state,
|
||||||
// see e.g. bug1720411.pdf where it's an *empty* Name-instance.
|
// see e.g. bug1720411.pdf where it's an *empty* Name-instance.
|
||||||
if (!exportValues.includes(this.data.fieldValue)) {
|
if (!exportValues.includes(this.data.fieldValue)) {
|
||||||
this.data.fieldValue = null;
|
this.data.fieldValue = "Off";
|
||||||
}
|
|
||||||
if (exportValues.length !== 2) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data.exportValue =
|
this.data.exportValue = exportValues[1];
|
||||||
exportValues[0] === "Off" ? exportValues[1] : exportValues[0];
|
|
||||||
|
|
||||||
this.checkedAppearance = normalAppearance.get(this.data.exportValue);
|
this.checkedAppearance =
|
||||||
|
normalAppearance.get(this.data.exportValue) || null;
|
||||||
this.uncheckedAppearance = normalAppearance.get("Off") || null;
|
this.uncheckedAppearance = normalAppearance.get("Off") || null;
|
||||||
|
|
||||||
this._streams.push(this.checkedAppearance);
|
if (this.checkedAppearance) {
|
||||||
|
this._streams.push(this.checkedAppearance);
|
||||||
|
} else {
|
||||||
|
this._getDefaultCheckedAppearance(params, "check");
|
||||||
|
}
|
||||||
if (this.uncheckedAppearance) {
|
if (this.uncheckedAppearance) {
|
||||||
this._streams.push(this.uncheckedAppearance);
|
this._streams.push(this.uncheckedAppearance);
|
||||||
}
|
}
|
||||||
@ -2168,10 +2249,15 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkedAppearance = normalAppearance.get(this.data.buttonValue);
|
this.checkedAppearance =
|
||||||
|
normalAppearance.get(this.data.buttonValue) || null;
|
||||||
this.uncheckedAppearance = normalAppearance.get("Off") || null;
|
this.uncheckedAppearance = normalAppearance.get("Off") || null;
|
||||||
|
|
||||||
this._streams.push(this.checkedAppearance);
|
if (this.checkedAppearance) {
|
||||||
|
this._streams.push(this.checkedAppearance);
|
||||||
|
} else {
|
||||||
|
this._getDefaultCheckedAppearance(params, "disc");
|
||||||
|
}
|
||||||
if (this.uncheckedAppearance) {
|
if (this.uncheckedAppearance) {
|
||||||
this._streams.push(this.uncheckedAppearance);
|
this._streams.push(this.uncheckedAppearance);
|
||||||
}
|
}
|
||||||
|
@ -1013,10 +1013,7 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
const data = this.data;
|
const data = this.data;
|
||||||
const id = data.id;
|
const id = data.id;
|
||||||
let value = storage.getValue(id, {
|
let value = storage.getValue(id, {
|
||||||
value:
|
value: data.exportValue === data.fieldValue,
|
||||||
data.fieldValue &&
|
|
||||||
((data.exportValue && data.exportValue === data.fieldValue) ||
|
|
||||||
(!data.exportValue && data.fieldValue !== "Off")),
|
|
||||||
}).value;
|
}).value;
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
// The value has been changed through js and set in annotationStorage.
|
// The value has been changed through js and set in annotationStorage.
|
||||||
|
1
test/pdfs/issue14021.pdf.link
Normal file
1
test/pdfs/issue14021.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://github.com/mozilla/pdf.js/files/7159923/docOficialPDF.php.pdf
|
@ -5944,5 +5944,36 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq",
|
"type": "eq",
|
||||||
"annotations": true
|
"annotations": true
|
||||||
|
},
|
||||||
|
{ "id": "issue14021",
|
||||||
|
"file": "pdfs/issue14021.pdf",
|
||||||
|
"md5": "d18aa84135ce985c70a8f56306ecb95f",
|
||||||
|
"link": true,
|
||||||
|
"rounds": 1,
|
||||||
|
"firstPage": 2,
|
||||||
|
"lastPage": 2,
|
||||||
|
"type": "eq",
|
||||||
|
"forms": true
|
||||||
|
},
|
||||||
|
{ "id": "issue14021-storage",
|
||||||
|
"file": "pdfs/issue14021.pdf",
|
||||||
|
"md5": "d18aa84135ce985c70a8f56306ecb95f",
|
||||||
|
"link": true,
|
||||||
|
"rounds": 1,
|
||||||
|
"firstPage": 2,
|
||||||
|
"lastPage": 3,
|
||||||
|
"type": "eq",
|
||||||
|
"print": true,
|
||||||
|
"annotationStorage": {
|
||||||
|
"148R": {
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
"138R": {
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
"139R": {
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user