From 9723c5d3775ac11479a96cf7104df3b527df99a6 Mon Sep 17 00:00:00 2001 From: Calixte Denizet <calixte.denizet@gmail.com> Date: Wed, 29 Jun 2022 15:39:02 +0200 Subject: [PATCH] [Editor] Handle correctly colors when saving a document in HCM - for example in Dusk theme (Windows 11), black appears to be white, so the user will draw something in white. But if they want to print or save the used color must be black. - fix a bug with the color input which only accepts hex string colors; - adjust outline color of the selected/hovered editors in HCM. --- src/display/display_utils.js | 13 +++++ src/display/editor/editor.js | 14 +++++- src/display/editor/freetext.js | 20 +++++--- src/display/editor/ink.js | 18 ++++--- src/display/editor/tools.js | 65 ++++++++++++++++++++++++- web/annotation_editor_layer_builder.css | 7 +++ 6 files changed, 120 insertions(+), 17 deletions(-) diff --git a/src/display/display_utils.js b/src/display/display_utils.js index c1fd5a167..bd1bd5a09 100644 --- a/src/display/display_utils.js +++ b/src/display/display_utils.js @@ -589,12 +589,25 @@ function getRGB(color) { return [0, 0, 0]; } +function getColorValues(colors) { + const span = document.createElement("span"); + span.style.visibility = "hidden"; + document.body.append(span); + for (const name of colors.keys()) { + span.style.color = name; + const computedColor = window.getComputedStyle(span).color; + colors.set(name, getRGB(computedColor)); + } + span.remove(); +} + export { deprecated, DOMCanvasFactory, DOMCMapReaderFactory, DOMStandardFontDataFactory, DOMSVGFactory, + getColorValues, getFilenameFromUrl, getPdfFilenameFromUrl, getRGB, diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 3b33f4cf8..1e350c883 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -16,8 +16,8 @@ // eslint-disable-next-line max-len /** @typedef {import("./annotation_editor_layer.js").AnnotationEditorLayer} AnnotationEditorLayer */ -import { bindEvents } from "./tools.js"; -import { unreachable } from "../../shared/util.js"; +import { bindEvents, ColorManager } from "./tools.js"; +import { shadow, unreachable } from "../../shared/util.js"; /** * @typedef {Object} AnnotationEditorParameters @@ -33,6 +33,8 @@ import { unreachable } from "../../shared/util.js"; class AnnotationEditor { #isInEditMode = false; + static _colorManager = new ColorManager(); + /** * @param {AnnotationEditorParameters} parameters */ @@ -56,6 +58,14 @@ class AnnotationEditor { this.isAttachedToDOM = false; } + static get _defaultLineColor() { + return shadow( + this, + "_defaultLineColor", + this._colorManager.getHexCode("CanvasText") + ); + } + /** * This editor will be behind the others. */ diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index 841e4098f..2ecd986bc 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -21,7 +21,6 @@ import { } 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. @@ -43,13 +42,16 @@ class FreeTextEditor extends AnnotationEditor { static _internalPadding = 0; - static _defaultFontSize = 10; + static _defaultColor = null; - static _defaultColor = "CanvasText"; + static _defaultFontSize = 10; constructor(params) { super({ ...params, name: "freeTextEditor" }); - this.#color = params.color || FreeTextEditor._defaultColor; + this.#color = + params.color || + FreeTextEditor._defaultColor || + AnnotationEditor._defaultLineColor; this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize; } @@ -124,7 +126,10 @@ class FreeTextEditor extends AnnotationEditor { AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize, ], - [AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor], + [ + AnnotationEditorParamsType.FREETEXT_COLOR, + FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor, + ], ]; } @@ -362,8 +367,9 @@ 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); + const color = AnnotationEditor._colorManager.convert( + getComputedStyle(this.editorDiv).color + ); return { annotationType: AnnotationEditorType.FREETEXT, diff --git a/src/display/editor/ink.js b/src/display/editor/ink.js index ec4a4c13c..8d6153fce 100644 --- a/src/display/editor/ink.js +++ b/src/display/editor/ink.js @@ -20,7 +20,6 @@ import { } from "../../shared/util.js"; import { AnnotationEditor } from "./editor.js"; import { fitCurve } from "./fit_curve/fit_curve.js"; -import { getRGB } from "../display_utils.js"; /** * Basic draw editor in order to generate an Ink annotation. @@ -48,13 +47,16 @@ class InkEditor extends AnnotationEditor { #realHeight = 0; - static _defaultThickness = 1; + static _defaultColor = null; - static _defaultColor = "CanvasText"; + static _defaultThickness = 1; constructor(params) { super({ ...params, name: "inkEditor" }); - this.color = params.color || InkEditor._defaultColor; + this.color = + params.color || + InkEditor._defaultColor || + AnnotationEditor._defaultLineColor; this.thickness = params.thickness || InkEditor._defaultThickness; this.paths = []; this.bezierPath2D = []; @@ -124,7 +126,10 @@ class InkEditor extends AnnotationEditor { static get defaultPropertiesToUpdate() { return [ [AnnotationEditorParamsType.INK_THICKNESS, InkEditor._defaultThickness], - [AnnotationEditorParamsType.INK_COLOR, InkEditor._defaultColor], + [ + AnnotationEditorParamsType.INK_COLOR, + InkEditor._defaultColor || AnnotationEditor._defaultLineColor, + ], ]; } @@ -846,8 +851,7 @@ class InkEditor extends AnnotationEditor { const height = this.rotation % 180 === 0 ? rect[3] - rect[1] : rect[2] - rect[0]; - // We don't use this.color directly because it can be CanvasText. - const color = getRGB(this.ctx.strokeStyle); + const color = AnnotationEditor._colorManager.convert(this.ctx.strokeStyle); return { annotationType: AnnotationEditorType.INK, diff --git a/src/display/editor/tools.js b/src/display/editor/tools.js index 10e532e4f..cc7b499a8 100644 --- a/src/display/editor/tools.js +++ b/src/display/editor/tools.js @@ -21,7 +21,9 @@ import { AnnotationEditorPrefix, AnnotationEditorType, shadow, + Util, } from "../../shared/util.js"; +import { getColorValues, getRGB } from "../display_utils.js"; function bindEvents(obj, element, names) { for (const name of names) { @@ -279,6 +281,67 @@ class ClipboardManager { } } +class ColorManager { + static _colorsMapping = new Map([ + ["CanvasText", [0, 0, 0]], + ["Canvas", [255, 255, 255]], + ]); + + get _colors() { + if ( + typeof PDFJSDev !== "undefined" && + PDFJSDev.test("LIB") && + typeof document === "undefined" + ) { + return shadow(this, "_colors", ColorManager._colorsMapping); + } + + const colors = new Map([ + ["CanvasText", null], + ["Canvas", null], + ]); + getColorValues(colors); + return shadow(this, "_colors", colors); + } + + /** + * In High Contrast Mode, the color on the screen is not always the + * real color used in the pdf. + * For example in some cases white can appear to be black but when saving + * we want to have white. + * @param {string} color + * @returns {Array<number>} + */ + convert(color) { + const rgb = getRGB(color); + if (!window.matchMedia("(forced-colors: active)").matches) { + return rgb; + } + + for (const [name, RGB] of this._colors) { + if (RGB.every((x, i) => x === rgb[i])) { + return ColorManager._colorsMapping.get(name); + } + } + return rgb; + } + + /** + * An input element must have its color value as a hex string + * and not as color name. + * So this function converts a name into an hex string. + * @param {string} name + * @returns {string} + */ + getHexCode(name) { + const rgb = this._colors.get(name); + if (!rgb) { + return name; + } + return Util.makeHexColor(...rgb); + } +} + /** * A pdf has several pages and each of them when it will rendered * will have an AnnotationEditorLayer which will contain the some @@ -683,4 +746,4 @@ class AnnotationEditorUIManager { } } -export { AnnotationEditorUIManager, bindEvents, KeyboardManager }; +export { AnnotationEditorUIManager, bindEvents, ColorManager, KeyboardManager }; diff --git a/web/annotation_editor_layer_builder.css b/web/annotation_editor_layer_builder.css index 1f72954db..a3e556a64 100644 --- a/web/annotation_editor_layer_builder.css +++ b/web/annotation_editor_layer_builder.css @@ -20,6 +20,13 @@ --freetext-padding: 2px; } +@media (forced-colors: active) { + :root { + --focus-outline: solid 3px ButtonText; + --hover-outline: dashed 3px ButtonText; + } +} + [data-editor-rotation="90"] { transform: rotate(90deg); }