Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[editor] Add some UI elements in order to set font size & color, and ink thickness & color #15039

Merged
merged 1 commit into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions l10n/en-US/viewer.properties
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,9 @@ editor_ink.title=Add Ink Annotation
editor_ink_label=Ink Annotation

freetext_default_content=Enter some text…

# Editor Parameters
editor_free_text_font_color=Font Color
editor_free_text_font_size=Font Size
editor_ink_line_color=Line Color
editor_ink_line_thickness=Line Thickness
2 changes: 1 addition & 1 deletion src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3767,7 +3767,7 @@ class InkAnnotation extends MarkupAnnotation {
}

const appearanceBuffer = [
`${thickness} w`,
`${thickness} w 1 J 1 j`,
`${getPdfColor(color, /* isFill */ false)}`,
];
const buffer = [];
Expand Down
7 changes: 2 additions & 5 deletions src/display/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ import {
Util,
warn,
} from "../shared/util.js";
import { getRGB, PixelsPerInch } from "./display_utils.js";
import {
getShadingPattern,
PathType,
TilingPattern,
} from "./pattern_helper.js";
import { applyMaskImageData } from "../shared/image_utils.js";
import { isNodeJS } from "../shared/is_node.js";
import { PixelsPerInch } from "./display_utils.js";

// <canvas> contexts store most of the state we need natively.
// However, PDF needs a bit more state, which we store here.
Expand Down Expand Up @@ -1326,10 +1326,7 @@ class CanvasGraphics {
// Then for every color in the pdf, if its rounded luminance is the
// same as the background one then it's replaced by the new
// background color else by the foreground one.
const cB = parseInt(defaultBg.slice(1), 16);
const rB = (cB && 0xff0000) >> 16;
const gB = (cB && 0x00ff00) >> 8;
const bB = cB && 0x0000ff;
const [rB, gB, bB] = getRGB(defaultBg);
const newComp = x => {
x /= 255;
return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
Expand Down
23 changes: 23 additions & 0 deletions src/display/display_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,28 @@ function getXfaPageViewport(xfaPage, { scale = 1, rotation = 0 }) {
});
}

function getRGB(color) {
if (color.startsWith("#")) {
const colorRGB = parseInt(color.slice(1), 16);
return [
(colorRGB & 0xff0000) >> 16,
(colorRGB & 0x00ff00) >> 8,
colorRGB & 0x0000ff,
];
}

if (color.startsWith("rgb(")) {
// getComputedStyle(...).color returns a `rgb(R, G, B)` color.
return color
.slice(/* "rgb(".length */ 4, -1) // Strip out "rgb(" and ")".
.split(",")
.map(x => parseInt(x));
}

warn(`Not a valid color format: "${color}"`);
return [0, 0, 0];
}

export {
deprecated,
DOMCanvasFactory,
Expand All @@ -575,6 +597,7 @@ export {
DOMSVGFactory,
getFilenameFromUrl,
getPdfFilenameFromUrl,
getRGB,
getXfaPageViewport,
isDataScheme,
isPdfFile,
Expand Down
42 changes: 26 additions & 16 deletions src/display/editor/annotation_editor_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class AnnotationEditorLayer {
if (!AnnotationEditorLayer._initialized) {
AnnotationEditorLayer._initialized = true;
FreeTextEditor.initialize(options.l10n);

options.uiManager.registerEditorTypes([FreeTextEditor, InkEditor]);
}
this.#uiManager = options.uiManager;
this.annotationStorage = options.annotationStorage;
Expand All @@ -98,14 +100,22 @@ class AnnotationEditorLayer {
* @param {number} mode
*/
updateMode(mode) {
if (mode === AnnotationEditorType.INK) {
// We want to have the ink editor covering all of the page without having
// to click to create it: it must be here when we start to draw.
this.div.addEventListener("mouseover", this.#boundMouseover);
this.div.removeEventListener("click", this.#boundClick);
} else {
this.div.removeEventListener("mouseover", this.#boundMouseover);
switch (mode) {
case AnnotationEditorType.INK:
// We want to have the ink editor covering all of the page without
// having to click to create it: it must be here when we start to draw.
this.div.addEventListener("mouseover", this.#boundMouseover);
this.div.removeEventListener("click", this.#boundClick);
break;
case AnnotationEditorType.FREETEXT:
this.div.removeEventListener("mouseover", this.#boundMouseover);
this.div.addEventListener("click", this.#boundClick);
break;
default:
this.div.removeEventListener("mouseover", this.#boundMouseover);
this.div.removeEventListener("click", this.#boundClick);
}

this.setActiveEditor(null);
}

Expand All @@ -130,13 +140,10 @@ class AnnotationEditorLayer {

/**
* Add some commands into the CommandManager (undo/redo stuff).
* @param {function} cmd
* @param {function} undo
* @param {boolean} mustExec - If true the command is executed after having
* been added.
* @param {Object} params
*/
addCommands(cmd, undo, mustExec) {
this.#uiManager.addCommands(cmd, undo, mustExec);
addCommands(params) {
this.#uiManager.addCommands(params);
}

/**
Expand Down Expand Up @@ -232,7 +239,10 @@ class AnnotationEditorLayer {
this.unselectAll();
this.div.removeEventListener("click", this.#boundClick);
} else {
this.#uiManager.allowClick = false;
// When in Ink mode, setting the editor to null allows the
// user to have to make one click in order to start drawing.
this.#uiManager.allowClick =
this.#uiManager.getMode() === AnnotationEditorType.INK;
this.div.addEventListener("click", this.#boundClick);
}
}
Expand Down Expand Up @@ -332,7 +342,7 @@ class AnnotationEditorLayer {
editor.remove();
};

this.addCommands(cmd, undo, true);
this.addCommands({ cmd, undo, mustExec: true });
}

/**
Expand All @@ -347,7 +357,7 @@ class AnnotationEditorLayer {
editor.remove();
};

this.addCommands(cmd, undo, false);
this.addCommands({ cmd, undo, mustExec: false });
}

/**
Expand Down
15 changes: 15 additions & 0 deletions src/display/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,21 @@ class AnnotationEditor {
this.div.classList.remove("selectedEditor");
}
}

/**
* Update some parameters which have been changed through the UI.
* @param {number} type
* @param {*} value
*/
updateParams(type, value) {}

/**
* Get some properties to update in the UI.
* @returns {Object}
*/
get propertiesToUpdate() {
return {};
}
}

export { AnnotationEditor };
108 changes: 104 additions & 4 deletions src/display/editor/freetext.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
*/

import {
AnnotationEditorParamsType,
AnnotationEditorType,
assert,
LINE_FACTOR,
} from "../../shared/util.js";
import { AnnotationEditor } from "./editor.js";
import { bindEvents } from "./tools.js";
import { getRGB } from "../display_utils.js";

/**
* Basic text editor in order to create a FreeTex annotation.
Expand All @@ -41,10 +43,14 @@ class FreeTextEditor extends AnnotationEditor {

static _internalPadding = 0;

static _defaultFontSize = 10;

static _defaultColor = "CanvasText";

constructor(params) {
super({ ...params, name: "freeTextEditor" });
this.#color = params.color || "CanvasText";
this.#fontSize = params.fontSize || 10;
this.#color = params.color || FreeTextEditor._defaultColor;
this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize;
}

static initialize(l10n) {
Expand Down Expand Up @@ -89,6 +95,94 @@ class FreeTextEditor extends AnnotationEditor {
return editor;
}

static updateDefaultParams(type, value) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want to update the default values, and not just the values for the current instance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure either it's a good idea: we need to know what UX thinks about that.
Anyway it works like this in Acrobat.

switch (type) {
case AnnotationEditorParamsType.FREETEXT_SIZE:
FreeTextEditor._defaultFontSize = value;
break;
case AnnotationEditorParamsType.FREETEXT_COLOR:
FreeTextEditor._defaultColor = value;
break;
}
}

/** @inheritdoc */
updateParams(type, value) {
switch (type) {
case AnnotationEditorParamsType.FREETEXT_SIZE:
this.#updateFontSize(value);
break;
case AnnotationEditorParamsType.FREETEXT_COLOR:
this.#updateColor(value);
break;
}
}

static get defaultPropertiesToUpdate() {
return [
[
AnnotationEditorParamsType.FREETEXT_SIZE,
FreeTextEditor._defaultFontSize,
],
[AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor],
];
}

/** @inheritdoc */
get propertiesToUpdate() {
return [
[AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize],
[AnnotationEditorParamsType.FREETEXT_COLOR, this.#color],
];
}

/**
* Update the font size and make this action as undoable.
* @param {number} fontSize
*/
#updateFontSize(fontSize) {
const setFontsize = size => {
this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`;
this.translate(0, -(size - this.#fontSize) * this.parent.scaleFactor);
this.#fontSize = size;
};
const savedFontsize = this.#fontSize;
this.parent.addCommands({
cmd: () => {
setFontsize(fontSize);
},
undo: () => {
setFontsize(savedFontsize);
},
mustExec: true,
type: AnnotationEditorParamsType.FREETEXT_SIZE,
overwriteIfSameType: true,
keepUndo: true,
});
}

/**
* Update the color and make this action undoable.
* @param {string} color
*/
#updateColor(color) {
const savedColor = this.#color;
this.parent.addCommands({
cmd: () => {
this.#color = color;
this.editorDiv.style.color = color;
},
undo: () => {
this.#color = savedColor;
this.editorDiv.style.color = savedColor;
},
mustExec: true,
type: AnnotationEditorParamsType.FREETEXT_COLOR,
overwriteIfSameType: true,
keepUndo: true,
});
}

/** @inheritdoc */
getInitialTranslation() {
// The start of the base line is where the user clicked.
Expand Down Expand Up @@ -116,13 +210,15 @@ class FreeTextEditor extends AnnotationEditor {
enableEditMode() {
super.enableEditMode();
this.overlayDiv.classList.remove("enabled");
this.editorDiv.contentEditable = true;
this.div.draggable = false;
}

/** @inheritdoc */
disableEditMode() {
super.disableEditMode();
this.overlayDiv.classList.add("enabled");
this.editorDiv.contentEditable = false;
this.div.draggable = true;
}

Expand Down Expand Up @@ -223,7 +319,7 @@ class FreeTextEditor extends AnnotationEditor {
this.editorDiv.contentEditable = true;

const { style } = this.editorDiv;
style.fontSize = `${this.#fontSize}%`;
style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;
style.color = this.#color;

this.div.append(this.editorDiv);
Expand All @@ -248,6 +344,7 @@ class FreeTextEditor extends AnnotationEditor {
);
// eslint-disable-next-line no-unsanitized/property
this.editorDiv.innerHTML = this.#contentHTML;
this.div.draggable = true;
}

return this.div;
Expand All @@ -258,9 +355,12 @@ class FreeTextEditor extends AnnotationEditor {
const padding = FreeTextEditor._internalPadding * this.parent.scaleFactor;
const rect = this.getRect(padding, padding);

// We don't use this.#color directly because it can be CanvasText.
const color = getRGB(getComputedStyle(this.editorDiv).color);

return {
annotationType: AnnotationEditorType.FREETEXT,
color: [0, 0, 0],
color,
fontSize: this.#fontSize,
value: this.#content,
pageIndex: this.parent.pageIndex,
Expand Down
Loading