[Editor] Add a color picker with predefined colors for highlighting text (bug 1866434)
The doorhanger for highlighting has a basic color picker composed of 5 predefined colors to set the default color to use. These colors can be changed thanks to a preference for now but it's something which could be changed in the Firefox settings in the future. Each highlight has in its own toolbar a color picker to just change its color. The different color pickers are so similar (modulo few differences in their styles) that this patch introduces a new class ColorPicker which provides a color picker component which could be reused in future editors. All in all, a large part of this patch is dedicated to color picker itself and its style and the rest is almost a matter of wiring the component.
This commit is contained in:
parent
c0436013a0
commit
ff23d37fa2
@ -85,6 +85,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"highlightEditorColors": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
|
||||||
|
},
|
||||||
"disableRange": {
|
"disableRange": {
|
||||||
"title": "Disable range requests",
|
"title": "Disable range requests",
|
||||||
"description": "Whether to disable range requests (not recommended).",
|
"description": "Whether to disable range requests (not recommended).",
|
||||||
|
@ -1079,6 +1079,7 @@ function buildComponents(defines, dir) {
|
|||||||
"web/images/loading-icon.gif",
|
"web/images/loading-icon.gif",
|
||||||
"web/images/altText_*.svg",
|
"web/images/altText_*.svg",
|
||||||
"web/images/editor-toolbar-*.svg",
|
"web/images/editor-toolbar-*.svg",
|
||||||
|
"web/images/toolbarButton-menuArrow.svg",
|
||||||
];
|
];
|
||||||
|
|
||||||
return merge([
|
return merge([
|
||||||
|
@ -332,6 +332,8 @@ pdfjs-editor-remove-freetext-button =
|
|||||||
.title = Remove text
|
.title = Remove text
|
||||||
pdfjs-editor-remove-stamp-button =
|
pdfjs-editor-remove-stamp-button =
|
||||||
.title = Remove image
|
.title = Remove image
|
||||||
|
pdfjs-editor-remove-highlight-button =
|
||||||
|
.title = Remove highlight
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
@ -384,3 +386,23 @@ pdfjs-editor-resizer-label-bottom-right = Bottom right corner — resize
|
|||||||
pdfjs-editor-resizer-label-bottom-middle = Bottom middle — resize
|
pdfjs-editor-resizer-label-bottom-middle = Bottom middle — resize
|
||||||
pdfjs-editor-resizer-label-bottom-left = Bottom left corner — resize
|
pdfjs-editor-resizer-label-bottom-left = Bottom left corner — resize
|
||||||
pdfjs-editor-resizer-label-middle-left = Middle left — resize
|
pdfjs-editor-resizer-label-middle-left = Middle left — resize
|
||||||
|
|
||||||
|
## Color picker
|
||||||
|
|
||||||
|
# This means "Color used to highlight text"
|
||||||
|
pdfjs-editor-highlight-colorpicker-label = Highlight color
|
||||||
|
|
||||||
|
pdfjs-editor-colorpicker-button =
|
||||||
|
.title = Change color
|
||||||
|
pdfjs-editor-colorpicker-dropdown =
|
||||||
|
.aria-label = Color choices
|
||||||
|
pdfjs-editor-colorpicker-yellow =
|
||||||
|
.title = Yellow
|
||||||
|
pdfjs-editor-colorpicker-green =
|
||||||
|
.title = Green
|
||||||
|
pdfjs-editor-colorpicker-blue =
|
||||||
|
.title = Blue
|
||||||
|
pdfjs-editor-colorpicker-pink =
|
||||||
|
.title = Pink
|
||||||
|
pdfjs-editor-colorpicker-red =
|
||||||
|
.title = Red
|
||||||
|
230
src/display/editor/color_picker.js
Normal file
230
src/display/editor/color_picker.js
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/* Copyright 2023 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AnnotationEditorParamsType, shadow } from "../../shared/util.js";
|
||||||
|
import { KeyboardManager } from "./tools.js";
|
||||||
|
import { noContextMenu } from "../display_utils.js";
|
||||||
|
|
||||||
|
class ColorPicker {
|
||||||
|
#boundKeyDown = this.#keyDown.bind(this);
|
||||||
|
|
||||||
|
#button = null;
|
||||||
|
|
||||||
|
#buttonSwatch = null;
|
||||||
|
|
||||||
|
#defaultColor;
|
||||||
|
|
||||||
|
#dropdown = null;
|
||||||
|
|
||||||
|
#dropdownWasFromKeyboard = false;
|
||||||
|
|
||||||
|
#isMainColorPicker = false;
|
||||||
|
|
||||||
|
#eventBus;
|
||||||
|
|
||||||
|
#uiManager = null;
|
||||||
|
|
||||||
|
static get _keyboardManager() {
|
||||||
|
return shadow(
|
||||||
|
this,
|
||||||
|
"_keyboardManager",
|
||||||
|
new KeyboardManager([
|
||||||
|
[
|
||||||
|
["Escape", "mac+Escape"],
|
||||||
|
ColorPicker.prototype._hideDropdownFromKeyboard,
|
||||||
|
],
|
||||||
|
[[" ", "mac+ "], ColorPicker.prototype._colorSelectFromKeyboard],
|
||||||
|
[
|
||||||
|
["ArrowDown", "ArrowRight", "mac+ArrowDown", "mac+ArrowRight"],
|
||||||
|
ColorPicker.prototype._moveToNext,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["ArrowUp", "ArrowLeft", "mac+ArrowUp", "mac+ArrowLeft"],
|
||||||
|
ColorPicker.prototype._moveToPrevious,
|
||||||
|
],
|
||||||
|
[["Home", "mac+Home"], ColorPicker.prototype._moveToBeginning],
|
||||||
|
[["End", "mac+End"], ColorPicker.prototype._moveToEnd],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor({ editor = null, uiManager = null }) {
|
||||||
|
this.#isMainColorPicker = !editor;
|
||||||
|
this.#uiManager = editor?._uiManager || uiManager;
|
||||||
|
this.#eventBus = this.#uiManager._eventBus;
|
||||||
|
this.#defaultColor =
|
||||||
|
editor?.color ||
|
||||||
|
this.#uiManager?.highlightColors.values().next().value ||
|
||||||
|
"#FFFF98";
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButton() {
|
||||||
|
const button = (this.#button = document.createElement("button"));
|
||||||
|
button.className = "colorPicker";
|
||||||
|
button.tabIndex = "0";
|
||||||
|
button.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-button");
|
||||||
|
button.setAttribute("aria-haspopup", true);
|
||||||
|
button.addEventListener("click", this.#openDropdown.bind(this));
|
||||||
|
const swatch = (this.#buttonSwatch = document.createElement("span"));
|
||||||
|
swatch.className = "swatch";
|
||||||
|
swatch.style.backgroundColor = this.#defaultColor;
|
||||||
|
button.append(swatch);
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMainDropdown() {
|
||||||
|
const dropdown = (this.#dropdown = this.#getDropdownRoot(
|
||||||
|
AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR
|
||||||
|
));
|
||||||
|
dropdown.setAttribute("aria-orientation", "horizontal");
|
||||||
|
dropdown.setAttribute("aria-labelledby", "highlightColorPickerLabel");
|
||||||
|
|
||||||
|
return dropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getDropdownRoot(paramType) {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.addEventListener("contextmenu", noContextMenu);
|
||||||
|
div.className = "dropdown";
|
||||||
|
div.role = "listbox";
|
||||||
|
div.setAttribute("aria-multiselectable", false);
|
||||||
|
div.setAttribute("aria-orientation", "vertical");
|
||||||
|
div.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-dropdown");
|
||||||
|
for (const [name, color] of this.#uiManager.highlightColors) {
|
||||||
|
const button = document.createElement("button");
|
||||||
|
button.tabIndex = "0";
|
||||||
|
button.role = "option";
|
||||||
|
button.setAttribute("data-color", color);
|
||||||
|
button.title = name;
|
||||||
|
button.setAttribute("data-l10n-id", `pdfjs-editor-colorpicker-${name}`);
|
||||||
|
const swatch = document.createElement("span");
|
||||||
|
button.append(swatch);
|
||||||
|
swatch.className = "swatch";
|
||||||
|
swatch.style.backgroundColor = color;
|
||||||
|
button.setAttribute("aria-selected", color === this.#defaultColor);
|
||||||
|
button.addEventListener(
|
||||||
|
"click",
|
||||||
|
this.#colorSelect.bind(this, paramType, color)
|
||||||
|
);
|
||||||
|
div.append(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.addEventListener("keydown", this.#boundKeyDown);
|
||||||
|
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
#colorSelect(type, color, event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.#eventBus.dispatch("switchannotationeditorparams", {
|
||||||
|
source: this,
|
||||||
|
type,
|
||||||
|
value: color,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_colorSelectFromKeyboard(event) {
|
||||||
|
const color = event.target.getAttribute("data-color");
|
||||||
|
if (!color) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#colorSelect(color, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
_moveToNext(event) {
|
||||||
|
if (event.target === this.#button) {
|
||||||
|
this.#dropdown.firstChild?.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.target.nextSibling?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
_moveToPrevious(event) {
|
||||||
|
event.target.previousSibling?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
_moveToBeginning() {
|
||||||
|
this.#dropdown.firstChild?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
_moveToEnd() {
|
||||||
|
this.#dropdown.lastChild?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
#keyDown(event) {
|
||||||
|
ColorPicker._keyboardManager.exec(this, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#openDropdown(event) {
|
||||||
|
if (this.#dropdown && !this.#dropdown.classList.contains("hidden")) {
|
||||||
|
this.hideDropdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#button.addEventListener("keydown", this.#boundKeyDown);
|
||||||
|
this.#dropdownWasFromKeyboard = event.detail === 0;
|
||||||
|
if (this.#dropdown) {
|
||||||
|
this.#dropdown.classList.remove("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const root = (this.#dropdown = this.#getDropdownRoot(
|
||||||
|
AnnotationEditorParamsType.HIGHLIGHT_COLOR
|
||||||
|
));
|
||||||
|
this.#button.append(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideDropdown() {
|
||||||
|
this.#dropdown?.classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
_hideDropdownFromKeyboard() {
|
||||||
|
if (
|
||||||
|
this.#isMainColorPicker ||
|
||||||
|
!this.#dropdown ||
|
||||||
|
this.#dropdown.classList.contains("hidden")
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hideDropdown();
|
||||||
|
this.#button.removeEventListener("keydown", this.#boundKeyDown);
|
||||||
|
this.#button.focus({
|
||||||
|
preventScroll: true,
|
||||||
|
focusVisible: this.#dropdownWasFromKeyboard,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateColor(color) {
|
||||||
|
if (this.#buttonSwatch) {
|
||||||
|
this.#buttonSwatch.style.backgroundColor = color;
|
||||||
|
}
|
||||||
|
if (!this.#dropdown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const i = this.#uiManager.highlightColors.values();
|
||||||
|
for (const child of this.#dropdown.children) {
|
||||||
|
child.setAttribute("aria-selected", i.next().value === color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.#button?.remove();
|
||||||
|
this.#button = null;
|
||||||
|
this.#buttonSwatch = null;
|
||||||
|
this.#dropdown?.remove();
|
||||||
|
this.#dropdown = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ColorPicker };
|
@ -903,15 +903,21 @@ class AnnotationEditor {
|
|||||||
this.#altText?.finish();
|
this.#altText?.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a toolbar for this editor.
|
||||||
|
* @returns {Promise<EditorToolbar|null>}
|
||||||
|
*/
|
||||||
async addEditToolbar() {
|
async addEditToolbar() {
|
||||||
if (this.#editToolbar || this.#isInEditMode) {
|
if (this.#editToolbar || this.#isInEditMode) {
|
||||||
return;
|
return this.#editToolbar;
|
||||||
}
|
}
|
||||||
this.#editToolbar = new EditorToolbar(this);
|
this.#editToolbar = new EditorToolbar(this);
|
||||||
this.div.append(this.#editToolbar.render());
|
this.div.append(this.#editToolbar.render());
|
||||||
if (this.#altText) {
|
if (this.#altText) {
|
||||||
this.#editToolbar.addAltTextButton(await this.#altText.render());
|
this.#editToolbar.addAltTextButton(await this.#altText.render());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.#editToolbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeEditToolbar() {
|
removeEditToolbar() {
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
} from "../../shared/util.js";
|
} from "../../shared/util.js";
|
||||||
import { AnnotationEditor } from "./editor.js";
|
import { AnnotationEditor } from "./editor.js";
|
||||||
import { bindEvents } from "./tools.js";
|
import { bindEvents } from "./tools.js";
|
||||||
|
import { ColorPicker } from "./color_picker.js";
|
||||||
import { Outliner } from "./outliner.js";
|
import { Outliner } from "./outliner.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +31,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
#clipPathId = null;
|
#clipPathId = null;
|
||||||
|
|
||||||
#color;
|
#colorPicker = null;
|
||||||
|
|
||||||
#focusOutlines = null;
|
#focusOutlines = null;
|
||||||
|
|
||||||
@ -46,9 +47,9 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
#outlineId = null;
|
#outlineId = null;
|
||||||
|
|
||||||
static _defaultColor = "#FFF066";
|
static _defaultColor = null;
|
||||||
|
|
||||||
static _defaultOpacity = 0.4;
|
static _defaultOpacity = 1;
|
||||||
|
|
||||||
static _l10nPromise;
|
static _l10nPromise;
|
||||||
|
|
||||||
@ -58,7 +59,9 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
super({ ...params, name: "highlightEditor" });
|
super({ ...params, name: "highlightEditor" });
|
||||||
this.#color = params.color || HighlightEditor._defaultColor;
|
HighlightEditor._defaultColor ||=
|
||||||
|
this._uiManager.highlightColors?.values().next().value || "#fff066";
|
||||||
|
this.color = params.color || HighlightEditor._defaultColor;
|
||||||
this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
|
this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
|
||||||
this.#boxes = params.boxes || null;
|
this.#boxes = params.boxes || null;
|
||||||
this._isDraggable = false;
|
this._isDraggable = false;
|
||||||
@ -100,12 +103,9 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
static updateDefaultParams(type, value) {
|
static updateDefaultParams(type, value) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
|
case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR:
|
||||||
HighlightEditor._defaultColor = value;
|
HighlightEditor._defaultColor = value;
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorParamsType.HIGHLIGHT_OPACITY:
|
|
||||||
HighlightEditor._defaultOpacity = value / 100;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,22 +120,15 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
|
case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
|
||||||
this.#updateColor(value);
|
this.#updateColor(value);
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorParamsType.HIGHLIGHT_OPACITY:
|
|
||||||
this.#updateOpacity(value);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get defaultPropertiesToUpdate() {
|
static get defaultPropertiesToUpdate() {
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
AnnotationEditorParamsType.HIGHLIGHT_COLOR,
|
AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR,
|
||||||
HighlightEditor._defaultColor,
|
HighlightEditor._defaultColor,
|
||||||
],
|
],
|
||||||
[
|
|
||||||
AnnotationEditorParamsType.HIGHLIGHT_OPACITY,
|
|
||||||
Math.round(HighlightEditor._defaultOpacity * 100),
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,11 +137,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
AnnotationEditorParamsType.HIGHLIGHT_COLOR,
|
AnnotationEditorParamsType.HIGHLIGHT_COLOR,
|
||||||
this.#color || HighlightEditor._defaultColor,
|
this.color || HighlightEditor._defaultColor,
|
||||||
],
|
|
||||||
[
|
|
||||||
AnnotationEditorParamsType.HIGHLIGHT_OPACITY,
|
|
||||||
Math.round(100 * (this.#opacity ?? HighlightEditor._defaultOpacity)),
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -161,12 +150,14 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
const savedColor = this.color;
|
const savedColor = this.color;
|
||||||
this.addCommands({
|
this.addCommands({
|
||||||
cmd: () => {
|
cmd: () => {
|
||||||
this.#color = color;
|
this.color = color;
|
||||||
this.parent.drawLayer.changeColor(this.#id, color);
|
this.parent.drawLayer.changeColor(this.#id, color);
|
||||||
|
this.#colorPicker?.updateColor(color);
|
||||||
},
|
},
|
||||||
undo: () => {
|
undo: () => {
|
||||||
this.#color = savedColor;
|
this.color = savedColor;
|
||||||
this.parent.drawLayer.changeColor(this.#id, savedColor);
|
this.parent.drawLayer.changeColor(this.#id, savedColor);
|
||||||
|
this.#colorPicker?.updateColor(savedColor);
|
||||||
},
|
},
|
||||||
mustExec: true,
|
mustExec: true,
|
||||||
type: AnnotationEditorParamsType.HIGHLIGHT_COLOR,
|
type: AnnotationEditorParamsType.HIGHLIGHT_COLOR,
|
||||||
@ -175,27 +166,17 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritdoc */
|
||||||
* Update the opacity and make this action undoable.
|
async addEditToolbar() {
|
||||||
* @param {number} opacity
|
const toolbar = await super.addEditToolbar();
|
||||||
*/
|
if (!toolbar) {
|
||||||
#updateOpacity(opacity) {
|
return null;
|
||||||
opacity /= 100;
|
}
|
||||||
const savedOpacity = this.#opacity;
|
if (this._uiManager.highlightColors) {
|
||||||
this.addCommands({
|
this.#colorPicker = new ColorPicker({ editor: this });
|
||||||
cmd: () => {
|
toolbar.addColorPicker(this.#colorPicker);
|
||||||
this.#opacity = opacity;
|
}
|
||||||
this.parent.drawLayer.changeOpacity(this.#id, opacity);
|
return toolbar;
|
||||||
},
|
|
||||||
undo: () => {
|
|
||||||
this.#opacity = savedOpacity;
|
|
||||||
this.parent.drawLayer.changeOpacity(this.#id, savedOpacity);
|
|
||||||
},
|
|
||||||
mustExec: true,
|
|
||||||
type: AnnotationEditorParamsType.HIGHLIGHT_OPACITY,
|
|
||||||
overwriteIfSameType: true,
|
|
||||||
keepUndo: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
@ -277,7 +258,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
({ id: this.#id, clipPathId: this.#clipPathId } =
|
({ id: this.#id, clipPathId: this.#clipPathId } =
|
||||||
parent.drawLayer.highlight(
|
parent.drawLayer.highlight(
|
||||||
this.#highlightOutlines,
|
this.#highlightOutlines,
|
||||||
this.#color,
|
this.color,
|
||||||
this.#opacity
|
this.#opacity
|
||||||
));
|
));
|
||||||
if (this.#highlightDiv) {
|
if (this.#highlightDiv) {
|
||||||
@ -415,7 +396,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
const editor = super.deserialize(data, parent, uiManager);
|
const editor = super.deserialize(data, parent, uiManager);
|
||||||
|
|
||||||
const { rect, color, quadPoints } = data;
|
const { rect, color, quadPoints } = data;
|
||||||
editor.#color = Util.makeHexColor(...color);
|
editor.color = Util.makeHexColor(...color);
|
||||||
editor.#opacity = data.opacity;
|
editor.#opacity = data.opacity;
|
||||||
|
|
||||||
const [pageWidth, pageHeight] = editor.pageDimensions;
|
const [pageWidth, pageHeight] = editor.pageDimensions;
|
||||||
@ -443,7 +424,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const rect = this.getRect(0, 0);
|
const rect = this.getRect(0, 0);
|
||||||
const color = AnnotationEditor._colorManager.convert(this.#color);
|
const color = AnnotationEditor._colorManager.convert(this.color);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
annotationType: AnnotationEditorType.HIGHLIGHT,
|
annotationType: AnnotationEditorType.HIGHLIGHT,
|
||||||
|
@ -18,6 +18,8 @@ import { noContextMenu } from "../display_utils.js";
|
|||||||
class EditorToolbar {
|
class EditorToolbar {
|
||||||
#toolbar = null;
|
#toolbar = null;
|
||||||
|
|
||||||
|
#colorPicker = null;
|
||||||
|
|
||||||
#editor;
|
#editor;
|
||||||
|
|
||||||
#buttons = null;
|
#buttons = null;
|
||||||
@ -85,6 +87,7 @@ class EditorToolbar {
|
|||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.#toolbar.classList.add("hidden");
|
this.#toolbar.classList.add("hidden");
|
||||||
|
this.#colorPicker?.hideDropdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
@ -106,19 +109,28 @@ class EditorToolbar {
|
|||||||
this.#buttons.append(button);
|
this.#buttons.append(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
addAltTextButton(button) {
|
|
||||||
this.#addListenersToElement(button);
|
|
||||||
this.#buttons.prepend(button, this.#divider);
|
|
||||||
}
|
|
||||||
|
|
||||||
get #divider() {
|
get #divider() {
|
||||||
const divider = document.createElement("div");
|
const divider = document.createElement("div");
|
||||||
divider.className = "divider";
|
divider.className = "divider";
|
||||||
return divider;
|
return divider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addAltTextButton(button) {
|
||||||
|
this.#addListenersToElement(button);
|
||||||
|
this.#buttons.prepend(button, this.#divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
addColorPicker(colorPicker) {
|
||||||
|
this.#colorPicker = colorPicker;
|
||||||
|
const button = colorPicker.renderButton();
|
||||||
|
this.#addListenersToElement(button);
|
||||||
|
this.#buttons.prepend(button, this.#divider);
|
||||||
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
this.#toolbar.remove();
|
this.#toolbar.remove();
|
||||||
|
this.#colorPicker?.destroy();
|
||||||
|
this.#colorPicker = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +438,7 @@ class KeyboardManager {
|
|||||||
if (checker && !checker(self, event)) {
|
if (checker && !checker(self, event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
callback.bind(self, ...args)();
|
callback.bind(self, ...args, event)();
|
||||||
|
|
||||||
// For example, ctrl+s in a FreeText must be handled by the viewer, hence
|
// For example, ctrl+s in a FreeText must be handled by the viewer, hence
|
||||||
// the event must bubble.
|
// the event must bubble.
|
||||||
@ -545,6 +545,8 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#focusMainContainerTimeoutId = null;
|
#focusMainContainerTimeoutId = null;
|
||||||
|
|
||||||
|
#highlightColors = null;
|
||||||
|
|
||||||
#idManager = new IdManager();
|
#idManager = new IdManager();
|
||||||
|
|
||||||
#isEnabled = false;
|
#isEnabled = false;
|
||||||
@ -553,6 +555,8 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#lastActiveElement = null;
|
#lastActiveElement = null;
|
||||||
|
|
||||||
|
#mainHighlightColorPicker = null;
|
||||||
|
|
||||||
#mode = AnnotationEditorType.NONE;
|
#mode = AnnotationEditorType.NONE;
|
||||||
|
|
||||||
#selectedEditors = new Set();
|
#selectedEditors = new Set();
|
||||||
@ -607,6 +611,7 @@ class AnnotationEditorUIManager {
|
|||||||
// For example, sliders can be controlled with the arrow keys.
|
// For example, sliders can be controlled with the arrow keys.
|
||||||
return (
|
return (
|
||||||
self.#container.contains(document.activeElement) &&
|
self.#container.contains(document.activeElement) &&
|
||||||
|
document.activeElement.tagName !== "BUTTON" &&
|
||||||
self.hasSomethingToControl()
|
self.hasSomethingToControl()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -736,7 +741,8 @@ class AnnotationEditorUIManager {
|
|||||||
altTextManager,
|
altTextManager,
|
||||||
eventBus,
|
eventBus,
|
||||||
pdfDocument,
|
pdfDocument,
|
||||||
pageColors
|
pageColors,
|
||||||
|
highlightColors
|
||||||
) {
|
) {
|
||||||
this.#container = container;
|
this.#container = container;
|
||||||
this.#viewer = viewer;
|
this.#viewer = viewer;
|
||||||
@ -749,6 +755,7 @@ class AnnotationEditorUIManager {
|
|||||||
this.#annotationStorage = pdfDocument.annotationStorage;
|
this.#annotationStorage = pdfDocument.annotationStorage;
|
||||||
this.#filterFactory = pdfDocument.filterFactory;
|
this.#filterFactory = pdfDocument.filterFactory;
|
||||||
this.#pageColors = pageColors;
|
this.#pageColors = pageColors;
|
||||||
|
this.#highlightColors = highlightColors || null;
|
||||||
this.viewParameters = {
|
this.viewParameters = {
|
||||||
realScale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
realScale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
@ -803,6 +810,24 @@ class AnnotationEditorUIManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get highlightColors() {
|
||||||
|
return shadow(
|
||||||
|
this,
|
||||||
|
"highlightColors",
|
||||||
|
this.#highlightColors
|
||||||
|
? new Map(
|
||||||
|
this.#highlightColors
|
||||||
|
.split(",")
|
||||||
|
.map(pair => pair.split("=").map(x => x.trim()))
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setMainHighlightColorPicker(colorPicker) {
|
||||||
|
this.#mainHighlightColorPicker = colorPicker;
|
||||||
|
}
|
||||||
|
|
||||||
editAltText(editor) {
|
editAltText(editor) {
|
||||||
this.#altTextManager?.editAltText(this, editor);
|
this.#altTextManager?.editAltText(this, editor);
|
||||||
}
|
}
|
||||||
@ -1246,9 +1271,14 @@ class AnnotationEditorUIManager {
|
|||||||
if (!this.#editorTypes) {
|
if (!this.#editorTypes) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type === AnnotationEditorParamsType.CREATE) {
|
|
||||||
this.currentLayer.addNewEditor();
|
switch (type) {
|
||||||
return;
|
case AnnotationEditorParamsType.CREATE:
|
||||||
|
this.currentLayer.addNewEditor();
|
||||||
|
return;
|
||||||
|
case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR:
|
||||||
|
this.#mainHighlightColorPicker?.updateColor(value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const editor of this.#selectedEditors) {
|
for (const editor of this.#selectedEditors) {
|
||||||
|
@ -70,6 +70,7 @@ import { renderTextLayer, updateTextLayer } from "./display/text_layer.js";
|
|||||||
import { AnnotationEditorLayer } from "./display/editor/annotation_editor_layer.js";
|
import { AnnotationEditorLayer } from "./display/editor/annotation_editor_layer.js";
|
||||||
import { AnnotationEditorUIManager } from "./display/editor/tools.js";
|
import { AnnotationEditorUIManager } from "./display/editor/tools.js";
|
||||||
import { AnnotationLayer } from "./display/annotation_layer.js";
|
import { AnnotationLayer } from "./display/annotation_layer.js";
|
||||||
|
import { ColorPicker } from "./display/editor/color_picker.js";
|
||||||
import { DrawLayer } from "./display/draw_layer.js";
|
import { DrawLayer } from "./display/draw_layer.js";
|
||||||
import { GlobalWorkerOptions } from "./display/worker_options.js";
|
import { GlobalWorkerOptions } from "./display/worker_options.js";
|
||||||
import { Outliner } from "./display/editor/outliner.js";
|
import { Outliner } from "./display/editor/outliner.js";
|
||||||
@ -92,6 +93,7 @@ export {
|
|||||||
AnnotationMode,
|
AnnotationMode,
|
||||||
build,
|
build,
|
||||||
CMapCompressionType,
|
CMapCompressionType,
|
||||||
|
ColorPicker,
|
||||||
createValidAbsoluteUrl,
|
createValidAbsoluteUrl,
|
||||||
DOMSVGFactory,
|
DOMSVGFactory,
|
||||||
DrawLayer,
|
DrawLayer,
|
||||||
|
@ -87,7 +87,7 @@ const AnnotationEditorParamsType = {
|
|||||||
INK_THICKNESS: 22,
|
INK_THICKNESS: 22,
|
||||||
INK_OPACITY: 23,
|
INK_OPACITY: 23,
|
||||||
HIGHLIGHT_COLOR: 31,
|
HIGHLIGHT_COLOR: 31,
|
||||||
HIGHLIGHT_OPACITY: 32,
|
HIGHLIGHT_DEFAULT_COLOR: 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Permission flags from Table 22, Section 7.6.3.2 of the PDF specification.
|
// Permission flags from Table 22, Section 7.6.3.2 of the PDF specification.
|
||||||
|
@ -63,6 +63,7 @@ import {
|
|||||||
import { AnnotationEditorLayer } from "../../src/display/editor/annotation_editor_layer.js";
|
import { AnnotationEditorLayer } from "../../src/display/editor/annotation_editor_layer.js";
|
||||||
import { AnnotationEditorUIManager } from "../../src/display/editor/tools.js";
|
import { AnnotationEditorUIManager } from "../../src/display/editor/tools.js";
|
||||||
import { AnnotationLayer } from "../../src/display/annotation_layer.js";
|
import { AnnotationLayer } from "../../src/display/annotation_layer.js";
|
||||||
|
import { ColorPicker } from "../../src/display/editor/color_picker.js";
|
||||||
import { DrawLayer } from "../../src/display/draw_layer.js";
|
import { DrawLayer } from "../../src/display/draw_layer.js";
|
||||||
import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
|
import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
|
||||||
import { Outliner } from "../../src/display/editor/outliner.js";
|
import { Outliner } from "../../src/display/editor/outliner.js";
|
||||||
@ -78,6 +79,7 @@ const expectedAPI = Object.freeze({
|
|||||||
AnnotationMode,
|
AnnotationMode,
|
||||||
build,
|
build,
|
||||||
CMapCompressionType,
|
CMapCompressionType,
|
||||||
|
ColorPicker,
|
||||||
createValidAbsoluteUrl,
|
createValidAbsoluteUrl,
|
||||||
DOMSVGFactory,
|
DOMSVGFactory,
|
||||||
DrawLayer,
|
DrawLayer,
|
||||||
|
@ -904,6 +904,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.colorPicker {
|
||||||
|
--hover-outline-color: #0250bb;
|
||||||
|
--selected-outline-color: #0060df;
|
||||||
|
--swatch-border-color: #cfcfd8;
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
--hover-outline-color: #80ebff;
|
||||||
|
--selected-outline-color: #aaf2ff;
|
||||||
|
--swatch-border-color: #52525e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (forced-colors: active) {
|
||||||
|
--hover-outline-color: Highlight;
|
||||||
|
--selected-outline-color: var(--hover-outline-color);
|
||||||
|
--swatch-border-color: ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swatch {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 1px solid var(--swatch-border-color);
|
||||||
|
border-radius: 100%;
|
||||||
|
outline-offset: 2px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
forced-color-adjust: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:is(:hover, .selected) > .swatch {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.annotationEditorLayer {
|
.annotationEditorLayer {
|
||||||
&[data-main-rotation="0"] {
|
&[data-main-rotation="0"] {
|
||||||
.highlightEditor > .editToolbar {
|
.highlightEditor > .editToolbar {
|
||||||
@ -962,7 +994,144 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editToolbar {
|
.editToolbar {
|
||||||
|
--editor-toolbar-colorpicker-arrow-image: url(images/toolbarButton-menuArrow.svg);
|
||||||
|
|
||||||
transform-origin: center !important;
|
transform-origin: center !important;
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
.colorPicker {
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
mask-image: var(--editor-toolbar-colorpicker-arrow-image);
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--editor-toolbar-fg-color);
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
background-color: var(--editor-toolbar-hover-fg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:has(.dropdown:not(.hidden)) {
|
||||||
|
background-color: var(--editor-toolbar-hover-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 11px;
|
||||||
|
padding-block: 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: var(--editor-toolbar-bg-color);
|
||||||
|
border: 1px solid var(--editor-toolbar-border-color);
|
||||||
|
box-shadow: var(--editor-toolbar-shadow);
|
||||||
|
inset-block-start: calc(100% + 4px);
|
||||||
|
width: calc(100% + 2 * var(--editor-toolbar-padding));
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
&:is(:active, :focus-visible) {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .swatch {
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[aria-selected="true"] > .swatch {
|
||||||
|
outline: 2px solid var(--selected-outline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:is(:hover, :active, :focus-visible) > .swatch {
|
||||||
|
outline: 2px solid var(--hover-outline-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editorParamsToolbar:has(#highlightParamsToolbarContainer) {
|
||||||
|
padding: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#highlightParamsToolbarContainer {
|
||||||
|
height: auto;
|
||||||
|
padding-inline: 10px;
|
||||||
|
padding-block: 10px 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.colorPicker {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
#highlightColorPickerLabel {
|
||||||
|
width: fit-content;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: none;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
.swatch {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:is(:active, :focus-visible) {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[aria-selected="true"] > .swatch {
|
||||||
|
outline: 2px solid var(--selected-outline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:is(:hover, :active, :focus-visible) > .swatch {
|
||||||
|
outline: 2px solid var(--hover-outline-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,6 @@ class AnnotationEditorParams {
|
|||||||
#bindListeners({
|
#bindListeners({
|
||||||
editorFreeTextFontSize,
|
editorFreeTextFontSize,
|
||||||
editorFreeTextColor,
|
editorFreeTextColor,
|
||||||
editorHighlightColor,
|
|
||||||
editorHighlightOpacity,
|
|
||||||
editorInkColor,
|
editorInkColor,
|
||||||
editorInkThickness,
|
editorInkThickness,
|
||||||
editorInkOpacity,
|
editorInkOpacity,
|
||||||
@ -48,12 +46,6 @@ class AnnotationEditorParams {
|
|||||||
editorFreeTextColor.addEventListener("input", function () {
|
editorFreeTextColor.addEventListener("input", function () {
|
||||||
dispatchEvent("FREETEXT_COLOR", this.value);
|
dispatchEvent("FREETEXT_COLOR", this.value);
|
||||||
});
|
});
|
||||||
editorHighlightColor.addEventListener("input", function () {
|
|
||||||
dispatchEvent("HIGHLIGHT_COLOR", this.value);
|
|
||||||
});
|
|
||||||
editorHighlightOpacity.addEventListener("input", function () {
|
|
||||||
dispatchEvent("HIGHLIGHT_OPACITY", this.valueAsNumber);
|
|
||||||
});
|
|
||||||
editorInkColor.addEventListener("input", function () {
|
editorInkColor.addEventListener("input", function () {
|
||||||
dispatchEvent("INK_COLOR", this.value);
|
dispatchEvent("INK_COLOR", this.value);
|
||||||
});
|
});
|
||||||
@ -76,12 +68,6 @@ class AnnotationEditorParams {
|
|||||||
case AnnotationEditorParamsType.FREETEXT_COLOR:
|
case AnnotationEditorParamsType.FREETEXT_COLOR:
|
||||||
editorFreeTextColor.value = value;
|
editorFreeTextColor.value = value;
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
|
|
||||||
editorHighlightColor.value = value;
|
|
||||||
break;
|
|
||||||
case AnnotationEditorParamsType.HIGHLIGHT_OPACITY:
|
|
||||||
editorHighlightOpacity.value = value;
|
|
||||||
break;
|
|
||||||
case AnnotationEditorParamsType.INK_COLOR:
|
case AnnotationEditorParamsType.INK_COLOR:
|
||||||
editorInkColor.value = value;
|
editorInkColor.value = value;
|
||||||
break;
|
break;
|
||||||
|
@ -442,6 +442,7 @@ const PDFViewerApplication = {
|
|||||||
textLayerMode: AppOptions.get("textLayerMode"),
|
textLayerMode: AppOptions.get("textLayerMode"),
|
||||||
annotationMode: AppOptions.get("annotationMode"),
|
annotationMode: AppOptions.get("annotationMode"),
|
||||||
annotationEditorMode,
|
annotationEditorMode,
|
||||||
|
annotationEditorHighlightColors: AppOptions.get("highlightEditorColors"),
|
||||||
imageResourcesPath: AppOptions.get("imageResourcesPath"),
|
imageResourcesPath: AppOptions.get("imageResourcesPath"),
|
||||||
enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
|
enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
|
||||||
isOffscreenCanvasSupported,
|
isOffscreenCanvasSupported,
|
||||||
|
@ -158,6 +158,11 @@ const defaultOptions = {
|
|||||||
value: 0,
|
value: 0,
|
||||||
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
||||||
},
|
},
|
||||||
|
highlightEditorColors: {
|
||||||
|
/** @type {string} */
|
||||||
|
value: "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F",
|
||||||
|
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
||||||
|
},
|
||||||
historyUpdateUrl: {
|
historyUpdateUrl: {
|
||||||
/** @type {boolean} */
|
/** @type {boolean} */
|
||||||
value: false,
|
value: false,
|
||||||
|
@ -36,8 +36,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.highlight {
|
&.highlight {
|
||||||
|
--blend-mode: multiply;
|
||||||
|
|
||||||
|
@media screen and (forced-colors: active) {
|
||||||
|
--blend-mode: difference;
|
||||||
|
}
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
mix-blend-mode: multiply;
|
mix-blend-mode: var(--blend-mode);
|
||||||
fill-rule: evenodd;
|
fill-rule: evenodd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +109,8 @@ function isValidAnnotationEditorMode(mode) {
|
|||||||
* @property {number} [annotationEditorMode] - Enables the creation and editing
|
* @property {number} [annotationEditorMode] - Enables the creation and editing
|
||||||
* of new Annotations. The constants from {@link AnnotationEditorType} should
|
* of new Annotations. The constants from {@link AnnotationEditorType} should
|
||||||
* be used. The default value is `AnnotationEditorType.NONE`.
|
* be used. The default value is `AnnotationEditorType.NONE`.
|
||||||
|
* @property {string} [annotationEditorHighlightColors] - A comma separated list
|
||||||
|
* of colors to propose to highlight some text in the pdf.
|
||||||
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
||||||
* mainly for annotation icons. Include trailing slash.
|
* mainly for annotation icons. Include trailing slash.
|
||||||
* @property {boolean} [enablePrintAutoRotate] - Enables automatic rotation of
|
* @property {boolean} [enablePrintAutoRotate] - Enables automatic rotation of
|
||||||
@ -202,6 +204,8 @@ class PDFViewer {
|
|||||||
|
|
||||||
#altTextManager = null;
|
#altTextManager = null;
|
||||||
|
|
||||||
|
#annotationEditorHighlightColors = null;
|
||||||
|
|
||||||
#annotationEditorMode = AnnotationEditorType.NONE;
|
#annotationEditorMode = AnnotationEditorType.NONE;
|
||||||
|
|
||||||
#annotationEditorUIManager = null;
|
#annotationEditorUIManager = null;
|
||||||
@ -276,6 +280,8 @@ class PDFViewer {
|
|||||||
options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
|
options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
|
||||||
this.#annotationEditorMode =
|
this.#annotationEditorMode =
|
||||||
options.annotationEditorMode ?? AnnotationEditorType.NONE;
|
options.annotationEditorMode ?? AnnotationEditorType.NONE;
|
||||||
|
this.#annotationEditorHighlightColors =
|
||||||
|
options.annotationEditorHighlightColors || null;
|
||||||
this.imageResourcesPath = options.imageResourcesPath || "";
|
this.imageResourcesPath = options.imageResourcesPath || "";
|
||||||
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
|
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||||
@ -862,8 +868,13 @@ class PDFViewer {
|
|||||||
this.#altTextManager,
|
this.#altTextManager,
|
||||||
this.eventBus,
|
this.eventBus,
|
||||||
pdfDocument,
|
pdfDocument,
|
||||||
this.pageColors
|
this.pageColors,
|
||||||
|
this.#annotationEditorHighlightColors
|
||||||
);
|
);
|
||||||
|
this.eventBus.dispatch("annotationeditoruimanager", {
|
||||||
|
source: this,
|
||||||
|
uiManager: this.#annotationEditorUIManager,
|
||||||
|
});
|
||||||
if (mode !== AnnotationEditorType.NONE) {
|
if (mode !== AnnotationEditorType.NONE) {
|
||||||
this.#annotationEditorUIManager.updateMode(mode);
|
this.#annotationEditorUIManager.updateMode(mode);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ const {
|
|||||||
AnnotationMode,
|
AnnotationMode,
|
||||||
build,
|
build,
|
||||||
CMapCompressionType,
|
CMapCompressionType,
|
||||||
|
ColorPicker,
|
||||||
createValidAbsoluteUrl,
|
createValidAbsoluteUrl,
|
||||||
DOMSVGFactory,
|
DOMSVGFactory,
|
||||||
DrawLayer,
|
DrawLayer,
|
||||||
@ -80,6 +81,7 @@ export {
|
|||||||
AnnotationMode,
|
AnnotationMode,
|
||||||
build,
|
build,
|
||||||
CMapCompressionType,
|
CMapCompressionType,
|
||||||
|
ColorPicker,
|
||||||
createValidAbsoluteUrl,
|
createValidAbsoluteUrl,
|
||||||
DOMSVGFactory,
|
DOMSVGFactory,
|
||||||
DrawLayer,
|
DrawLayer,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AnnotationEditorType, noContextMenu } from "pdfjs-lib";
|
import { AnnotationEditorType, ColorPicker, noContextMenu } from "pdfjs-lib";
|
||||||
import {
|
import {
|
||||||
DEFAULT_SCALE,
|
DEFAULT_SCALE,
|
||||||
DEFAULT_SCALE_VALUE,
|
DEFAULT_SCALE_VALUE,
|
||||||
@ -120,9 +120,24 @@ class Toolbar {
|
|||||||
// Bind the event listeners for click and various other actions.
|
// Bind the event listeners for click and various other actions.
|
||||||
this.#bindListeners(options);
|
this.#bindListeners(options);
|
||||||
|
|
||||||
|
if (options.editorHighlightColorPicker) {
|
||||||
|
this.eventBus._on("annotationeditoruimanager", ({ uiManager }) => {
|
||||||
|
this.#setAnnotationEditorUIManager(
|
||||||
|
uiManager,
|
||||||
|
options.editorHighlightColorPicker
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#setAnnotationEditorUIManager(uiManager, parentContainer) {
|
||||||
|
const colorPicker = new ColorPicker({ uiManager });
|
||||||
|
uiManager.setMainHighlightColorPicker(colorPicker);
|
||||||
|
parentContainer.append(colorPicker.renderMainDropdown());
|
||||||
|
}
|
||||||
|
|
||||||
setPageNumber(pageNumber, pageLabel) {
|
setPageNumber(pageNumber, pageLabel) {
|
||||||
this.pageNumber = pageNumber;
|
this.pageNumber = pageNumber;
|
||||||
this.pageLabel = pageLabel;
|
this.pageLabel = pageLabel;
|
||||||
|
@ -532,6 +532,11 @@ body {
|
|||||||
.editorParamsToolbarContainer .editorParamsLabel {
|
.editorParamsToolbarContainer .editorParamsLabel {
|
||||||
padding-inline-end: 10px;
|
padding-inline-end: 10px;
|
||||||
flex: none;
|
flex: none;
|
||||||
|
font: menu;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 150%;
|
||||||
color: var(--main-color);
|
color: var(--main-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,14 +172,9 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||||||
</div> <!-- findbar -->
|
</div> <!-- findbar -->
|
||||||
|
|
||||||
<div class="editorParamsToolbar hidden doorHangerRight" id="editorHighlightParamsToolbar">
|
<div class="editorParamsToolbar hidden doorHangerRight" id="editorHighlightParamsToolbar">
|
||||||
<div class="editorParamsToolbarContainer">
|
<div id="highlightParamsToolbarContainer" class="editorParamsToolbarContainer">
|
||||||
<div class="editorParamsSetter">
|
<div id="editorHighlightColorPicker" class="colorPicker">
|
||||||
<label for="editorHighlightColor" class="editorParamsLabel" data-l10n-id="editor_highlight_color">Color</label>
|
<span id="highlightColorPickerLabel" class="editorParamsLabel" data-l10n-id="pdfjs-editor-highlight-colorpicker-label">Highlight color</span>
|
||||||
<input type="color" value="#FFFF00" id="editorHighlightColor" class="editorParamsColor" tabindex="100">
|
|
||||||
</div>
|
|
||||||
<div class="editorParamsSetter">
|
|
||||||
<label for="editorHighlightOpacity" class="editorParamsLabel" data-l10n-id="editor_highlight_opacity">Opacity</label>
|
|
||||||
<input type="range" id="editorHighlightOpacity" class="editorParamsSlider" value="100" min="1" max="100" step="1" tabindex="101">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -217,7 +212,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||||||
<div class="editorParamsToolbar hidden doorHangerRight" id="editorStampParamsToolbar">
|
<div class="editorParamsToolbar hidden doorHangerRight" id="editorStampParamsToolbar">
|
||||||
<div class="editorParamsToolbarContainer">
|
<div class="editorParamsToolbarContainer">
|
||||||
<button id="editorStampAddImage" class="secondaryToolbarButton" title="Add image" tabindex="107" data-l10n-id="pdfjs-editor-stamp-add-image-button">
|
<button id="editorStampAddImage" class="secondaryToolbarButton" title="Add image" tabindex="107" data-l10n-id="pdfjs-editor-stamp-add-image-button">
|
||||||
<span data-l10n-id="pdfjs-editor-stamp-add-image-button-label">Add image</span>
|
<span class="editorParamsLabel" data-l10n-id="pdfjs-editor-stamp-add-image-button-label">Add image</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,6 +61,9 @@ function getViewerConfiguration() {
|
|||||||
editorHighlightParamsToolbar: document.getElementById(
|
editorHighlightParamsToolbar: document.getElementById(
|
||||||
"editorHighlightParamsToolbar"
|
"editorHighlightParamsToolbar"
|
||||||
),
|
),
|
||||||
|
editorHighlightColorPicker: document.getElementById(
|
||||||
|
"editorHighlightColorPicker"
|
||||||
|
),
|
||||||
editorInkButton: document.getElementById("editorInk"),
|
editorInkButton: document.getElementById("editorInk"),
|
||||||
editorInkParamsToolbar: document.getElementById("editorInkParamsToolbar"),
|
editorInkParamsToolbar: document.getElementById("editorInkParamsToolbar"),
|
||||||
editorStampButton: document.getElementById("editorStamp"),
|
editorStampButton: document.getElementById("editorStamp"),
|
||||||
@ -168,8 +171,6 @@ function getViewerConfiguration() {
|
|||||||
annotationEditorParams: {
|
annotationEditorParams: {
|
||||||
editorFreeTextFontSize: document.getElementById("editorFreeTextFontSize"),
|
editorFreeTextFontSize: document.getElementById("editorFreeTextFontSize"),
|
||||||
editorFreeTextColor: document.getElementById("editorFreeTextColor"),
|
editorFreeTextColor: document.getElementById("editorFreeTextColor"),
|
||||||
editorHighlightColor: document.getElementById("editorHighlightColor"),
|
|
||||||
editorHighlightOpacity: document.getElementById("editorHighlightOpacity"),
|
|
||||||
editorInkColor: document.getElementById("editorInkColor"),
|
editorInkColor: document.getElementById("editorInkColor"),
|
||||||
editorInkThickness: document.getElementById("editorInkThickness"),
|
editorInkThickness: document.getElementById("editorInkThickness"),
|
||||||
editorInkOpacity: document.getElementById("editorInkOpacity"),
|
editorInkOpacity: document.getElementById("editorInkOpacity"),
|
||||||
|
Loading…
Reference in New Issue
Block a user