Merge pull request #16523 from calixteman/restore_save
[Editor] Add the possibility to update an existing annotation with some new properties when saving or printing
This commit is contained in:
commit
b8447eb49f
@ -264,6 +264,9 @@ class AnnotationFactory {
|
||||
const promises = [];
|
||||
|
||||
for (const annotation of annotations) {
|
||||
if (annotation.deleted) {
|
||||
continue;
|
||||
}
|
||||
switch (annotation.annotationType) {
|
||||
case AnnotationEditorType.FREETEXT:
|
||||
if (!baseFontRef) {
|
||||
@ -308,6 +311,9 @@ class AnnotationFactory {
|
||||
const { isOffscreenCanvasSupported } = evaluator.options;
|
||||
const promises = [];
|
||||
for (const annotation of annotations) {
|
||||
if (annotation.deleted) {
|
||||
continue;
|
||||
}
|
||||
switch (annotation.annotationType) {
|
||||
case AnnotationEditorType.FREETEXT:
|
||||
promises.push(
|
||||
@ -466,6 +472,7 @@ class Annotation {
|
||||
const MK = dict.get("MK");
|
||||
this.setBorderAndBackgroundColors(MK);
|
||||
this.setRotation(MK);
|
||||
this.ref = params.ref instanceof Ref ? params.ref : null;
|
||||
|
||||
this._streams = [];
|
||||
if (this.appearance) {
|
||||
@ -1467,7 +1474,7 @@ class MarkupAnnotation extends Annotation {
|
||||
}
|
||||
|
||||
static async createNewAnnotation(xref, annotation, dependencies, params) {
|
||||
const annotationRef = xref.getNewTemporaryRef();
|
||||
const annotationRef = annotation.ref || xref.getNewTemporaryRef();
|
||||
const ap = await this.createNewAppearanceStream(annotation, xref, params);
|
||||
const buffer = [];
|
||||
let annotationDict;
|
||||
@ -1497,11 +1504,17 @@ class MarkupAnnotation extends Annotation {
|
||||
const ap = await this.createNewAppearanceStream(annotation, xref, params);
|
||||
const annotationDict = this.createNewDict(annotation, xref, { ap });
|
||||
|
||||
return new this.prototype.constructor({
|
||||
const newAnnotation = new this.prototype.constructor({
|
||||
dict: annotationDict,
|
||||
xref,
|
||||
isOffscreenCanvasSupported: params.isOffscreenCanvasSupported,
|
||||
});
|
||||
|
||||
if (annotation.ref) {
|
||||
newAnnotation.ref = newAnnotation.refToReplace = annotation.ref;
|
||||
}
|
||||
|
||||
return newAnnotation;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1511,7 +1524,6 @@ class WidgetAnnotation extends Annotation {
|
||||
|
||||
const { dict, xref } = params;
|
||||
const data = this.data;
|
||||
this.ref = params.ref;
|
||||
this._needAppearances = params.needAppearances;
|
||||
|
||||
data.annotationType = AnnotationType.WIDGET;
|
||||
|
@ -41,7 +41,7 @@ import {
|
||||
XRefEntryException,
|
||||
XRefParseException,
|
||||
} from "./core_utils.js";
|
||||
import { Dict, isName, Name, Ref } from "./primitives.js";
|
||||
import { Dict, isName, isRefsEqual, Name, Ref, RefSet } from "./primitives.js";
|
||||
import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js";
|
||||
import { BaseStream } from "./base_stream.js";
|
||||
import { calculateMD5 } from "./crypto.js";
|
||||
@ -258,6 +258,24 @@ class Page {
|
||||
);
|
||||
}
|
||||
|
||||
#replaceIdByRef(annotations, deletedAnnotations) {
|
||||
for (const annotation of annotations) {
|
||||
if (annotation.id) {
|
||||
const ref = Ref.fromString(annotation.id);
|
||||
if (!ref) {
|
||||
warn(`A non-linked annotation cannot be modified: ${annotation.id}`);
|
||||
continue;
|
||||
}
|
||||
if (annotation.deleted) {
|
||||
deletedAnnotations.put(ref);
|
||||
continue;
|
||||
}
|
||||
annotation.ref = ref;
|
||||
delete annotation.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async saveNewAnnotations(handler, task, annotations) {
|
||||
if (this.xfaFactory) {
|
||||
throw new Error("XFA: Cannot save new annotations.");
|
||||
@ -276,8 +294,13 @@ class Page {
|
||||
options: this.evaluatorOptions,
|
||||
});
|
||||
|
||||
const deletedAnnotations = new RefSet();
|
||||
this.#replaceIdByRef(annotations, deletedAnnotations);
|
||||
|
||||
const pageDict = this.pageDict;
|
||||
const annotationsArray = this.annotations.slice();
|
||||
const annotationsArray = this.annotations.filter(
|
||||
a => !(a instanceof Ref && deletedAnnotations.has(a))
|
||||
);
|
||||
const newData = await AnnotationFactory.saveNewAnnotations(
|
||||
partialEvaluator,
|
||||
task,
|
||||
@ -401,11 +424,14 @@ class Page {
|
||||
const newAnnotationsByPage = !this.xfaFactory
|
||||
? getNewAnnotationsMap(annotationStorage)
|
||||
: null;
|
||||
let deletedAnnotations = null;
|
||||
|
||||
let newAnnotationsPromise = Promise.resolve(null);
|
||||
if (newAnnotationsByPage) {
|
||||
const newAnnotations = newAnnotationsByPage.get(this.pageIndex);
|
||||
if (newAnnotations) {
|
||||
deletedAnnotations = new RefSet();
|
||||
this.#replaceIdByRef(newAnnotations, deletedAnnotations);
|
||||
newAnnotationsPromise = AnnotationFactory.printNewAnnotations(
|
||||
partialEvaluator,
|
||||
task,
|
||||
@ -446,6 +472,25 @@ class Page {
|
||||
newAnnotationsPromise,
|
||||
]).then(function ([pageOpList, annotations, newAnnotations]) {
|
||||
if (newAnnotations) {
|
||||
// Some annotations can already exist (if it has the refToReplace
|
||||
// property). In this case, we replace the old annotation by the new
|
||||
// one.
|
||||
annotations = annotations.filter(
|
||||
a => !(a.ref && deletedAnnotations.has(a.ref))
|
||||
);
|
||||
for (let i = 0, ii = newAnnotations.length; i < ii; i++) {
|
||||
const newAnnotation = newAnnotations[i];
|
||||
if (newAnnotation.refToReplace) {
|
||||
const j = annotations.findIndex(
|
||||
a => a.ref && isRefsEqual(a.ref, newAnnotation.refToReplace)
|
||||
);
|
||||
if (j >= 0) {
|
||||
annotations.splice(j, 1, newAnnotation);
|
||||
newAnnotations.splice(i--, 1);
|
||||
ii--;
|
||||
}
|
||||
}
|
||||
}
|
||||
annotations = annotations.concat(newAnnotations);
|
||||
}
|
||||
if (
|
||||
|
@ -279,6 +279,23 @@ class Ref {
|
||||
return `${this.num}R${this.gen}`;
|
||||
}
|
||||
|
||||
static fromString(str) {
|
||||
const ref = RefCache[str];
|
||||
if (ref) {
|
||||
return ref;
|
||||
}
|
||||
const m = /^(\d+)R(\d*)$/.exec(str);
|
||||
if (!m || m[1] === "0") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return (RefCache[str] = new Ref(
|
||||
parseInt(m[1]),
|
||||
!m[2] ? 0 : parseInt(m[2])
|
||||
));
|
||||
}
|
||||
|
||||
static get(num, gen) {
|
||||
const key = gen === 0 ? `${num}R` : `${num}R${gen}`;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -599,3 +599,4 @@
|
||||
!bug1529502.pdf
|
||||
!issue16500.pdf
|
||||
!issue16538.pdf
|
||||
!freetexts.pdf
|
||||
|
BIN
test/pdfs/freetexts.pdf
Executable file
BIN
test/pdfs/freetexts.pdf
Executable file
Binary file not shown.
@ -7720,5 +7720,91 @@
|
||||
"md5": "35b691c3a343f4531bd287b001b67a77",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "freetexts-editor-print",
|
||||
"file": "pdfs/freetexts.pdf",
|
||||
"md5": "da1310a25ab796c1201810070d5032a3",
|
||||
"rounds": 1,
|
||||
"lastPage": 1,
|
||||
"type": "eq",
|
||||
"print": true,
|
||||
"annotationStorage": {
|
||||
"pdfjs_internal_editor_0": {
|
||||
"annotationType": 3,
|
||||
"color": [0, 0, 255],
|
||||
"fontSize": 21,
|
||||
"value": "The content must have been changed",
|
||||
"pageIndex": 0,
|
||||
"rect": [ 92.58600000000003, 415.2426115258789, 449.1110015258789, 447.59261],
|
||||
"rotation": 0,
|
||||
"id": "36R"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "freetexts-editor-save",
|
||||
"file": "pdfs/freetexts.pdf",
|
||||
"md5": "da1310a25ab796c1201810070d5032a3",
|
||||
"rounds": 1,
|
||||
"lastPage": 1,
|
||||
"type": "eq",
|
||||
"save": true,
|
||||
"print": true,
|
||||
"annotationStorage": {
|
||||
"pdfjs_internal_editor_0": {
|
||||
"annotationType": 3,
|
||||
"color": [255, 0, 0],
|
||||
"fontSize": 21,
|
||||
"value": "The content must have been changed",
|
||||
"pageIndex": 0,
|
||||
"rect": [ 92.58600000000003, 415.2426115258789, 449.1110015258789, 447.59261],
|
||||
"rotation": 0,
|
||||
"id": "36R"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "freetexts-delete-editor-print",
|
||||
"file": "pdfs/freetexts.pdf",
|
||||
"md5": "da1310a25ab796c1201810070d5032a3",
|
||||
"rounds": 1,
|
||||
"lastPage": 1,
|
||||
"type": "eq",
|
||||
"print": true,
|
||||
"annotationStorage": {
|
||||
"pdfjs_internal_editor_0": {
|
||||
"pageIndex": 0,
|
||||
"deleted": true,
|
||||
"id": "36R"
|
||||
},
|
||||
"pdfjs_internal_editor_1": {
|
||||
"pageIndex": 0,
|
||||
"deleted": true,
|
||||
"id": "53R"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "freetexts-delete-editor-save",
|
||||
"file": "pdfs/freetexts.pdf",
|
||||
"md5": "da1310a25ab796c1201810070d5032a3",
|
||||
"rounds": 1,
|
||||
"lastPage": 1,
|
||||
"type": "eq",
|
||||
"save": true,
|
||||
"print": true,
|
||||
"annotationStorage": {
|
||||
"pdfjs_internal_editor_0": {
|
||||
"pageIndex": 0,
|
||||
"deleted": true,
|
||||
"id": "36R"
|
||||
},
|
||||
"pdfjs_internal_editor_1": {
|
||||
"pageIndex": 0,
|
||||
"deleted": true,
|
||||
"id": "53R"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user