Merge pull request #19054 from calixteman/issue18630
When saving some annotations with the same name, set the value in the parent
This commit is contained in:
commit
8a8b69f456
@ -42,6 +42,7 @@ import {
|
|||||||
collectActions,
|
collectActions,
|
||||||
escapeString,
|
escapeString,
|
||||||
getInheritableProperty,
|
getInheritableProperty,
|
||||||
|
getParentToUpdate,
|
||||||
getRotationMatrix,
|
getRotationMatrix,
|
||||||
isNumberArray,
|
isNumberArray,
|
||||||
lookupMatrix,
|
lookupMatrix,
|
||||||
@ -2108,6 +2109,24 @@ class WidgetAnnotation extends Annotation {
|
|||||||
|
|
||||||
amendSavedDict(annotationStorage, dict) {}
|
amendSavedDict(annotationStorage, dict) {}
|
||||||
|
|
||||||
|
setValue(dict, value, xref, changes) {
|
||||||
|
const { dict: parentDict, ref: parentRef } = getParentToUpdate(
|
||||||
|
dict,
|
||||||
|
this.ref,
|
||||||
|
xref
|
||||||
|
);
|
||||||
|
if (!parentDict) {
|
||||||
|
dict.set("V", value);
|
||||||
|
} else if (!changes.has(parentRef)) {
|
||||||
|
const newParentDict = parentDict.clone();
|
||||||
|
newParentDict.set("V", value);
|
||||||
|
changes.put(parentRef, { data: newParentDict });
|
||||||
|
return newParentDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async save(evaluator, task, annotationStorage, changes) {
|
async save(evaluator, task, annotationStorage, changes) {
|
||||||
const storageEntry = annotationStorage?.get(this.data.id);
|
const storageEntry = annotationStorage?.get(this.data.id);
|
||||||
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
|
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
|
||||||
@ -2191,13 +2210,15 @@ class WidgetAnnotation extends Annotation {
|
|||||||
value,
|
value,
|
||||||
};
|
};
|
||||||
|
|
||||||
dict.set(
|
const newParentDict = this.setValue(
|
||||||
"V",
|
dict,
|
||||||
Array.isArray(value)
|
Array.isArray(value)
|
||||||
? value.map(stringToAsciiOrUTF16BE)
|
? value.map(stringToAsciiOrUTF16BE)
|
||||||
: stringToAsciiOrUTF16BE(value)
|
: stringToAsciiOrUTF16BE(value),
|
||||||
|
xref,
|
||||||
|
changes
|
||||||
);
|
);
|
||||||
this.amendSavedDict(annotationStorage, dict);
|
this.amendSavedDict(annotationStorage, newParentDict || dict);
|
||||||
|
|
||||||
const maybeMK = this._getMKDict(rotation);
|
const maybeMK = this._getMKDict(rotation);
|
||||||
if (maybeMK) {
|
if (maybeMK) {
|
||||||
@ -3111,7 +3132,8 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const name = Name.get(value ? this.data.exportValue : "Off");
|
const name = Name.get(value ? this.data.exportValue : "Off");
|
||||||
dict.set("V", name);
|
this.setValue(dict, name, evaluator.xref, changes);
|
||||||
|
|
||||||
dict.set("AS", name);
|
dict.set("AS", name);
|
||||||
dict.set("M", `D:${getModificationDate()}`);
|
dict.set("M", `D:${getModificationDate()}`);
|
||||||
if (flags !== undefined) {
|
if (flags !== undefined) {
|
||||||
@ -3170,24 +3192,8 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const name = Name.get(value ? this.data.buttonValue : "Off");
|
const name = Name.get(value ? this.data.buttonValue : "Off");
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
if (this.parent instanceof Ref) {
|
this.setValue(dict, name, evaluator.xref, changes);
|
||||||
const parent = evaluator.xref.fetch(this.parent).clone();
|
|
||||||
parent.set("V", name);
|
|
||||||
changes.put(this.parent, {
|
|
||||||
data: parent,
|
|
||||||
xfa: null,
|
|
||||||
needAppearances: false,
|
|
||||||
});
|
|
||||||
} else if (this.parent instanceof Dict) {
|
|
||||||
this.parent.set("V", name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.parent) {
|
|
||||||
// If there is no parent then we must set the value in the field.
|
|
||||||
dict.set("V", name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dict.set("AS", name);
|
dict.set("AS", name);
|
||||||
|
|||||||
@ -146,6 +146,36 @@ function getInheritableProperty({
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the parent dictionary to update when a property is set.
|
||||||
|
*
|
||||||
|
* @param {Dict} dict - Dictionary from where to start the traversal.
|
||||||
|
* @param {Ref} ref - The reference to the dictionary.
|
||||||
|
* @param {XRef} xref - The `XRef` instance.
|
||||||
|
*/
|
||||||
|
function getParentToUpdate(dict, ref, xref) {
|
||||||
|
const visited = new RefSet();
|
||||||
|
const firstDict = dict;
|
||||||
|
const result = { dict: null, ref: null };
|
||||||
|
|
||||||
|
while (dict instanceof Dict && !visited.has(ref)) {
|
||||||
|
visited.put(ref);
|
||||||
|
if (dict.has("T")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ref = dict.getRaw("Parent");
|
||||||
|
if (!(ref instanceof Ref)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
dict = xref.fetch(ref);
|
||||||
|
}
|
||||||
|
if (dict instanceof Dict && dict !== firstDict) {
|
||||||
|
result.dict = dict;
|
||||||
|
result.ref = ref;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const ROMAN_NUMBER_MAP = [
|
const ROMAN_NUMBER_MAP = [
|
||||||
"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM",
|
"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM",
|
||||||
@ -672,6 +702,7 @@ export {
|
|||||||
getInheritableProperty,
|
getInheritableProperty,
|
||||||
getLookupTableFactory,
|
getLookupTableFactory,
|
||||||
getNewAnnotationsMap,
|
getNewAnnotationsMap,
|
||||||
|
getParentToUpdate,
|
||||||
getRotationMatrix,
|
getRotationMatrix,
|
||||||
getSizeInBytes,
|
getSizeInBytes,
|
||||||
isAscii,
|
isAscii,
|
||||||
|
|||||||
@ -2145,6 +2145,81 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should save the text in two fields with the same name", async function () {
|
||||||
|
const textWidget1Ref = Ref.get(123, 0);
|
||||||
|
const textWidget2Ref = Ref.get(124, 0);
|
||||||
|
|
||||||
|
const parentRef = Ref.get(125, 0);
|
||||||
|
textWidgetDict.set("Parent", parentRef);
|
||||||
|
const parentDict = new Dict();
|
||||||
|
parentDict.set("Kids", [textWidget1Ref, textWidget2Ref]);
|
||||||
|
parentDict.set("T", "foo");
|
||||||
|
const textWidget2Dict = textWidgetDict.clone();
|
||||||
|
|
||||||
|
const xref = new XRefMock([
|
||||||
|
{ ref: textWidget1Ref, data: textWidgetDict },
|
||||||
|
{ ref: textWidget2Ref, data: textWidget2Dict },
|
||||||
|
{ ref: parentRef, data: parentDict },
|
||||||
|
helvRefObj,
|
||||||
|
]);
|
||||||
|
partialEvaluator.xref = xref;
|
||||||
|
const task = new WorkerTask("test save");
|
||||||
|
|
||||||
|
const annotation1 = await AnnotationFactory.create(
|
||||||
|
xref,
|
||||||
|
textWidget1Ref,
|
||||||
|
annotationGlobalsMock,
|
||||||
|
idFactoryMock
|
||||||
|
);
|
||||||
|
const annotation2 = await AnnotationFactory.create(
|
||||||
|
xref,
|
||||||
|
textWidget2Ref,
|
||||||
|
annotationGlobalsMock,
|
||||||
|
idFactoryMock
|
||||||
|
);
|
||||||
|
const annotationStorage = new Map();
|
||||||
|
annotationStorage.set(annotation1.data.id, { value: "hello world" });
|
||||||
|
annotationStorage.set(annotation2.data.id, { value: "hello world" });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
|
await annotation1.save(
|
||||||
|
partialEvaluator,
|
||||||
|
task,
|
||||||
|
annotationStorage,
|
||||||
|
changes
|
||||||
|
);
|
||||||
|
await annotation2.save(
|
||||||
|
partialEvaluator,
|
||||||
|
task,
|
||||||
|
annotationStorage,
|
||||||
|
changes
|
||||||
|
);
|
||||||
|
const data = await writeChanges(changes, xref);
|
||||||
|
expect(data.length).toEqual(5);
|
||||||
|
const [, , data1, data2, parentData] = data;
|
||||||
|
expect(data1.ref).toEqual(textWidget1Ref);
|
||||||
|
expect(data2.ref).toEqual(textWidget2Ref);
|
||||||
|
expect(parentData.ref).toEqual(parentRef);
|
||||||
|
|
||||||
|
data1.data = data1.data.replace(/\(D:\d+\)/, "(date)");
|
||||||
|
data2.data = data2.data.replace(/\(D:\d+\)/, "(date)");
|
||||||
|
expect(data1.data).toEqual(
|
||||||
|
"123 0 obj\n" +
|
||||||
|
"<< /Type /Annot /Subtype /Widget /FT /Tx /DA (/Helv 5 Tf) /DR " +
|
||||||
|
"<< /Font << /Helv 314 0 R>>>> /Rect [0 0 32 10] " +
|
||||||
|
"/Parent 125 0 R /AP << /N 4 0 R>> /M (date)>>\nendobj\n"
|
||||||
|
);
|
||||||
|
expect(data2.data).toEqual(
|
||||||
|
"124 0 obj\n" +
|
||||||
|
"<< /Type /Annot /Subtype /Widget /FT /Tx /DA (/Helv 5 Tf) /DR " +
|
||||||
|
"<< /Font << /Helv 314 0 R>>>> /Rect [0 0 32 10] " +
|
||||||
|
"/Parent 125 0 R /AP << /N 5 0 R>> /M (date)>>\nendobj\n"
|
||||||
|
);
|
||||||
|
expect(parentData.data).toEqual(
|
||||||
|
"125 0 obj\n<< /Kids [123 0 R 124 0 R] /T (foo) /V (hello world)>>\nendobj\n"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("should save rotated text", async function () {
|
it("should save rotated text", async function () {
|
||||||
const textWidgetRef = Ref.get(123, 0);
|
const textWidgetRef = Ref.get(123, 0);
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
@ -3080,6 +3155,7 @@ describe("annotation", function () {
|
|||||||
const parentDict = new Dict();
|
const parentDict = new Dict();
|
||||||
parentDict.set("V", Name.get("Off"));
|
parentDict.set("V", Name.get("Off"));
|
||||||
parentDict.set("Kids", [buttonWidgetRef]);
|
parentDict.set("Kids", [buttonWidgetRef]);
|
||||||
|
parentDict.set("T", "RadioGroup");
|
||||||
buttonWidgetDict.set("Parent", parentRef);
|
buttonWidgetDict.set("Parent", parentRef);
|
||||||
|
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
@ -3116,7 +3192,7 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
expect(parentData.ref).toEqual(Ref.get(456, 0));
|
expect(parentData.ref).toEqual(Ref.get(456, 0));
|
||||||
expect(parentData.data).toEqual(
|
expect(parentData.data).toEqual(
|
||||||
"456 0 obj\n<< /V /Checked /Kids [123 0 R]>>\nendobj\n"
|
"456 0 obj\n<< /V /Checked /Kids [123 0 R] /T (RadioGroup)>>\nendobj\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
annotationStorage.set(annotation.data.id, { value: false });
|
annotationStorage.set(annotation.data.id, { value: false });
|
||||||
@ -3142,6 +3218,7 @@ describe("annotation", function () {
|
|||||||
|
|
||||||
const parentDict = new Dict();
|
const parentDict = new Dict();
|
||||||
parentDict.set("Kids", [buttonWidgetRef]);
|
parentDict.set("Kids", [buttonWidgetRef]);
|
||||||
|
parentDict.set("T", "RadioGroup");
|
||||||
buttonWidgetDict.set("Parent", parentRef);
|
buttonWidgetDict.set("Parent", parentRef);
|
||||||
|
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
@ -3178,7 +3255,7 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
expect(parentData.ref).toEqual(Ref.get(456, 0));
|
expect(parentData.ref).toEqual(Ref.get(456, 0));
|
||||||
expect(parentData.data).toEqual(
|
expect(parentData.data).toEqual(
|
||||||
"456 0 obj\n<< /Kids [123 0 R] /V /Checked>>\nendobj\n"
|
"456 0 obj\n<< /Kids [123 0 R] /T (RadioGroup) /V /Checked>>\nendobj\n"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user