Skip to content

Commit

Permalink
[Editor] Add the possibility to change the thickness of a free highli…
Browse files Browse the repository at this point in the history
…ght (bug 1876096)
  • Loading branch information
calixteman committed Jan 24, 2024
1 parent ae62787 commit 5ce7f40
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 56 deletions.
2 changes: 2 additions & 0 deletions l10n/en-US/viewer.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ pdfjs-editor-ink-opacity-input = Opacity
pdfjs-editor-stamp-add-image-button =
.title = Add image
pdfjs-editor-stamp-add-image-button-label = Add image
# This refers to the thickness of the line used for free highlighting (not bound to text)
pdfjs-editor-free-highlight-thickness-input = Thickness
pdfjs-free-text =
.aria-label = Text Editor
Expand Down
8 changes: 8 additions & 0 deletions src/display/draw_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ class DrawLayer {
path.setAttribute("d", line.toSVGPath());
}

updateLine(id, line) {
const root = this.#mapping.get(id);
const defs = root.firstChild;
const path = defs.firstChild;
this.updateBox(id, line.box);
path.setAttribute("d", line.toSVGPath());
}

removeFreeHighlight(id) {
this.remove(id);
this.#toUpdate.delete(id);
Expand Down
6 changes: 5 additions & 1 deletion src/display/editor/annotation_editor_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,11 @@ class AnnotationEditorLayer {
// Do nothing on right click.
return;
}
HighlightEditor.startHighlighting(this, event);
HighlightEditor.startHighlighting(
this,
this.#uiManager.direction === "ltr",
event
);
event.preventDefault();
}
}
Expand Down
13 changes: 10 additions & 3 deletions src/display/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ class AnnotationEditor {
// the position: it'll be done when the user will release the mouse button.

let { x, y } = this;
const [bx, by] = this.#getBaseTranslation();
const [bx, by] = this.getBaseTranslation();
x += bx;
y += by;

Expand All @@ -481,7 +481,14 @@ class AnnotationEditor {
this.div.scrollIntoView({ block: "nearest" });
}

#getBaseTranslation() {
/**
* Get the translation to take into account the editor border.
* The CSS engine positions the element by taking the border into account so
* we must apply the opposite translation to have the editor in the right
* position.
* @returns {Array<number>}
*/
getBaseTranslation() {
const [parentWidth, parentHeight] = this.parentDimensions;
const { _borderLineWidth } = AnnotationEditor;
const x = _borderLineWidth / parentWidth;
Expand Down Expand Up @@ -532,7 +539,7 @@ class AnnotationEditor {
this.x = x /= pageWidth;
this.y = y /= pageHeight;

const [bx, by] = this.#getBaseTranslation();
const [bx, by] = this.getBaseTranslation();
x += bx;
y += by;

Expand Down
112 changes: 87 additions & 25 deletions src/display/editor/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ class HighlightEditor extends AnnotationEditor {

#outlineId = null;

#thickness;

static _defaultColor = null;

static _defaultOpacity = 1;

static _defaultThickness = 10;
static _defaultThickness = 12;

static _l10nPromise;

Expand All @@ -71,6 +73,7 @@ class HighlightEditor extends AnnotationEditor {
constructor(params) {
super({ ...params, name: "highlightEditor" });
this.color = params.color || HighlightEditor._defaultColor;
this.#thickness = params.thickness || HighlightEditor._defaultThickness;
this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
this.#boxes = params.boxes || null;
this._isDraggable = false;
Expand Down Expand Up @@ -112,17 +115,31 @@ class HighlightEditor extends AnnotationEditor {
];
}

#createFreeOutlines({ highlight, highlightId, clipPathId }) {
this.#highlightOutlines = highlight.getOutlines(
this._uiManager.direction === "ltr"
#createFreeOutlines({ highlightOutlines, highlightId, clipPathId }) {
this.#highlightOutlines = highlightOutlines;
const extraThickness = 1.5;
this.#focusOutlines = highlightOutlines.getNewOutline(
/* Slightly bigger than the highlight in order to have a little
space between the highlight and the outline. */
this.#thickness / 2 + extraThickness,
/* innerMargin = */ 0.0025
);
this.#id = highlightId;
this.#clipPathId = clipPathId;
const { x, y, width, height, lastPoint } = this.#highlightOutlines.box;

// We need to redraw the highlight because we change the coordinates to be
// in the box coordinate system.
this.parent.drawLayer.finalizeLine(this.#id, this.#highlightOutlines);
if (highlightId >= 0) {
this.#id = highlightId;
this.#clipPathId = clipPathId;
// We need to redraw the highlight because we change the coordinates to be
// in the box coordinate system.
this.parent.drawLayer.finalizeLine(highlightId, highlightOutlines);
this.#outlineId = this.parent.drawLayer.highlightOutline(
this.#focusOutlines
);
} else if (this.parent) {
this.parent.drawLayer.updateLine(this.#id, highlightOutlines);
this.parent.drawLayer.updateLine(this.#outlineId, this.#focusOutlines);
}
const { x, y, width, height, lastPoint } = highlightOutlines.box;
this.#lastPoint = lastPoint;
switch (this.rotation) {
case 0:
this.x = x;
Expand Down Expand Up @@ -153,30 +170,24 @@ class HighlightEditor extends AnnotationEditor {
break;
}
}

const innerMargin = 1.5;
this.#focusOutlines = highlight.getFocusOutline(
/* Slightly bigger than the highlight in order to have a little
space between the highlight and the outline. */
HighlightEditor._defaultThickness + innerMargin
);
this.#outlineId = this.parent.drawLayer.highlightOutline(
this.#focusOutlines
);
this.#lastPoint = lastPoint;
}

/** @inheritdoc */
static initialize(l10n, uiManager) {
AnnotationEditor.initialize(l10n, uiManager);
HighlightEditor._defaultColor ||=
uiManager.highlightColors?.values().next().value || "#fff066";
}

/** @inheritdoc */
static updateDefaultParams(type, value) {
switch (type) {
case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR:
HighlightEditor._defaultColor = value;
break;
case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
HighlightEditor._defaultThickness = value;
break;
}
}

Expand All @@ -194,6 +205,9 @@ class HighlightEditor extends AnnotationEditor {
case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
this.#updateColor(value);
break;
case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
this.#updateThickness(value);
break;
}
}

Expand All @@ -203,6 +217,10 @@ class HighlightEditor extends AnnotationEditor {
AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR,
HighlightEditor._defaultColor,
],
[
AnnotationEditorParamsType.HIGHLIGHT_THICKNESS,
HighlightEditor._defaultThickness,
],
];
}

