Merge pull request #20221 from calixteman/button_color_comment

[Editor] Use the color of the annotation for the background of the comment button
This commit is contained in:
Tim van der Meij 2025-09-04 21:27:07 +02:00 committed by GitHub
commit 84e32964ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 114 additions and 51 deletions

View File

@ -38,6 +38,7 @@ import {
warn,
} from "../shared/util.js";
import {
applyOpacity,
changeLightness,
PDFDateString,
setLayerDimensions,
@ -232,15 +233,8 @@ class AnnotationElement {
if (!this.data.color) {
return null;
}
const [r, g, b] = this.data.color;
const opacity = this.data.opacity ?? 1;
const oppositeOpacity = 255 * (1 - opacity);
return changeLightness(
Math.min(r + oppositeOpacity, 255),
Math.min(g + oppositeOpacity, 255),
Math.min(b + oppositeOpacity, 255)
);
const [r, g, b] = applyOpacity(...this.data.color, this.data.opacity);
return changeLightness(r, g, b);
}
_normalizePoint(point) {

View File

@ -770,7 +770,24 @@ const SupportedImageMimeTypes = [
"image/x-icon",
];
function changeLightness(r, g, b, lumCallback = l => (1 + Math.sqrt(l)) / 2) {
class ColorScheme {
static get isDarkMode() {
return shadow(
this,
"isDarkMode",
!!window?.matchMedia?.("(prefers-color-scheme: dark)").matches
);
}
}
function changeLightness(
r,
g,
b,
lumCallback = ColorScheme.isDarkMode
? l => (1 - Math.sqrt(1 - l)) / 2
: l => (1 + Math.sqrt(l)) / 2
) {
r /= 255;
g /= 255;
b /= 255;
@ -803,8 +820,19 @@ function changeLightness(r, g, b, lumCallback = l => (1 + Math.sqrt(l)) / 2) {
return `hsl(${h}, ${s}%, ${newL}%)`;
}
function applyOpacity(r, g, b, opacity) {
opacity = Math.min(Math.max(opacity ?? 1, 0), 1);
const white = 255 * (1 - opacity);
r = Math.round(r * opacity + white);
g = Math.round(g * opacity + white);
b = Math.round(b * opacity + white);
return [r, g, b];
}
export {
applyOpacity,
changeLightness,
ColorScheme,
deprecated,
fetchData,
getColorValues,

View File

@ -58,6 +58,10 @@ class Comment {
: position[0])
}% - var(--comment-button-dim))`;
style.top = `calc(${100 * position[1]}% - var(--comment-button-dim))`;
const color = this.#editor.commentButtonColor;
if (color) {
style.backgroundColor = color;
}
}
return this.#render(button);

View File

@ -22,13 +22,18 @@ import {
ColorManager,
KeyboardManager,
} from "./tools.js";
import {
applyOpacity,
changeLightness,
noContextMenu,
stopEvent,
} from "../display_utils.js";
import {
FeatureTest,
MathClamp,
shadow,
unreachable,
} from "../../shared/util.js";
import { noContextMenu, stopEvent } from "../display_utils.js";
import { AltText } from "./alt_text.js";
import { Comment } from "./comment.js";
import { EditorToolbar } from "./toolbar.js";
@ -1857,6 +1862,17 @@ class AnnotationEditor {
return this._uiManager.direction === "ltr" ? [1, 0] : [0, 0];
}
get commentButtonColor() {
if (!this.color) {
return null;
}
let [r, g, b] = AnnotationEditor._colorManager.convert(
this._uiManager.getNonHCMColor(this.color)
);
[r, g, b] = applyOpacity(r, g, b, this.opacity);
return changeLightness(r, g, b);
}
/**
* onkeydown callback.
* @param {KeyboardEvent} event

View File

@ -35,8 +35,6 @@ const EOL_PATTERN = /\r\n?|\n/g;
* Basic text editor in order to create a FreeTex annotation.
*/
class FreeTextEditor extends AnnotationEditor {
#color;
#content = "";
#editorDivId = `${this.id}-editor`;
@ -129,7 +127,7 @@ class FreeTextEditor extends AnnotationEditor {
constructor(params) {
super({ ...params, name: "freeTextEditor" });
this.#color =
this.color =
params.color ||
FreeTextEditor._defaultColor ||
AnnotationEditor._defaultLineColor;
@ -201,7 +199,7 @@ class FreeTextEditor extends AnnotationEditor {
get propertiesToUpdate() {
return [
[AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize],
[AnnotationEditorParamsType.FREETEXT_COLOR, this.#color],
[AnnotationEditorParamsType.FREETEXT_COLOR, this.color],
];
}
@ -215,10 +213,6 @@ class FreeTextEditor extends AnnotationEditor {
return AnnotationEditorParamsType.FREETEXT_COLOR;
}
get colorValue() {
return this.#color;
}
/**
* Update the font size and make this action as undoable.
* @param {number} fontSize
@ -248,10 +242,10 @@ class FreeTextEditor extends AnnotationEditor {
*/
#updateColor(color) {
const setColor = col => {
this.#color = this.editorDiv.style.color = col;
this.color = this.editorDiv.style.color = col;
this._colorPicker?.update(col);
};
const savedColor = this.#color;
const savedColor = this.color;
this.addCommands({
cmd: setColor.bind(this, color),
undo: setColor.bind(this, savedColor),
@ -581,7 +575,7 @@ class FreeTextEditor extends AnnotationEditor {
const { style } = this.editorDiv;
style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`;
style.color = this.#color;
style.color = this.color;
this.div.append(this.editorDiv);
@ -819,7 +813,7 @@ class FreeTextEditor extends AnnotationEditor {
}
const editor = await super.deserialize(data, parent, uiManager);
editor.#fontSize = data.fontSize;
editor.#color = Util.makeHexColor(...data.color);
editor.color = Util.makeHexColor(...data.color);
editor.#content = FreeTextEditor.#deserializeContent(data.value);
editor._initialData = initialData;
if (data.comment) {
@ -841,9 +835,7 @@ class FreeTextEditor extends AnnotationEditor {
const rect = this.getPDFRect();
const color = AnnotationEditor._colorManager.convert(
this.isAttachedToDOM
? getComputedStyle(this.editorDiv).color
: this.#color
this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.color
);
const serialized = {
@ -895,7 +887,7 @@ class FreeTextEditor extends AnnotationEditor {
}
const { style } = content;
style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`;
style.color = this.#color;
style.color = this.color;
content.replaceChildren();
for (const line of this.#content.split("\n")) {

View File

@ -64,8 +64,6 @@ class HighlightEditor extends AnnotationEditor {
#lastPoint = null;
#opacity;
#outlineId = null;
#text = "";
@ -108,7 +106,7 @@ class HighlightEditor extends AnnotationEditor {
super({ ...params, name: "highlightEditor" });
this.color = params.color || HighlightEditor._defaultColor;
this.#thickness = params.thickness || HighlightEditor._defaultThickness;
this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
this.opacity = params.opacity || HighlightEditor._defaultOpacity;
this.#boxes = params.boxes || null;
this.#methodOfCreation = params.methodOfCreation || "";
this.#text = params.text || "";
@ -361,7 +359,7 @@ class HighlightEditor extends AnnotationEditor {
#updateColor(color) {
const setColorAndOpacity = (col, opa) => {
this.color = col;
this.#opacity = opa;
this.opacity = opa;
this.parent?.drawLayer.updateProperties(this.#id, {
root: {
fill: col,
@ -371,7 +369,7 @@ class HighlightEditor extends AnnotationEditor {
this.#colorPicker?.updateColor(col);
};
const savedColor = this.color;
const savedOpacity = this.#opacity;
const savedOpacity = this.opacity;
this.addCommands({
cmd: setColorAndOpacity.bind(
this,
@ -549,7 +547,7 @@ class HighlightEditor extends AnnotationEditor {
root: {
viewBox: "0 0 1 1",
fill: this.color,
"fill-opacity": this.#opacity,
"fill-opacity": this.opacity,
},
rootClass: {
highlight: true,
@ -951,7 +949,7 @@ class HighlightEditor extends AnnotationEditor {
const editor = await super.deserialize(data, parent, uiManager);
editor.color = Util.makeHexColor(...color);
editor.#opacity = opacity || 1;
editor.opacity = opacity || 1;
if (inkLists) {
editor.#thickness = data.thickness;
}
@ -1046,7 +1044,7 @@ class HighlightEditor extends AnnotationEditor {
const serialized = {
annotationType: AnnotationEditorType.HIGHLIGHT,
color,
opacity: this.#opacity,
opacity: this.opacity,
thickness: this.#thickness,
quadPoints: this.#serializeBoxes(),
outlines: this.#serializeOutlines(rect),

View File

@ -193,10 +193,14 @@ class InkEditor extends DrawingEditor {
return AnnotationEditorParamsType.INK_COLOR;
}
get colorValue() {
get color() {
return this._drawingOptions.stroke;
}
get opacity() {
return this._drawingOptions["stroke-opacity"];
}
/** @inheritdoc */
onScaleChanging() {
if (!this.parent) {

View File

@ -15,6 +15,7 @@
import { AnnotationEditorType, AnnotationPrefix } from "../../shared/util.js";
import {
ColorScheme,
OutputScale,
PixelsPerInch,
SupportedImageMimeTypes,
@ -535,7 +536,7 @@ class StampEditor extends AnnotationEditor {
black = "#cfcfd8";
if (this._uiManager.hcmFilter !== "none") {
black = "black";
} else if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) {
} else if (ColorScheme.isDarkMode) {
white = "#8f8f9d";
black = "#42414d";
}

View File

@ -45,13 +45,7 @@ import {
VerbosityLevel,
} from "./shared/util.js";
import {
build,
getDocument,
PDFDataRangeTransport,
PDFWorker,
version,
} from "./display/api.js";
import {
applyOpacity,
changeLightness,
fetchData,
getFilenameFromUrl,
@ -69,6 +63,13 @@ import {
stopEvent,
SupportedImageMimeTypes,
} from "./display/display_utils.js";
import {
build,
getDocument,
PDFDataRangeTransport,
PDFWorker,
version,
} from "./display/api.js";
import { AnnotationEditorLayer } from "./display/editor/annotation_editor_layer.js";
import { AnnotationEditorUIManager } from "./display/editor/tools.js";
import { AnnotationLayer } from "./display/annotation_layer.js";
@ -98,6 +99,7 @@ globalThis.pdfjsLib = {
AnnotationLayer,
AnnotationMode,
AnnotationType,
applyOpacity,
build,
changeLightness,
ColorPicker,
@ -154,6 +156,7 @@ export {
AnnotationLayer,
AnnotationMode,
AnnotationType,
applyOpacity,
build,
changeLightness,
ColorPicker,

View File

@ -14,6 +14,7 @@
*/
import {
applyOpacity,
changeLightness,
getFilenameFromUrl,
getPdfFilenameFromUrl,
@ -324,4 +325,21 @@ describe("display_utils", function () {
);
});
});
describe("applyOpacity", function () {
it("Check that the opacity is applied correctly", function () {
if (isNodeJS) {
pending("OffscreenCanvas is not supported in Node.js.");
}
const canvas = new OffscreenCanvas(1, 1);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 1, 1);
ctx.fillStyle = "rgb(123, 45, 67)";
ctx.globalAlpha = 0.8;
ctx.fillRect(0, 0, 1, 1);
const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data;
expect(applyOpacity(123, 45, 67, ctx.globalAlpha)).toEqual([r, g, b]);
});
});
});

View File

@ -36,13 +36,7 @@ import {
VerbosityLevel,
} from "../../src/shared/util.js";
import {
build,
getDocument,
PDFDataRangeTransport,
PDFWorker,
version,
} from "../../src/display/api.js";
import {
applyOpacity,
changeLightness,
fetchData,
getFilenameFromUrl,
@ -60,6 +54,13 @@ import {
stopEvent,
SupportedImageMimeTypes,
} from "../../src/display/display_utils.js";
import {
build,
getDocument,
PDFDataRangeTransport,
PDFWorker,
version,
} from "../../src/display/api.js";
import { AnnotationEditorLayer } from "../../src/display/editor/annotation_editor_layer.js";
import { AnnotationEditorUIManager } from "../../src/display/editor/tools.js";
import { AnnotationLayer } from "../../src/display/annotation_layer.js";
@ -82,6 +83,7 @@ const expectedAPI = Object.freeze({
AnnotationLayer,
AnnotationMode,
AnnotationType,
applyOpacity,
build,
changeLightness,
ColorPicker,

View File

@ -257,6 +257,7 @@
:is(.annotationLayer, .annotationEditorLayer) {
.annotationCommentButton {
color-scheme: light dark;
--comment-button-bg: light-dark(white, #1c1b22);
--comment-button-fg: light-dark(#5b5b66, #fbfbfe);
--comment-button-active-bg: light-dark(#0041a4, #a6ecf4);

View File

@ -22,6 +22,7 @@ const {
AnnotationLayer,
AnnotationMode,
AnnotationType,
applyOpacity,
build,
changeLightness,
ColorPicker,
@ -78,6 +79,7 @@ export {
AnnotationLayer,
AnnotationMode,
AnnotationType,
applyOpacity,
build,
changeLightness,
ColorPicker,