mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-22 16:18:08 +02:00
[api-minor][Editor] When switching to editing mode, redraw pages containing editable annotations
Right now, editable annotations are using their own canvas when they're drawn, but it induces several issues: - if the annotation has to be composed with the page then the canvas must be correctly composed with its parent. That means we should move the canvas under canvasWrapper and we should extract composing info from the drawing instructions... Currently it's the case with highlight annotations. - we use some extra memory for those canvas even if the user will never edit them, which the case for example when opening a pdf in Fenix. So with this patch, all the editable annotations are drawn on the canvas. When the user switches to editing mode, then the pages with some editable annotations are redrawn but without them: they'll be replaced by their counterpart in the annotation editor layer.
This commit is contained in:
parent
75129fd61a
commit
64635f3b35
13 changed files with 358 additions and 103 deletions
|
@ -680,6 +680,7 @@ class Annotation {
|
|||
hasOwnCanvas: false,
|
||||
noRotate: !!(this.flags & AnnotationFlag.NOROTATE),
|
||||
noHTML: isLocked && isContentLocked,
|
||||
isEditable: false,
|
||||
};
|
||||
|
||||
if (params.collectFields) {
|
||||
|
@ -776,6 +777,10 @@ class Annotation {
|
|||
return this.printable;
|
||||
}
|
||||
|
||||
mustBeViewedWhenEditing() {
|
||||
return !this.data.isEditable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
|
@ -3802,7 +3807,8 @@ class FreeTextAnnotation extends MarkupAnnotation {
|
|||
// It uses its own canvas in order to be hidden if edited.
|
||||
// But if it has the noHTML flag, it means that we don't want to be able
|
||||
// to modify it so we can just draw it on the main canvas.
|
||||
this.data.hasOwnCanvas = !this.data.noHTML;
|
||||
this.data.hasOwnCanvas = this.data.noRotate;
|
||||
this.data.isEditable = !this.data.noHTML;
|
||||
// We want to be able to add mouse listeners to the annotation.
|
||||
this.data.noHTML = false;
|
||||
|
||||
|
|
|
@ -411,6 +411,8 @@ class Page {
|
|||
intent,
|
||||
cacheKey,
|
||||
annotationStorage = null,
|
||||
isEditing = false,
|
||||
modifiedIds = null,
|
||||
}) {
|
||||
const contentStreamPromise = this.getContentStream();
|
||||
const resourcesPromise = this.loadResources([
|
||||
|
@ -579,7 +581,9 @@ class Page {
|
|||
if (
|
||||
intentAny ||
|
||||
(intentDisplay &&
|
||||
annotation.mustBeViewed(annotationStorage, renderForms)) ||
|
||||
annotation.mustBeViewed(annotationStorage, renderForms) &&
|
||||
((isEditing && annotation.mustBeViewedWhenEditing()) ||
|
||||
(!isEditing && !modifiedIds?.has(annotation.data.id)))) ||
|
||||
(intentPrint && annotation.mustBePrinted(annotationStorage))
|
||||
) {
|
||||
opListPromises.push(
|
||||
|
|
|
@ -752,6 +752,8 @@ class WorkerMessageHandler {
|
|||
intent: data.intent,
|
||||
cacheKey: data.cacheKey,
|
||||
annotationStorage: data.annotationStorage,
|
||||
isEditing: data.isEditing,
|
||||
modifiedIds: data.modifiedIds,
|
||||
})
|
||||
.then(
|
||||
function (operatorListInfo) {
|
||||
|
|
|
@ -198,6 +198,10 @@ class AnnotationElement {
|
|||
return !!(titleObj?.str || contentsObj?.str || richText?.str);
|
||||
}
|
||||
|
||||
get _isEditable() {
|
||||
return this.data.isEditable;
|
||||
}
|
||||
|
||||
get hasPopupData() {
|
||||
return AnnotationElement._hasPopupData(this.data);
|
||||
}
|
||||
|
@ -734,10 +738,6 @@ class AnnotationElement {
|
|||
}
|
||||
}
|
||||
|
||||
get _isEditable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
_editOnDoubleClick() {
|
||||
if (!this._isEditable) {
|
||||
return;
|
||||
|
@ -2530,10 +2530,6 @@ class FreeTextAnnotationElement extends AnnotationElement {
|
|||
|
||||
return this.container;
|
||||
}
|
||||
|
||||
get _isEditable() {
|
||||
return this.data.hasOwnCanvas;
|
||||
}
|
||||
}
|
||||
|
||||
class LineAnnotationElement extends AnnotationElement {
|
||||
|
@ -3107,6 +3103,10 @@ class AnnotationLayer {
|
|||
}
|
||||
}
|
||||
|
||||
hasEditableAnnotations() {
|
||||
return this.#editableAnnotations.size > 0;
|
||||
}
|
||||
|
||||
#appendElement(element, id) {
|
||||
const contentElement = element.firstChild || element;
|
||||
contentElement.id = `${AnnotationPrefix}${id}`;
|
||||
|
@ -3188,7 +3188,7 @@ class AnnotationLayer {
|
|||
}
|
||||
this.#appendElement(rendered, data.id);
|
||||
|
||||
if (element.annotationEditorType > 0) {
|
||||
if (element._isEditable) {
|
||||
this.#editableAnnotations.set(element.data.id, element);
|
||||
this._annotationEditorUIManager?.renderAnnotationElement(element);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { objectFromMap, unreachable } from "../shared/util.js";
|
||||
import { objectFromMap, shadow, unreachable } from "../shared/util.js";
|
||||
import { AnnotationEditor } from "./editor/editor.js";
|
||||
import { MurmurHash3_64 } from "../shared/murmurhash3.js";
|
||||
|
||||
|
@ -29,6 +29,8 @@ const SerializableEmpty = Object.freeze({
|
|||
class AnnotationStorage {
|
||||
#modified = false;
|
||||
|
||||
#modifiedIds = null;
|
||||
|
||||
#storage = new Map();
|
||||
|
||||
constructor() {
|
||||
|
@ -248,6 +250,34 @@ class AnnotationStorage {
|
|||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
resetModifiedIds() {
|
||||
this.#modifiedIds = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {{ids: Set<string>, hash: string}}
|
||||
*/
|
||||
get modifiedIds() {
|
||||
if (this.#modifiedIds) {
|
||||
return this.#modifiedIds;
|
||||
}
|
||||
const ids = [];
|
||||
for (const value of this.#storage.values()) {
|
||||
if (
|
||||
!(value instanceof AnnotationEditor) ||
|
||||
!value.annotationElementId ||
|
||||
!value.serialize()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
ids.push(value.annotationElementId);
|
||||
}
|
||||
return (this.#modifiedIds = {
|
||||
ids: new Set(ids),
|
||||
hash: ids.join(","),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -282,6 +312,13 @@ class PrintAnnotationStorage extends AnnotationStorage {
|
|||
get serializable() {
|
||||
return this.#serializable;
|
||||
}
|
||||
|
||||
get modifiedIds() {
|
||||
return shadow(this, "modifiedIds", {
|
||||
ids: new Set(),
|
||||
hash: "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { AnnotationStorage, PrintAnnotationStorage, SerializableEmpty };
|
||||
|
|
|
@ -1227,6 +1227,7 @@ class PDFDocumentProxy {
|
|||
* @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] - Map some
|
||||
* annotation ids with canvases used to render them.
|
||||
* @property {PrintAnnotationStorage} [printAnnotationStorage]
|
||||
* @property {boolean} [isEditing] - Render the page in editing mode.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -1248,6 +1249,7 @@ class PDFDocumentProxy {
|
|||
* from the {@link AnnotationStorage}-instance; useful e.g. for printing.
|
||||
* The default value is `AnnotationMode.ENABLE`.
|
||||
* @property {PrintAnnotationStorage} [printAnnotationStorage]
|
||||
* @property {boolean} [isEditing] - Render the page in editing mode.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -1420,13 +1422,15 @@ class PDFPageProxy {
|
|||
annotationCanvasMap = null,
|
||||
pageColors = null,
|
||||
printAnnotationStorage = null,
|
||||
isEditing = false,
|
||||
}) {
|
||||
this._stats?.time("Overall");
|
||||
|
||||
const intentArgs = this._transport.getRenderingIntent(
|
||||
intent,
|
||||
annotationMode,
|
||||
printAnnotationStorage
|
||||
printAnnotationStorage,
|
||||
isEditing
|
||||
);
|
||||
const { renderingIntent, cacheKey } = intentArgs;
|
||||
// If there was a pending destroy, cancel it so no cleanup happens during
|
||||
|
@ -1560,6 +1564,7 @@ class PDFPageProxy {
|
|||
intent = "display",
|
||||
annotationMode = AnnotationMode.ENABLE,
|
||||
printAnnotationStorage = null,
|
||||
isEditing = false,
|
||||
} = {}) {
|
||||
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) {
|
||||
throw new Error("Not implemented: getOperatorList");
|
||||
|
@ -1576,6 +1581,7 @@ class PDFPageProxy {
|
|||
intent,
|
||||
annotationMode,
|
||||
printAnnotationStorage,
|
||||
isEditing,
|
||||
/* isOpList = */ true
|
||||
);
|
||||
let intentState = this._intentStates.get(intentArgs.cacheKey);
|
||||
|
@ -1812,6 +1818,8 @@ class PDFPageProxy {
|
|||
renderingIntent,
|
||||
cacheKey,
|
||||
annotationStorageSerializable,
|
||||
isEditing,
|
||||
modifiedIds,
|
||||
}) {
|
||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||
assert(
|
||||
|
@ -1828,6 +1836,8 @@ class PDFPageProxy {
|
|||
intent: renderingIntent,
|
||||
cacheKey,
|
||||
annotationStorage: map,
|
||||
isEditing,
|
||||
modifiedIds,
|
||||
},
|
||||
transfer
|
||||
);
|
||||
|
@ -2420,6 +2430,7 @@ class WorkerTransport {
|
|||
intent,
|
||||
annotationMode = AnnotationMode.ENABLE,
|
||||
printAnnotationStorage = null,
|
||||
isEditing = false,
|
||||
isOpList = false
|
||||
) {
|
||||
let renderingIntent = RenderingIntentFlag.DISPLAY; // Default value.
|
||||
|
@ -2438,6 +2449,12 @@ class WorkerTransport {
|
|||
warn(`getRenderingIntent - invalid intent: ${intent}`);
|
||||
}
|
||||
|
||||
const annotationStorage =
|
||||
renderingIntent & RenderingIntentFlag.PRINT &&
|
||||
printAnnotationStorage instanceof PrintAnnotationStorage
|
||||
? printAnnotationStorage
|
||||
: this.annotationStorage;
|
||||
|
||||
switch (annotationMode) {
|
||||
case AnnotationMode.DISABLE:
|
||||
renderingIntent += RenderingIntentFlag.ANNOTATIONS_DISABLE;
|
||||
|
@ -2450,12 +2467,6 @@ class WorkerTransport {
|
|||
case AnnotationMode.ENABLE_STORAGE:
|
||||
renderingIntent += RenderingIntentFlag.ANNOTATIONS_STORAGE;
|
||||
|
||||
const annotationStorage =
|
||||
renderingIntent & RenderingIntentFlag.PRINT &&
|
||||
printAnnotationStorage instanceof PrintAnnotationStorage
|
||||
? printAnnotationStorage
|
||||
: this.annotationStorage;
|
||||
|
||||
annotationStorageSerializable = annotationStorage.serializable;
|
||||
break;
|
||||
default:
|
||||
|
@ -2466,10 +2477,22 @@ class WorkerTransport {
|
|||
renderingIntent += RenderingIntentFlag.OPLIST;
|
||||
}
|
||||
|
||||
const { ids: modifiedIds, hash: modifiedIdsHash } =
|
||||
annotationStorage.modifiedIds;
|
||||
|
||||
const cacheKeyBuf = [
|
||||
renderingIntent,
|
||||
annotationStorageSerializable.hash,
|
||||
isEditing ? 1 : 0,
|
||||
modifiedIdsHash,
|
||||
];
|
||||
|
||||
return {
|
||||
renderingIntent,
|
||||
cacheKey: `${renderingIntent}_${annotationStorageSerializable.hash}`,
|
||||
cacheKey: cacheKeyBuf.join("_"),
|
||||
annotationStorageSerializable,
|
||||
isEditing,
|
||||
modifiedIds,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue