Skip to content

Commit

Permalink
[editor] Add some UI elements in order to set font size & color, and …
Browse files Browse the repository at this point in the history
…ink thickness & color
  • Loading branch information
calixteman committed Jun 28, 2022
1 parent 4e025e1 commit 1a3ef2a
Show file tree
Hide file tree
Showing 20 changed files with 625 additions and 66 deletions.
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) {
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

0 comments on commit 1a3ef2a

Please sign in to comment.