Expand All @@ -213,6 +231,10 @@ class HighlightEditor extends AnnotationEditor {
AnnotationEditorParamsType.HIGHLIGHT_COLOR,
this.color || HighlightEditor._defaultColor,
],
[
AnnotationEditorParamsType.HIGHLIGHT_THICKNESS,
this.#thickness || HighlightEditor._defaultThickness,
],
];
}

Expand Down Expand Up @@ -240,6 +262,26 @@ class HighlightEditor extends AnnotationEditor {
});
}

/**
* Update the thickness and make this action undoable.
* @param {number} thickness
*/
#updateThickness(thickness) {
const savedThickness = this.#thickness;
const setThickness = th => {
this.#thickness = th;
this.#changeThickness(th);
};
this.addCommands({
cmd: setThickness.bind(this, thickness),
undo: setThickness.bind(this, savedThickness),
mustExec: true,
type: AnnotationEditorParamsType.INK_THICKNESS,
overwriteIfSameType: true,
keepUndo: true,
});
}

/** @inheritdoc */
async addEditToolbar() {
const toolbar = await super.addEditToolbar();
Expand Down Expand Up @@ -270,6 +312,13 @@ class HighlightEditor extends AnnotationEditor {
return super.fixAndSetPosition(this.#getRotation());
}

/** @inheritdoc */
getBaseTranslation() {
// The editor itself doesn't have any CSS border (we're drawing one
// ourselves in using SVG).
return [0, 0];
}

/** @inheritdoc */
getRect(tx, ty) {
return super.getRect(tx, ty, this.#getRotation());
Expand Down Expand Up @@ -324,6 +373,18 @@ class HighlightEditor extends AnnotationEditor {
}
}

#changeThickness(thickness) {
if (!this.#isFreeHighlight) {
return;
}
this.#createFreeOutlines({
highlightOutlines: this.#highlightOutlines.getNewOutline(thickness / 2),
});
this.fixAndSetPosition();
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(this.width * parentWidth, this.height * parentHeight);
}

#cleanDrawLayer() {
if (this.#id === null || !this.parent) {
return;
Expand Down Expand Up @@ -482,7 +543,7 @@ class HighlightEditor extends AnnotationEditor {
return this.#highlightOutlines.serialize(rect, this.#getRotation());
}

static startHighlighting(parent, { target: textLayer, x, y }) {
static startHighlighting(parent, isLTR, { target: textLayer, x, y }) {
const {
x: layerX,
y: layerY,
Expand Down Expand Up @@ -520,7 +581,8 @@ class HighlightEditor extends AnnotationEditor {
{ x, y },
[layerX, layerY, parentWidth, parentHeight],
parent.scale,
this._defaultThickness,
this._defaultThickness / 2,
isLTR,
/* innerMargin = */ 0.001
);
({ id: this._freeHighlightId, clipPathId: this._freeHighlightClipId } =
Expand All @@ -543,7 +605,7 @@ class HighlightEditor extends AnnotationEditor {
if (!this._freeHighlight.isEmpty()) {
parent.createAndAddNewEditor(event, false, {
highlightId: this._freeHighlightId,
highlight: this._freeHighlight,
highlightOutlines: this._freeHighlight.getOutlines(),
clipPathId: this._freeHighlightClipId,
});
} else {
Expand Down Expand Up @@ -597,7 +659,7 @@ class HighlightEditor extends AnnotationEditor {
annotationType: AnnotationEditorType.HIGHLIGHT,
color,
opacity: this.#opacity,
thickness: 2 * HighlightEditor._defaultThickness,
thickness: this.#thickness,
quadPoints: this.#serializeBoxes(rect),
outlines: this.#serializeOutlines(rect),
pageIndex: this.pageIndex,
Expand Down
Loading

0 comments on commit 5ce7f40

Please sign in to comment.