mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-08-02 20:37:21 +02:00
Add the possibility to save added annotations when reorganizing a pdf (bug 2023086)
This commit is contained in:
parent
ff1af5a058
commit
04272de41d
@ -354,12 +354,12 @@ class AnnotationFactory {
|
|||||||
|
|
||||||
static async saveNewAnnotations(
|
static async saveNewAnnotations(
|
||||||
evaluator,
|
evaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
annotations,
|
annotations,
|
||||||
imagePromises,
|
imagePromises,
|
||||||
changes
|
changes
|
||||||
) {
|
) {
|
||||||
const xref = evaluator.xref;
|
|
||||||
let baseFontRef;
|
let baseFontRef;
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const { isOffscreenCanvasSupported } = evaluator.options;
|
const { isOffscreenCanvasSupported } = evaluator.options;
|
||||||
|
|||||||
@ -151,6 +151,10 @@ class Page {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createAnnotationEvaluator(handler) {
|
||||||
|
return this.#createPartialEvaluator(handler);
|
||||||
|
}
|
||||||
|
|
||||||
#getInheritableProperty(key, getArray = false) {
|
#getInheritableProperty(key, getArray = false) {
|
||||||
const value = getInheritableProperty({
|
const value = getInheritableProperty({
|
||||||
dict: this.pageDict,
|
dict: this.pageDict,
|
||||||
@ -386,6 +390,7 @@ class Page {
|
|||||||
);
|
);
|
||||||
const newData = await AnnotationFactory.saveNewAnnotations(
|
const newData = await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
this.xref,
|
||||||
task,
|
task,
|
||||||
annotations,
|
annotations,
|
||||||
imagePromises,
|
imagePromises,
|
||||||
|
|||||||
@ -16,16 +16,21 @@
|
|||||||
/** @typedef {import("../document.js").PDFDocument} PDFDocument */
|
/** @typedef {import("../document.js").PDFDocument} PDFDocument */
|
||||||
/** @typedef {import("../document.js").Page} Page */
|
/** @typedef {import("../document.js").Page} Page */
|
||||||
/** @typedef {import("../xref.js").XRef} XRef */
|
/** @typedef {import("../xref.js").XRef} XRef */
|
||||||
|
/** @typedef {import("../worker.js").WorkerTask} WorkerTask */
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
/** @typedef {import("../../shared/message_handler.js").MessageHandler} MessageHandler */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
deepCompare,
|
deepCompare,
|
||||||
getInheritableProperty,
|
getInheritableProperty,
|
||||||
|
getNewAnnotationsMap,
|
||||||
stringToAsciiOrUTF16BE,
|
stringToAsciiOrUTF16BE,
|
||||||
} from "../core_utils.js";
|
} from "../core_utils.js";
|
||||||
import { Dict, isName, Name, Ref, RefSet, RefSetCache } from "../primitives.js";
|
import { Dict, isName, Name, Ref, RefSet, RefSetCache } from "../primitives.js";
|
||||||
import { getModificationDate, stringToPDFString } from "../../shared/util.js";
|
import { getModificationDate, stringToPDFString } from "../../shared/util.js";
|
||||||
import { incrementalUpdate, writeValue } from "../writer.js";
|
import { incrementalUpdate, writeValue } from "../writer.js";
|
||||||
import { NameTree, NumberTree } from "../name_number_tree.js";
|
import { NameTree, NumberTree } from "../name_number_tree.js";
|
||||||
|
import { AnnotationFactory } from "../annotation.js";
|
||||||
import { BaseStream } from "../base_stream.js";
|
import { BaseStream } from "../base_stream.js";
|
||||||
import { StringStream } from "../stream.js";
|
import { StringStream } from "../stream.js";
|
||||||
|
|
||||||
@ -75,8 +80,9 @@ class DocumentData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class XRefWrapper {
|
class XRefWrapper {
|
||||||
constructor(entries) {
|
constructor(entries, getNewRef) {
|
||||||
this.entries = entries;
|
this.entries = entries;
|
||||||
|
this._getNewRef = getNewRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(ref) {
|
fetch(ref) {
|
||||||
@ -94,11 +100,17 @@ class XRefWrapper {
|
|||||||
fetchAsync(ref) {
|
fetchAsync(ref) {
|
||||||
return Promise.resolve(this.fetch(ref));
|
return Promise.resolve(this.fetch(ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNewTemporaryRef() {
|
||||||
|
return this._getNewRef();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PDFEditor {
|
class PDFEditor {
|
||||||
hasSingleFile = false;
|
hasSingleFile = false;
|
||||||
|
|
||||||
|
#newAnnotationsParams = null;
|
||||||
|
|
||||||
currentDocument = null;
|
currentDocument = null;
|
||||||
|
|
||||||
oldPages = [];
|
oldPages = [];
|
||||||
@ -107,7 +119,7 @@ class PDFEditor {
|
|||||||
|
|
||||||
xref = [null];
|
xref = [null];
|
||||||
|
|
||||||
xrefWrapper = new XRefWrapper(this.xref);
|
xrefWrapper = new XRefWrapper(this.xref, () => this.newRef);
|
||||||
|
|
||||||
newRefCount = 1;
|
newRefCount = 1;
|
||||||
|
|
||||||
@ -535,13 +547,33 @@ class PDFEditor {
|
|||||||
/**
|
/**
|
||||||
* Extract pages from the given documents.
|
* Extract pages from the given documents.
|
||||||
* @param {Array<PageInfo>} pageInfos
|
* @param {Array<PageInfo>} pageInfos
|
||||||
|
* @param {Object} annotationStorage - The annotation storage containing the
|
||||||
|
* annotations to be merged into the new document.
|
||||||
|
* @param {MessageHandler} handler - The message handler to use for processing
|
||||||
|
* the annotations.
|
||||||
|
* @param {WorkerTask} task - The worker task to use for reporting progress
|
||||||
|
* and cancellation.
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async extractPages(pageInfos) {
|
async extractPages(pageInfos, annotationStorage, handler, task) {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
let newIndex = 0;
|
let newIndex = 0;
|
||||||
this.hasSingleFile = pageInfos.length === 1;
|
this.hasSingleFile = pageInfos.length === 1;
|
||||||
const allDocumentData = [];
|
const allDocumentData = [];
|
||||||
|
|
||||||
|
if (annotationStorage) {
|
||||||
|
this.#newAnnotationsParams = {
|
||||||
|
handler,
|
||||||
|
task,
|
||||||
|
newAnnotationsByPage: getNewAnnotationsMap(annotationStorage),
|
||||||
|
imagesPromises: AnnotationFactory.generateImages(
|
||||||
|
annotationStorage.values(),
|
||||||
|
this.xrefWrapper,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for (const {
|
for (const {
|
||||||
document,
|
document,
|
||||||
includePages,
|
includePages,
|
||||||
@ -1930,6 +1962,8 @@ class PDFEditor {
|
|||||||
await this.#collectDependencies(resources, true, xref)
|
await this.#collectDependencies(resources, true, xref)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let newAnnots = null;
|
||||||
|
|
||||||
if (annotations) {
|
if (annotations) {
|
||||||
const newAnnotations = await this.#collectDependencies(
|
const newAnnotations = await this.#collectDependencies(
|
||||||
annotations,
|
annotations,
|
||||||
@ -1937,8 +1971,34 @@ class PDFEditor {
|
|||||||
xref
|
xref
|
||||||
);
|
);
|
||||||
this.#fixNamedDestinations(newAnnotations, dedupNamedDestinations);
|
this.#fixNamedDestinations(newAnnotations, dedupNamedDestinations);
|
||||||
pageDict.setIfArray("Annots", newAnnotations);
|
if (Array.isArray(newAnnotations) && newAnnotations.length > 0) {
|
||||||
|
newAnnots = newAnnotations;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAnnotations =
|
||||||
|
this.#newAnnotationsParams?.newAnnotationsByPage.get(pageIndex);
|
||||||
|
if (newAnnotations) {
|
||||||
|
const { handler, task, imagesPromises } = this.#newAnnotationsParams;
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
const newData = await AnnotationFactory.saveNewAnnotations(
|
||||||
|
page.createAnnotationEvaluator(handler),
|
||||||
|
this.xrefWrapper,
|
||||||
|
task,
|
||||||
|
newAnnotations,
|
||||||
|
imagesPromises,
|
||||||
|
changes
|
||||||
|
);
|
||||||
|
for (const [ref, { data }] of changes.items()) {
|
||||||
|
this.xref[ref.num] = data;
|
||||||
|
}
|
||||||
|
newAnnots ||= [];
|
||||||
|
for (const { ref } of newData.annotations) {
|
||||||
|
newAnnots.push(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pageDict.setIfArray("Annots", newAnnots);
|
||||||
|
|
||||||
if (this.useObjectStreams) {
|
if (this.useObjectStreams) {
|
||||||
const newLastRef = this.newRefCount;
|
const newLastRef = this.newRefCount;
|
||||||
|
|||||||
@ -551,7 +551,9 @@ class WorkerMessageHandler {
|
|||||||
return pdfManager.ensureDoc("calculationOrderIds");
|
return pdfManager.ensureDoc("calculationOrderIds");
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.on("ExtractPages", async function ({ pageInfos }) {
|
handler.on(
|
||||||
|
"ExtractPages",
|
||||||
|
async function ({ pageInfos, annotationStorage }) {
|
||||||
if (!pageInfos) {
|
if (!pageInfos) {
|
||||||
warn("extractPages: nothing to extract.");
|
warn("extractPages: nothing to extract.");
|
||||||
return null;
|
return null;
|
||||||
@ -631,16 +633,29 @@ class WorkerMessageHandler {
|
|||||||
warn("extractPages: invalid document.");
|
warn("extractPages: invalid document.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let task;
|
||||||
try {
|
try {
|
||||||
const pdfEditor = new PDFEditor();
|
const pdfEditor = new PDFEditor();
|
||||||
const buffer = await pdfEditor.extractPages(pageInfos);
|
task = new WorkerTask(`ExtractPages: ${pageInfos.length} page(s)`);
|
||||||
|
startWorkerTask(task);
|
||||||
|
const buffer = await pdfEditor.extractPages(
|
||||||
|
pageInfos,
|
||||||
|
annotationStorage,
|
||||||
|
handler,
|
||||||
|
task
|
||||||
|
);
|
||||||
return buffer;
|
return buffer;
|
||||||
} catch (reason) {
|
} catch (reason) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(reason);
|
console.error(reason);
|
||||||
return null;
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (task) {
|
||||||
|
finishWorkerTask(task);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
handler.on(
|
handler.on(
|
||||||
"SaveDocument",
|
"SaveDocument",
|
||||||
|
|||||||
@ -2973,7 +2973,20 @@ class WorkerTransport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extractPages(pageInfos) {
|
extractPages(pageInfos) {
|
||||||
return this.messageHandler.sendWithPromise("ExtractPages", { pageInfos });
|
const params = {
|
||||||
|
pageInfos,
|
||||||
|
};
|
||||||
|
let transfer;
|
||||||
|
if (this.annotationStorage.size > 0) {
|
||||||
|
const { map, transfer: t } = this.annotationStorage.serializable;
|
||||||
|
params.annotationStorage = map;
|
||||||
|
transfer = t;
|
||||||
|
}
|
||||||
|
return this.messageHandler
|
||||||
|
.sendWithPromise("ExtractPages", params, transfer)
|
||||||
|
.finally(() => {
|
||||||
|
this.annotationStorage.resetModified();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getPage(pageNumber) {
|
getPage(pageNumber) {
|
||||||
|
|||||||
@ -144,47 +144,6 @@ class AnnotationEditorLayer {
|
|||||||
this.#uiManager.addLayer(this);
|
this.#uiManager.addLayer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePageIndex(newPageIndex) {
|
|
||||||
for (const editor of this.#allEditorsIterator) {
|
|
||||||
editor.updatePageIndex(newPageIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pageIndex = newPageIndex;
|
|
||||||
this.#uiManager.addLayer(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones all annotation editors from another layer into this layer.
|
|
||||||
* This is typically used when duplicating a page - the editors from the
|
|
||||||
* source page are serialized and then deserialized into the new page's layer.
|
|
||||||
*
|
|
||||||
* @param {AnnotationEditorLayer} clonedFrom - The source annotation editor
|
|
||||||
* layer to clone editors from. If null or undefined, no action is taken.
|
|
||||||
* @returns {Promise<void>} A promise that resolves when all editors have been
|
|
||||||
* cloned and added to this layer.
|
|
||||||
*/
|
|
||||||
async setClonedFrom(clonedFrom) {
|
|
||||||
if (!clonedFrom) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const promises = [];
|
|
||||||
for (const editor of clonedFrom.#allEditorsIterator) {
|
|
||||||
const serialized = editor.serialize(/* isForCopying = */ true);
|
|
||||||
if (!serialized) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
serialized.isCopy = false;
|
|
||||||
promises.push(
|
|
||||||
this.deserialize(serialized).then(deserialized => {
|
|
||||||
if (deserialized) {
|
|
||||||
this.addOrRebuild(deserialized);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await Promise.all(promises);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isEmpty() {
|
get isEmpty() {
|
||||||
return this.#editors.size === 0;
|
return this.#editors.size === 0;
|
||||||
}
|
}
|
||||||
@ -713,14 +672,6 @@ class AnnotationEditorLayer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an id for an editor.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
getNextId() {
|
|
||||||
return this.#uiManager.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
get #currentEditorType() {
|
get #currentEditorType() {
|
||||||
return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode());
|
return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode());
|
||||||
}
|
}
|
||||||
@ -753,7 +704,7 @@ class AnnotationEditorLayer {
|
|||||||
await this.#uiManager.updateMode(options.mode);
|
await this.#uiManager.updateMode(options.mode);
|
||||||
|
|
||||||
const { offsetX, offsetY } = this.#getCenterPoint();
|
const { offsetX, offsetY } = this.#getCenterPoint();
|
||||||
const id = this.getNextId();
|
const id = this.#uiManager.getId();
|
||||||
const editor = this.#createNewEditor({
|
const editor = this.#createNewEditor({
|
||||||
parent: this,
|
parent: this,
|
||||||
id,
|
id,
|
||||||
@ -789,7 +740,7 @@ class AnnotationEditorLayer {
|
|||||||
* @returns {AnnotationEditor}
|
* @returns {AnnotationEditor}
|
||||||
*/
|
*/
|
||||||
createAndAddNewEditor(event, isCentered, data = {}) {
|
createAndAddNewEditor(event, isCentered, data = {}) {
|
||||||
const id = this.getNextId();
|
const id = this.#uiManager.getId();
|
||||||
const editor = this.#createNewEditor({
|
const editor = this.#createNewEditor({
|
||||||
parent: this,
|
parent: this,
|
||||||
id,
|
id,
|
||||||
@ -1073,13 +1024,17 @@ class AnnotationEditorLayer {
|
|||||||
* Render the main editor.
|
* Render the main editor.
|
||||||
* @param {RenderEditorLayerOptions} parameters
|
* @param {RenderEditorLayerOptions} parameters
|
||||||
*/
|
*/
|
||||||
render({ viewport }) {
|
async render({ viewport }) {
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
setLayerDimensions(this.div, viewport);
|
setLayerDimensions(this.div, viewport);
|
||||||
for (const editor of this.#uiManager.getEditors(this.pageIndex)) {
|
for (const editor of this.#uiManager.getEditors(this.pageIndex)) {
|
||||||
this.add(editor);
|
this.add(editor);
|
||||||
editor.rebuild();
|
editor.rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.#uiManager.findClonesForPage(this);
|
||||||
|
this.div.hidden = this.isEmpty;
|
||||||
|
|
||||||
// We're maybe rendering a layer which was invisible when we started to edit
|
// We're maybe rendering a layer which was invisible when we started to edit
|
||||||
// so we must set the different callbacks for it.
|
// so we must set the different callbacks for it.
|
||||||
this.updateMode();
|
this.updateMode();
|
||||||
|
|||||||
@ -235,7 +235,7 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
static deleteAnnotationElement(editor) {
|
static deleteAnnotationElement(editor) {
|
||||||
const fakeEditor = new FakeEditor({
|
const fakeEditor = new FakeEditor({
|
||||||
id: editor.parent.getNextId(),
|
id: editor._uiManager.getId(),
|
||||||
parent: editor.parent,
|
parent: editor.parent,
|
||||||
uiManager: editor._uiManager,
|
uiManager: editor._uiManager,
|
||||||
});
|
});
|
||||||
@ -480,6 +480,10 @@ class AnnotationEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_moveAfterPaste(baseX, baseY) {
|
_moveAfterPaste(baseX, baseY) {
|
||||||
|
if (this.isClone) {
|
||||||
|
delete this.isClone;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const [parentWidth, parentHeight] = this.parentDimensions;
|
const [parentWidth, parentHeight] = this.parentDimensions;
|
||||||
this.setAt(
|
this.setAt(
|
||||||
baseX * parentWidth,
|
baseX * parentWidth,
|
||||||
@ -1862,7 +1866,7 @@ class AnnotationEditor {
|
|||||||
static async deserialize(data, parent, uiManager) {
|
static async deserialize(data, parent, uiManager) {
|
||||||
const editor = new this.prototype.constructor({
|
const editor = new this.prototype.constructor({
|
||||||
parent,
|
parent,
|
||||||
id: parent.getNextId(),
|
id: uiManager.getId(),
|
||||||
uiManager,
|
uiManager,
|
||||||
annotationElementId: data.annotationElementId,
|
annotationElementId: data.annotationElementId,
|
||||||
creationDate: data.creationDate,
|
creationDate: data.creationDate,
|
||||||
|
|||||||
@ -959,7 +959,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const { color, quadPoints, inkLists, opacity } = data;
|
const { color, quadPoints, inkLists, outlines, opacity } = data;
|
||||||
const editor = await super.deserialize(data, parent, uiManager);
|
const editor = await super.deserialize(data, parent, uiManager);
|
||||||
|
|
||||||
editor.color = Util.makeHexColor(...color);
|
editor.color = Util.makeHexColor(...color);
|
||||||
@ -988,9 +988,9 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
editor.#createOutlines();
|
editor.#createOutlines();
|
||||||
editor.#addToDrawLayer();
|
editor.#addToDrawLayer();
|
||||||
editor.rotate(editor.rotation);
|
editor.rotate(editor.rotation);
|
||||||
} else if (inkLists) {
|
} else if (inkLists || outlines) {
|
||||||
editor.#isFreeHighlight = true;
|
editor.#isFreeHighlight = true;
|
||||||
const points = inkLists[0];
|
const points = (inkLists || outlines.points)[0];
|
||||||
const point = {
|
const point = {
|
||||||
x: points[0] - pageX,
|
x: points[0] - pageX,
|
||||||
y: pageHeight - (points[1] - pageY),
|
y: pageHeight - (points[1] - pageY),
|
||||||
|
|||||||
@ -675,6 +675,8 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#allLayers = new Map();
|
#allLayers = new Map();
|
||||||
|
|
||||||
|
#savedAllLayers = null;
|
||||||
|
|
||||||
#altTextManager = null;
|
#altTextManager = null;
|
||||||
|
|
||||||
#annotationStorage = null;
|
#annotationStorage = null;
|
||||||
@ -1841,6 +1843,66 @@ class AnnotationEditorUIManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePageIndex(oldPageIndex, newPageIndex) {
|
||||||
|
for (const editor of this.getEditors(oldPageIndex)) {
|
||||||
|
editor.pageIndex = newPageIndex;
|
||||||
|
}
|
||||||
|
const layer = this.#savedAllLayers.get(oldPageIndex);
|
||||||
|
if (layer) {
|
||||||
|
layer.pageIndex = newPageIndex;
|
||||||
|
this.#allLayers.set(newPageIndex, layer);
|
||||||
|
if (this.#isEnabled) {
|
||||||
|
layer.enable();
|
||||||
|
} else {
|
||||||
|
layer.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startUpdatePages() {
|
||||||
|
this.#savedAllLayers = new Map(this.#allLayers);
|
||||||
|
this.#allLayers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
endUpdatePages() {
|
||||||
|
this.#savedAllLayers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
clonePage(pageIndex, newPageIndex) {
|
||||||
|
for (const editor of this.getEditors(pageIndex)) {
|
||||||
|
const serialized = editor.serialize(
|
||||||
|
editor.mode !== AnnotationEditorType.HIGHLIGHT
|
||||||
|
);
|
||||||
|
if (!serialized) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
serialized.pageIndex = newPageIndex;
|
||||||
|
serialized.id = this.getId();
|
||||||
|
serialized.isClone = true;
|
||||||
|
delete serialized.popupRef;
|
||||||
|
this.#annotationStorage.setValue(serialized.id, serialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findClonesForPage(layer) {
|
||||||
|
const promises = [];
|
||||||
|
const { pageIndex } = layer;
|
||||||
|
for (const [id, editor] of this.#annotationStorage) {
|
||||||
|
if (editor.pageIndex === pageIndex && editor.isClone) {
|
||||||
|
this.#annotationStorage.remove(id);
|
||||||
|
promises.push(
|
||||||
|
layer.deserialize(editor).then(deserializedEditor => {
|
||||||
|
if (deserializedEditor) {
|
||||||
|
deserializedEditor.isClone = true;
|
||||||
|
layer.addOrRebuild(deserializedEditor);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the different possible states of this manager, e.g. is there
|
* Update the different possible states of this manager, e.g. is there
|
||||||
* something to undo, redo, ...
|
* something to undo, redo, ...
|
||||||
|
|||||||
@ -31,9 +31,13 @@ import {
|
|||||||
PDI,
|
PDI,
|
||||||
scrollIntoView,
|
scrollIntoView,
|
||||||
showViewsManager,
|
showViewsManager,
|
||||||
|
switchToEditor,
|
||||||
waitAndClick,
|
waitAndClick,
|
||||||
waitForBrowserTrip,
|
waitForBrowserTrip,
|
||||||
waitForDOMMutation,
|
waitForDOMMutation,
|
||||||
|
waitForPointerUp,
|
||||||
|
waitForSerialized,
|
||||||
|
waitForStorageEntries,
|
||||||
waitForTextToBe,
|
waitForTextToBe,
|
||||||
waitForTooltipToBe,
|
waitForTooltipToBe,
|
||||||
} from "./test_utils.mjs";
|
} from "./test_utils.mjs";
|
||||||
@ -2563,4 +2567,87 @@ describe("Reorganize Pages View", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Copy page with an ink annotation and paste it", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"page_with_number.pdf",
|
||||||
|
".annotationEditorLayer",
|
||||||
|
"50",
|
||||||
|
null,
|
||||||
|
{ enableSplitMerge: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should check that the pasted page has an ink annotation in the DOM", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
// Enable ink editor mode and draw a line on page 1.
|
||||||
|
await switchToEditor("Ink", page);
|
||||||
|
const rect = await getRect(
|
||||||
|
page,
|
||||||
|
".page[data-page-number='1'] .annotationEditorLayer"
|
||||||
|
);
|
||||||
|
const x = rect.x + rect.width * 0.3;
|
||||||
|
const y = rect.y + rect.height * 0.3;
|
||||||
|
const clickHandle = await waitForPointerUp(page);
|
||||||
|
await page.mouse.move(x, y);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(x + 50, y + 50);
|
||||||
|
await page.mouse.up();
|
||||||
|
await awaitPromise(clickHandle);
|
||||||
|
|
||||||
|
// Commit the drawing and wait for it to be serialized.
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
await waitForSerialized(page, 1);
|
||||||
|
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
|
||||||
|
// Select page 1 and copy it.
|
||||||
|
await page.waitForSelector("#viewsManagerStatusActionButton", {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
await waitAndClick(
|
||||||
|
page,
|
||||||
|
`.thumbnail:has(${getThumbnailSelector(1)}) input`
|
||||||
|
);
|
||||||
|
let handlePagesEdited = await waitForPagesEdited(page);
|
||||||
|
await waitAndClick(page, "#viewsManagerStatusActionButton");
|
||||||
|
await waitAndClick(page, "#viewsManagerStatusActionCopy");
|
||||||
|
await awaitPromise(handlePagesEdited);
|
||||||
|
|
||||||
|
// Paste after page 2 so the copy lands at position 3.
|
||||||
|
handlePagesEdited = await waitForPagesEdited(page);
|
||||||
|
await waitAndClick(page, `${getThumbnailSelector(2)}+button`);
|
||||||
|
await awaitPromise(handlePagesEdited);
|
||||||
|
|
||||||
|
// Both the original and the cloned annotation must now be in storage.
|
||||||
|
await waitForStorageEntries(page, 2);
|
||||||
|
|
||||||
|
// Close the reorganize view and navigate to page 3 (the pasted copy)
|
||||||
|
// to trigger rendering of its annotation editor layer.
|
||||||
|
await page.click("#viewsManagerToggleButton");
|
||||||
|
await page.waitForSelector("#viewsManager", { hidden: true });
|
||||||
|
await page.evaluate(() => {
|
||||||
|
window.PDFViewerApplication.pdfViewer.currentPageNumber = 3;
|
||||||
|
});
|
||||||
|
|
||||||
|
// The cloned ink annotation must appear in the DOM of page 3.
|
||||||
|
await page.waitForSelector(`.page[data-page-number="3"] .inkEditor`, {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
const inkEditors = await page.$$(
|
||||||
|
`.page[data-page-number="3"] .inkEditor`
|
||||||
|
);
|
||||||
|
expect(inkEditors.length).withContext(`In ${browserName}`).toBe(1);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4203,6 +4203,7 @@ describe("annotation", function () {
|
|||||||
const changes = new RefSetCache();
|
const changes = new RefSetCache();
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -4320,6 +4321,7 @@ describe("annotation", function () {
|
|||||||
const task = new WorkerTask("test FreeText update");
|
const task = new WorkerTask("test FreeText update");
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -4435,6 +4437,7 @@ describe("annotation", function () {
|
|||||||
const task = new WorkerTask("test Ink creation");
|
const task = new WorkerTask("test Ink creation");
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -4532,6 +4535,7 @@ describe("annotation", function () {
|
|||||||
const task = new WorkerTask("test Ink creation");
|
const task = new WorkerTask("test Ink creation");
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -4764,6 +4768,7 @@ describe("annotation", function () {
|
|||||||
const task = new WorkerTask("test Highlight creation");
|
const task = new WorkerTask("test Highlight creation");
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -4857,6 +4862,7 @@ describe("annotation", function () {
|
|||||||
const task = new WorkerTask("test free Highlight creation");
|
const task = new WorkerTask("test free Highlight creation");
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -4988,6 +4994,7 @@ describe("annotation", function () {
|
|||||||
const task = new WorkerTask("test Highlight update");
|
const task = new WorkerTask("test Highlight update");
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -5051,6 +5058,7 @@ describe("annotation", function () {
|
|||||||
const task = new WorkerTask("test Highlight update");
|
const task = new WorkerTask("test Highlight update");
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -5215,6 +5223,7 @@ describe("annotation", function () {
|
|||||||
const task = new WorkerTask("test Stamp creation");
|
const task = new WorkerTask("test Stamp creation");
|
||||||
await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
|
xref,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6254,6 +6254,79 @@ small scripts as well as for`);
|
|||||||
await loadingTask.destroy();
|
await loadingTask.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("save an ink annotation on a cloned page", async function () {
|
||||||
|
let loadingTask = getDocument(buildGetDocumentParams("empty.pdf"));
|
||||||
|
let pdfDoc = await loadingTask.promise;
|
||||||
|
|
||||||
|
// Simulate what clonePage() puts in annotationStorage when a page is
|
||||||
|
// copied: the original annotation stays on pageIndex 0 and the clone
|
||||||
|
// is placed on pageIndex 1 (the new position of the pasted copy).
|
||||||
|
const inkAnnotation = {
|
||||||
|
annotationType: AnnotationEditorType.INK,
|
||||||
|
rect: [50, 50, 200, 200],
|
||||||
|
rotation: 0,
|
||||||
|
structTreeParentId: null,
|
||||||
|
popupRef: "",
|
||||||
|
color: [0, 0, 255],
|
||||||
|
opacity: 1,
|
||||||
|
thickness: 2,
|
||||||
|
paths: {
|
||||||
|
lines: [
|
||||||
|
new Float32Array([
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
50,
|
||||||
|
200,
|
||||||
|
NaN,
|
||||||
|
NaN,
|
||||||
|
NaN,
|
||||||
|
NaN,
|
||||||
|
200,
|
||||||
|
50,
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
points: [[50, 200, 100, 100, 200, 50]],
|
||||||
|
},
|
||||||
|
isCopy: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
pdfDoc.annotationStorage.setValue("pdfjs_internal_editor_0", {
|
||||||
|
...inkAnnotation,
|
||||||
|
pageIndex: 0,
|
||||||
|
});
|
||||||
|
pdfDoc.annotationStorage.setValue("pdfjs_internal_editor_1", {
|
||||||
|
...inkAnnotation,
|
||||||
|
pageIndex: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extract page 0 twice: once at output position 0 (original) and once
|
||||||
|
// at output position 1 (clone), mirroring copy+paste in the UI.
|
||||||
|
const data = await pdfDoc.extractPages([
|
||||||
|
{ document: null, includePages: [0], pageIndices: [0] },
|
||||||
|
{ document: null, includePages: [0], pageIndices: [1] },
|
||||||
|
]);
|
||||||
|
await loadingTask.destroy();
|
||||||
|
|
||||||
|
loadingTask = getDocument(data);
|
||||||
|
pdfDoc = await loadingTask.promise;
|
||||||
|
|
||||||
|
expect(pdfDoc.numPages).toEqual(2);
|
||||||
|
|
||||||
|
// Both pages should carry the ink annotation.
|
||||||
|
for (let i = 1; i <= 2; i++) {
|
||||||
|
const pdfPage = await pdfDoc.getPage(i);
|
||||||
|
const annotations = await pdfPage.getAnnotations();
|
||||||
|
expect(annotations.length).withContext(`Page ${i}`).toEqual(1);
|
||||||
|
expect(annotations[0].annotationType)
|
||||||
|
.withContext(`Page ${i}`)
|
||||||
|
.toEqual(AnnotationType.INK);
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadingTask.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
it("fills missing pageIndices with the first free slots", async function () {
|
it("fills missing pageIndices with the first free slots", async function () {
|
||||||
let loadingTask = getDocument(
|
let loadingTask = getDocument(
|
||||||
buildGetDocumentParams("tracemonkey.pdf")
|
buildGetDocumentParams("tracemonkey.pdf")
|
||||||
|
|||||||
@ -39,7 +39,6 @@ import { GenericL10n } from "web-null_l10n";
|
|||||||
* @property {TextLayer} [textLayer]
|
* @property {TextLayer} [textLayer]
|
||||||
* @property {DrawLayer} [drawLayer]
|
* @property {DrawLayer} [drawLayer]
|
||||||
* @property {function} [onAppend]
|
* @property {function} [onAppend]
|
||||||
* @property {AnnotationEditorLayer} [clonedFrom]
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,8 +60,6 @@ class AnnotationEditorLayerBuilder {
|
|||||||
|
|
||||||
#uiManager;
|
#uiManager;
|
||||||
|
|
||||||
#clonedFrom = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AnnotationEditorLayerBuilderOptions} options
|
* @param {AnnotationEditorLayerBuilderOptions} options
|
||||||
*/
|
*/
|
||||||
@ -82,7 +79,6 @@ class AnnotationEditorLayerBuilder {
|
|||||||
this.#drawLayer = options.drawLayer || null;
|
this.#drawLayer = options.drawLayer || null;
|
||||||
this.#onAppend = options.onAppend || null;
|
this.#onAppend = options.onAppend || null;
|
||||||
this.#structTreeLayer = options.structTreeLayer || null;
|
this.#structTreeLayer = options.structTreeLayer || null;
|
||||||
this.#clonedFrom = options.clonedFrom || null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePageIndex(newPageIndex) {
|
updatePageIndex(newPageIndex) {
|
||||||
@ -130,11 +126,6 @@ class AnnotationEditorLayerBuilder {
|
|||||||
drawLayer: this.#drawLayer,
|
drawLayer: this.#drawLayer,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.annotationEditorLayer.setClonedFrom(
|
|
||||||
this.#clonedFrom?.annotationEditorLayer
|
|
||||||
);
|
|
||||||
this.#clonedFrom = null;
|
|
||||||
|
|
||||||
const parameters = {
|
const parameters = {
|
||||||
viewport: clonedViewport,
|
viewport: clonedViewport,
|
||||||
div,
|
div,
|
||||||
@ -142,7 +133,7 @@ class AnnotationEditorLayerBuilder {
|
|||||||
intent,
|
intent,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.annotationEditorLayer.render(parameters);
|
await this.annotationEditorLayer.render(parameters);
|
||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -107,7 +107,6 @@ import { XfaLayerBuilder } from "./xfa_layer_builder.js";
|
|||||||
* @property {boolean} [enableAutoLinking] - Enable creation of hyperlinks from
|
* @property {boolean} [enableAutoLinking] - Enable creation of hyperlinks from
|
||||||
* text that look like URLs. The default value is `true`.
|
* text that look like URLs. The default value is `true`.
|
||||||
* @property {CommentManager} [commentManager] - The comment manager instance.
|
* @property {CommentManager} [commentManager] - The comment manager instance.
|
||||||
* @property {PDFPageView} [clonedFrom] - The page view that is cloned
|
|
||||||
* to.
|
* to.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -172,8 +171,6 @@ class PDFPageView extends BasePDFPageView {
|
|||||||
|
|
||||||
#layers = [null, null, null, null];
|
#layers = [null, null, null, null];
|
||||||
|
|
||||||
#clonedFrom = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {PDFPageViewOptions} options
|
* @param {PDFPageViewOptions} options
|
||||||
*/
|
*/
|
||||||
@ -205,7 +202,6 @@ class PDFPageView extends BasePDFPageView {
|
|||||||
options.capCanvasAreaFactor ?? AppOptions.get("capCanvasAreaFactor");
|
options.capCanvasAreaFactor ?? AppOptions.get("capCanvasAreaFactor");
|
||||||
this.#enableAutoLinking = options.enableAutoLinking !== false;
|
this.#enableAutoLinking = options.enableAutoLinking !== false;
|
||||||
this.#commentManager = options.commentManager || null;
|
this.#commentManager = options.commentManager || null;
|
||||||
this.#clonedFrom = options.clonedFrom || null;
|
|
||||||
|
|
||||||
this.l10n = options.l10n;
|
this.l10n = options.l10n;
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||||
@ -301,7 +297,6 @@ class PDFPageView extends BasePDFPageView {
|
|||||||
enableAutoLinking: this.#enableAutoLinking,
|
enableAutoLinking: this.#enableAutoLinking,
|
||||||
commentManager: this.#commentManager,
|
commentManager: this.#commentManager,
|
||||||
l10n: this.l10n,
|
l10n: this.l10n,
|
||||||
clonedFrom: this,
|
|
||||||
});
|
});
|
||||||
clone.setPdfPage(this.pdfPage.clone(id - 1));
|
clone.setPdfPage(this.pdfPage.clone(id - 1));
|
||||||
return clone;
|
return clone;
|
||||||
@ -355,6 +350,7 @@ class PDFPageView extends BasePDFPageView {
|
|||||||
if (this.id === newPageNumber) {
|
if (this.id === newPageNumber) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const oldPageNumber = this.id;
|
||||||
this.id = newPageNumber;
|
this.id = newPageNumber;
|
||||||
this.renderingId = `page${newPageNumber}`;
|
this.renderingId = `page${newPageNumber}`;
|
||||||
if (this.pdfPage) {
|
if (this.pdfPage) {
|
||||||
@ -368,7 +364,11 @@ class PDFPageView extends BasePDFPageView {
|
|||||||
this._textHighlighter.pageIdx = newPageNumber - 1;
|
this._textHighlighter.pageIdx = newPageNumber - 1;
|
||||||
// Don't update the page index for the draw layer, since it's just used as
|
// Don't update the page index for the draw layer, since it's just used as
|
||||||
// an identifier.
|
// an identifier.
|
||||||
this.annotationEditorLayer?.updatePageIndex(newPageNumber - 1);
|
|
||||||
|
this.#layerProperties.annotationEditorUIManager?.updatePageIndex(
|
||||||
|
oldPageNumber - 1,
|
||||||
|
newPageNumber - 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPdfPage(pdfPage) {
|
setPdfPage(pdfPage) {
|
||||||
@ -1207,12 +1207,10 @@ class PDFPageView extends BasePDFPageView {
|
|||||||
annotationLayer: this.annotationLayer?.annotationLayer,
|
annotationLayer: this.annotationLayer?.annotationLayer,
|
||||||
textLayer: this.textLayer,
|
textLayer: this.textLayer,
|
||||||
drawLayer: this.drawLayer.getDrawLayer(),
|
drawLayer: this.drawLayer.getDrawLayer(),
|
||||||
clonedFrom: this.#clonedFrom?.annotationEditorLayer,
|
|
||||||
onAppend: annotationEditorLayerDiv => {
|
onAppend: annotationEditorLayerDiv => {
|
||||||
this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer");
|
this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.#clonedFrom = null;
|
|
||||||
this.#renderAnnotationEditorLayer();
|
this.#renderAnnotationEditorLayer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -80,8 +80,6 @@ class TempImageFactory {
|
|||||||
class PDFThumbnailView extends RenderableView {
|
class PDFThumbnailView extends RenderableView {
|
||||||
#renderingState = RenderingStates.INITIAL;
|
#renderingState = RenderingStates.INITIAL;
|
||||||
|
|
||||||
static foo = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {PDFThumbnailViewOptions} options
|
* @param {PDFThumbnailViewOptions} options
|
||||||
*/
|
*/
|
||||||
@ -99,7 +97,6 @@ class PDFThumbnailView extends RenderableView {
|
|||||||
enableSplitMerge = false,
|
enableSplitMerge = false,
|
||||||
}) {
|
}) {
|
||||||
super();
|
super();
|
||||||
this.foo = PDFThumbnailView.foo++;
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.renderingId = `thumbnail${id}`;
|
this.renderingId = `thumbnail${id}`;
|
||||||
this.pageLabel = null;
|
this.pageLabel = null;
|
||||||
|
|||||||
@ -1209,6 +1209,7 @@ class PDFViewer {
|
|||||||
const viewerElement =
|
const viewerElement =
|
||||||
this._scrollMode === ScrollMode.PAGE ? null : this.viewer;
|
this._scrollMode === ScrollMode.PAGE ? null : this.viewer;
|
||||||
if (viewerElement) {
|
if (viewerElement) {
|
||||||
|
this.#annotationEditorUIManager?.startUpdatePages();
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
for (let i = 0, ii = this.#savedPageViews.length; i < ii; i++) {
|
for (let i = 0, ii = this.#savedPageViews.length; i < ii; i++) {
|
||||||
const page = this.#savedPageViews[i];
|
const page = this.#savedPageViews[i];
|
||||||
@ -1216,6 +1217,7 @@ class PDFViewer {
|
|||||||
fragment.append(page.div);
|
fragment.append(page.div);
|
||||||
}
|
}
|
||||||
viewerElement.replaceChildren(fragment);
|
viewerElement.replaceChildren(fragment);
|
||||||
|
this.#annotationEditorUIManager?.endUpdatePages();
|
||||||
}
|
}
|
||||||
this._pages = this.#savedPageViews;
|
this._pages = this.#savedPageViews;
|
||||||
this.#savedPageViews = null;
|
this.#savedPageViews = null;
|
||||||
@ -1238,6 +1240,9 @@ class PDFViewer {
|
|||||||
this._currentPageNumber = 0;
|
this._currentPageNumber = 0;
|
||||||
const prevPages = this._pages;
|
const prevPages = this._pages;
|
||||||
const newPages = (this._pages = []);
|
const newPages = (this._pages = []);
|
||||||
|
|
||||||
|
this.#annotationEditorUIManager?.startUpdatePages();
|
||||||
|
|
||||||
for (let i = 1, ii = pagesMapper.pagesNumber; i <= ii; i++) {
|
for (let i = 1, ii = pagesMapper.pagesNumber; i <= ii; i++) {
|
||||||
const prevPageNumber = pagesMapper.getPrevPageNumber(i);
|
const prevPageNumber = pagesMapper.getPrevPageNumber(i);
|
||||||
if (prevPageNumber < 0) {
|
if (prevPageNumber < 0) {
|
||||||
@ -1245,6 +1250,10 @@ class PDFViewer {
|
|||||||
if (hasBeenCut) {
|
if (hasBeenCut) {
|
||||||
page.updatePageNumber(i);
|
page.updatePageNumber(i);
|
||||||
} else {
|
} else {
|
||||||
|
this.#annotationEditorUIManager?.clonePage(
|
||||||
|
-prevPageNumber - 1,
|
||||||
|
i - 1
|
||||||
|
);
|
||||||
page = page.clone(i);
|
page = page.clone(i);
|
||||||
}
|
}
|
||||||
newPages.push(page);
|
newPages.push(page);
|
||||||
@ -1255,6 +1264,8 @@ class PDFViewer {
|
|||||||
page.updatePageNumber(i);
|
page.updatePageNumber(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.#annotationEditorUIManager?.endUpdatePages();
|
||||||
|
|
||||||
if (type === "paste") {
|
if (type === "paste") {
|
||||||
this.#copiedPageViews = null;
|
this.#copiedPageViews = null;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user