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} + */ + 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); }