Skip to content

Commit

Permalink
Support rotating editor layer
Browse files Browse the repository at this point in the history
- As in the annotation layer, use percent instead of pixels as unit;
- handle the rotation of the editor layer in allowing editing when rotation
  angle is not zero;
- the different editors are rotated counterclockwise in order to be usable
  when the main page is itself rotated;
- add support for saving/printing rotated editors.
  • Loading branch information
calixteman committed Jun 23, 2022
1 parent c5dc082 commit 2761f06
Show file tree
Hide file tree
Showing 11 changed files with 456 additions and 143 deletions.
72 changes: 48 additions & 24 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,19 @@ class WidgetAnnotation extends Annotation {
return !!(this.data.fieldFlags & flag);
}

static _getRotationMatrix(rotation, width, height) {
switch (rotation) {
case 90:
return [0, 1, -1, 0, width, 0];
case 180:
return [-1, 0, 0, -1, width, height];
case 270:
return [0, -1, 1, 0, 0, height];
default:
throw new Error("Invalid rotation");
}
}

getRotationMatrix(annotationStorage) {
const storageEntry = annotationStorage
? annotationStorage.get(this.data.id)
Expand All @@ -1539,16 +1552,7 @@ class WidgetAnnotation extends Annotation {
const width = this.data.rect[2] - this.data.rect[0];
const height = this.data.rect[3] - this.data.rect[1];

switch (rotation) {
case 90:
return [0, 1, -1, 0, width, 0];
case 180:
return [-1, 0, 0, -1, width, height];
case 270:
return [0, -1, 1, 0, 0, height];
default:
throw new Error("Invalid rotation");
}
return WidgetAnnotation._getRotationMatrix(rotation, width, height);
}

getBorderAndBackgroundAppearances(annotationStorage) {
Expand Down Expand Up @@ -3199,7 +3203,7 @@ class FreeTextAnnotation extends MarkupAnnotation {
}

static createNewDict(annotation, xref, { apRef, ap }) {
const { color, fontSize, rect, user, value } = annotation;
const { color, fontSize, rect, rotation, user, value } = annotation;
const freetext = new Dict(xref);
freetext.set("Type", Name.get("Annot"));
freetext.set("Subtype", Name.get("FreeText"));
Expand All @@ -3210,7 +3214,7 @@ class FreeTextAnnotation extends MarkupAnnotation {
freetext.set("Contents", value);
freetext.set("F", 4);
freetext.set("Border", [0, 0, 0]);
freetext.set("Rotate", 0);
freetext.set("Rotate", rotation);

if (user) {
freetext.set("T", stringToUTF8String(user));
Expand All @@ -3230,7 +3234,7 @@ class FreeTextAnnotation extends MarkupAnnotation {

static async createNewAppearanceStream(annotation, xref, params) {
const { baseFontRef, evaluator, task } = params;
const { color, fontSize, rect, value } = annotation;
const { color, fontSize, rect, rotation, value } = annotation;

const resources = new Dict(xref);
const font = new Dict(xref);
Expand Down Expand Up @@ -3258,8 +3262,12 @@ class FreeTextAnnotation extends MarkupAnnotation {
);

const [x1, y1, x2, y2] = rect;
const w = x2 - x1;
const h = y2 - y1;
let w = x2 - x1;
let h = y2 - y1;

if (rotation % 180 !== 0) {
[w, h] = [h, w];
}

const lines = value.split("\n");
const scale = fontSize / 1000;
Expand Down Expand Up @@ -3315,6 +3323,11 @@ class FreeTextAnnotation extends MarkupAnnotation {
appearanceStreamDict.set("Length", appearance.length);
appearanceStreamDict.set("Resources", resources);

if (rotation) {
const matrix = WidgetAnnotation._getRotationMatrix(rotation, w, h);
appearanceStreamDict.set("Matrix", matrix);
}

const ap = new StringStream(appearance);
ap.dict = appearanceStreamDict;

Expand Down Expand Up @@ -3683,18 +3696,19 @@ class InkAnnotation extends MarkupAnnotation {
}

static createNewDict(annotation, xref, { apRef, ap }) {
const { paths, rect, rotation } = annotation;
const ink = new Dict(xref);
ink.set("Type", Name.get("Annot"));
ink.set("Subtype", Name.get("Ink"));
ink.set("CreationDate", `D:${getModificationDate()}`);
ink.set("Rect", annotation.rect);
ink.set("Rect", rect);
ink.set(
"InkList",
annotation.paths.map(p => p.points)
paths.map(p => p.points)
);
ink.set("F", 4);
ink.set("Border", [0, 0, 0]);
ink.set("Rotate", 0);
ink.set("Rotate", rotation);

const n = new Dict(xref);
ink.set("AP", n);
Expand All @@ -3709,16 +3723,21 @@ class InkAnnotation extends MarkupAnnotation {
}

static async createNewAppearanceStream(annotation, xref, params) {
const [x1, y1, x2, y2] = annotation.rect;
const w = x2 - x1;
const h = y2 - y1;
const { color, rect, rotation, paths, thickness } = annotation;
const [x1, y1, x2, y2] = rect;
let w = x2 - x1;
let h = y2 - y1;

if (rotation % 180 !== 0) {
[w, h] = [h, w];
}

const appearanceBuffer = [
`${annotation.thickness} w`,
`${getPdfColor(annotation.color, /* isFill */ false)}`,
`${thickness} w`,
`${getPdfColor(color, /* isFill */ false)}`,
];
const buffer = [];
for (const { bezier } of annotation.paths) {
for (const { bezier } of paths) {
buffer.length = 0;
buffer.push(
`${numberToString(bezier[0])} ${numberToString(bezier[1])} m`
Expand All @@ -3742,6 +3761,11 @@ class InkAnnotation extends MarkupAnnotation {
appearanceStreamDict.set("BBox", [0, 0, w, h]);
appearanceStreamDict.set("Length", appearance.length);

if (rotation) {
const matrix = WidgetAnnotation._getRotationMatrix(rotation, w, h);
appearanceStreamDict.set("Matrix", matrix);
}

const ap = new StringStream(appearance);
ap.dict = appearanceStreamDict;

Expand Down
1 change: 0 additions & 1 deletion src/core/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,6 @@ class Page {
...newData.annotations
);

this.xref.resetNewRef();
return objects;
}

Expand Down
1 change: 1 addition & 0 deletions src/display/annotation_storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class AnnotationStorage {
const val = value instanceof AnnotationEditor ? value.serialize() : value;
clone.set(key, val);
}

return clone;
}

Expand Down
64 changes: 46 additions & 18 deletions src/display/editor/annotation_editor_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
/** @typedef {import("../annotation_storage.js").AnnotationStorage} AnnotationStorage */
/** @typedef {import("../../web/interfaces").IL10n} IL10n */

import { AnnotationEditorType, Util } from "../../shared/util.js";
import { bindEvents, KeyboardManager } from "./tools.js";
import { AnnotationEditorType } from "../../shared/util.js";
import { FreeTextEditor } from "./freetext.js";
import { InkEditor } from "./ink.js";

Expand Down Expand Up @@ -106,6 +106,7 @@ class AnnotationEditorLayer {
} else {
this.div.removeEventListener("mouseover", this.#boundMouseover);
}
this.setActiveEditor(null);
}

/**
Expand Down Expand Up @@ -273,6 +274,11 @@ class AnnotationEditorLayer {
if (editor.parent === this) {
return;
}

if (this.#uiManager.isActive(editor)) {
editor.parent.setActiveEditor(null);
}

this.attach(editor);
editor.pageIndex = this.pageIndex;
editor.parent.detach(editor);
Expand Down Expand Up @@ -419,10 +425,10 @@ class AnnotationEditorLayer {
this.#changeParent(editor);

const rect = this.div.getBoundingClientRect();
editor.setAt(
event.clientX - rect.x - editor.mouseX,
event.clientY - rect.y - editor.mouseY
);
const endX = event.clientX - rect.x;
const endY = event.clientY - rect.y;

editor.translate(endX - editor.startX, endY - editor.startY);
}

/**
Expand Down Expand Up @@ -463,29 +469,19 @@ class AnnotationEditorLayer {
*/
render(parameters) {
this.viewport = parameters.viewport;
this.inverseViewportTransform = Util.inverseTransform(
this.viewport.transform
);
bindEvents(this, this.div, ["dragover", "drop", "keydown"]);
this.div.addEventListener("click", this.#boundClick);
this.setDimensions();
}

/**
* Update the main editor.
* @param {Object} parameters
*/
update(parameters) {
const transform = Util.transform(
parameters.viewport.transform,
this.inverseViewportTransform
);
this.setActiveEditor(null);
this.viewport = parameters.viewport;
this.inverseViewportTransform = Util.inverseTransform(
this.viewport.transform
);
for (const editor of this.#editors.values()) {
editor.transform(transform);
}
this.setDimensions();
}

/**
Expand All @@ -495,6 +491,38 @@ class AnnotationEditorLayer {
get scaleFactor() {
return this.viewport.scale;
}

/**
* Get page dimensions.
* @returns {Object} dimensions.
*/
get pageDimensions() {
const [pageLLx, pageLLy, pageURx, pageURy] = this.viewport.viewBox;
const width = pageURx - pageLLx;
const height = pageURy - pageLLy;

return [width, height];
}

get viewportBaseDimensions() {
const { width, height, rotation } = this.viewport;
return rotation % 180 === 0 ? [width, height] : [height, width];
}

/**
* Set the dimensions of the main div.
*/
setDimensions() {
const { width, height, rotation } = this.viewport;

const flipOrientation = rotation % 180 !== 0,
widthStr = Math.floor(width) + "px",
heightStr = Math.floor(height) + "px";

this.div.style.width = flipOrientation ? heightStr : widthStr;
this.div.style.height = flipOrientation ? widthStr : heightStr;
this.div.setAttribute("data-annotation-rotation", rotation);
}
}

export { AnnotationEditorLayer };
Loading

0 comments on commit 2761f06

Please sign in to comment.