Get rid of CSS transform on each annotation in the annotation layer

- each annotation has its coordinates/dimensions expressed in percentage,
  hence it's correctly positioned whatever the scale factor is;
- the font sizes are expressed in percentage too and the main font size
  is scaled thanks a css var (--scale-factor);
- the rotation is now applied on the div annotationLayer;
- this patch improve the rendering of some strings where the glyph spacing
  was not correct (it's a Firefox bug);
- it helps to simplify the code and it should slightly improve the update of
  page (on zoom or rotation).
This commit is contained in:
Calixte Denizet 2022-06-13 11:01:55 +02:00
parent 6dc8d1f532
commit e2db9bacef
17 changed files with 154 additions and 155 deletions

View File

@ -432,6 +432,7 @@ class Annotation {
this.setAppearance(dict); this.setAppearance(dict);
this.setBorderAndBackgroundColors(dict.get("MK")); this.setBorderAndBackgroundColors(dict.get("MK"));
this._hasOwnCanvas = false;
this._streams = []; this._streams = [];
if (this.appearance) { if (this.appearance) {
this._streams.push(this.appearance); this._streams.push(this.appearance);
@ -450,7 +451,6 @@ class Annotation {
modificationDate: this.modificationDate, modificationDate: this.modificationDate,
rect: this.rectangle, rect: this.rectangle,
subtype: params.subtype, subtype: params.subtype,
hasOwnCanvas: false,
}; };
if (params.collectFields) { if (params.collectFields) {
@ -849,7 +849,7 @@ class Annotation {
const data = this.data; const data = this.data;
let appearance = this.appearance; let appearance = this.appearance;
const isUsingOwnCanvas = const isUsingOwnCanvas =
data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY; this._hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY;
if (!appearance) { if (!appearance) {
if (!isUsingOwnCanvas) { if (!isUsingOwnCanvas) {
return Promise.resolve(new OperatorList()); return Promise.resolve(new OperatorList());
@ -2163,7 +2163,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
} else if (this.data.radioButton) { } else if (this.data.radioButton) {
this._processRadioButton(params); this._processRadioButton(params);
} else if (this.data.pushButton) { } else if (this.data.pushButton) {
this.data.hasOwnCanvas = true; this._hasOwnCanvas = true;
this._processPushButton(params); this._processPushButton(params);
} else { } else {
warn("Invalid field flags for button widget annotation"); warn("Invalid field flags for button widget annotation");

View File

@ -1425,7 +1425,7 @@ class ChoiceList extends XFAObject {
const field = ui[$getParent](); const field = ui[$getParent]();
const fontSize = (field.font && field.font.size) || 10; const fontSize = (field.font && field.font.size) || 10;
const optionStyle = { const optionStyle = {
fontSize: `calc(${fontSize}px * var(--zoom-factor))`, fontSize: `calc(${fontSize}px * var(--scale-factor))`,
}; };
const children = []; const children = [];

View File

@ -182,6 +182,10 @@ function mapStyle(styleStr, node, richText) {
); );
} }
if (richText && style.fontSize) {
style.fontSize = `calc(${style.fontSize} * var(--scale-factor))`;
}
fixTextIndent(style); fixTextIndent(style);
return style; return style;
} }

View File

@ -197,7 +197,11 @@ class AnnotationElement {
page = this.page, page = this.page,
viewport = this.viewport; viewport = this.viewport;
const container = document.createElement("section"); const container = document.createElement("section");
let { width, height } = getRectDims(data.rect); const { width, height } = getRectDims(data.rect);
const [pageLLx, pageLLy, pageURx, pageURy] = viewport.viewBox;
const pageWidth = pageURx - pageLLx;
const pageHeight = pageURy - pageLLy;
container.setAttribute("data-annotation-id", data.id); container.setAttribute("data-annotation-id", data.id);
@ -210,41 +214,13 @@ class AnnotationElement {
page.view[3] - data.rect[3] + page.view[1], page.view[3] - data.rect[3] + page.view[1],
]); ]);
if (data.hasOwnCanvas) {
const transform = viewport.transform.slice();
const [scaleX, scaleY] = Util.singularValueDecompose2dScale(transform);
width = Math.ceil(width * scaleX);
height = Math.ceil(height * scaleY);
rect[0] *= scaleX;
rect[1] *= scaleY;
// Reset the scale part of the transform matrix (which must be diagonal
// or anti-diagonal) in order to avoid to rescale the canvas.
// The canvas for the annotation is correctly scaled when it is drawn
// (see `beginAnnotation` in canvas.js).
for (let i = 0; i < 4; i++) {
transform[i] = Math.sign(transform[i]);
}
container.style.transform = `matrix(${transform.join(",")})`;
} else {
container.style.transform = `matrix(${viewport.transform.join(",")})`;
}
container.style.transformOrigin = `${-rect[0]}px ${-rect[1]}px`;
if (!ignoreBorder && data.borderStyle.width > 0) { if (!ignoreBorder && data.borderStyle.width > 0) {
container.style.borderWidth = `${data.borderStyle.width}px`; container.style.borderWidth = `${data.borderStyle.width}px`;
if (data.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) {
// Underline styles only have a bottom border, so we do not need
// to adjust for all borders. This yields a similar result as
// Adobe Acrobat/Reader.
width -= 2 * data.borderStyle.width;
height -= 2 * data.borderStyle.width;
}
const horizontalRadius = data.borderStyle.horizontalCornerRadius; const horizontalRadius = data.borderStyle.horizontalCornerRadius;
const verticalRadius = data.borderStyle.verticalCornerRadius; const verticalRadius = data.borderStyle.verticalCornerRadius;
if (horizontalRadius > 0 || verticalRadius > 0) { if (horizontalRadius > 0 || verticalRadius > 0) {
const radius = `${horizontalRadius}px / ${verticalRadius}px`; const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`;
container.style.borderRadius = radius; container.style.borderRadius = radius;
} }
@ -286,15 +262,11 @@ class AnnotationElement {
} }
} }
container.style.left = `${rect[0]}px`; container.style.left = `${(100 * (rect[0] - pageLLx)) / pageWidth}%`;
container.style.top = `${rect[1]}px`; container.style.top = `${(100 * (rect[1] - pageLLy)) / pageHeight}%`;
container.style.width = `${(100 * width) / pageWidth}%`;
container.style.height = `${(100 * height) / pageHeight}%`;
if (data.hasOwnCanvas) {
container.style.width = container.style.height = "auto";
} else {
container.style.width = `${width}px`;
container.style.height = `${height}px`;
}
return container; return container;
} }
@ -464,7 +436,7 @@ class AnnotationElement {
const popup = popupElement.render(); const popup = popupElement.render();
// Position the popup next to the annotation's container. // Position the popup next to the annotation's container.
popup.style.left = container.style.width; popup.style.left = "100%";
container.append(popup); container.append(popup);
} }
@ -820,8 +792,6 @@ class TextAnnotationElement extends AnnotationElement {
this.container.className = "textAnnotation"; this.container.className = "textAnnotation";
const image = document.createElement("img"); const image = document.createElement("img");
image.style.height = this.container.style.height;
image.style.width = this.container.style.width;
image.src = image.src =
this.imageResourcesPath + this.imageResourcesPath +
"annotation-" + "annotation-" +
@ -925,21 +895,20 @@ class WidgetAnnotationElement extends AnnotationElement {
// it's instead based on the field height. // it's instead based on the field height.
// If the height is "big" then it could lead to a too big font size // If the height is "big" then it could lead to a too big font size
// so in this case use the one we've in the pdf (hence the min). // so in this case use the one we've in the pdf (hence the min).
let computedFontSize;
if (this.data.multiLine) { if (this.data.multiLine) {
const height = Math.abs(this.data.rect[3] - this.data.rect[1]); const height = Math.abs(this.data.rect[3] - this.data.rect[1]);
const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1; const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1;
const lineHeight = height / numberOfLines; const lineHeight = height / numberOfLines;
style.fontSize = `${Math.min( computedFontSize = Math.min(
fontSize, fontSize,
Math.round(lineHeight / LINE_FACTOR) Math.round(lineHeight / LINE_FACTOR)
)}px`; );
} else { } else {
const height = Math.abs(this.data.rect[3] - this.data.rect[1]); const height = Math.abs(this.data.rect[3] - this.data.rect[1]);
style.fontSize = `${Math.min( computedFontSize = Math.min(fontSize, Math.round(height / LINE_FACTOR));
fontSize,
Math.round(height / LINE_FACTOR)
)}px`;
} }
style.fontSize = `${computedFontSize}%`;
style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]);
@ -1227,7 +1196,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
const combWidth = fieldWidth / this.data.maxLen; const combWidth = fieldWidth / this.data.maxLen;
element.classList.add("comb"); element.classList.add("comb");
element.style.letterSpacing = `calc(${combWidth}px - 1ch)`; element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`;
} }
} else { } else {
element = document.createElement("div"); element = document.createElement("div");
@ -1464,10 +1433,6 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
value: this.data.fieldValue, value: this.data.fieldValue,
}); });
const fontSize =
this.data.defaultAppearanceData.fontSize || DEFAULT_FONT_SIZE;
const fontSizeStyle = `calc(${fontSize}px * var(--zoom-factor))`;
const selectElement = document.createElement("select"); const selectElement = document.createElement("select");
GetElementsByNameSet.add(selectElement); GetElementsByNameSet.add(selectElement);
selectElement.setAttribute("data-element-id", id); selectElement.setAttribute("data-element-id", id);
@ -1499,9 +1464,6 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
const optionElement = document.createElement("option"); const optionElement = document.createElement("option");
optionElement.textContent = option.displayValue; optionElement.textContent = option.displayValue;
optionElement.value = option.exportValue; optionElement.value = option.exportValue;
if (this.data.combo) {
optionElement.style.fontSize = fontSizeStyle;
}
if (storedData.value.includes(option.exportValue)) { if (storedData.value.includes(option.exportValue)) {
optionElement.setAttribute("selected", true); optionElement.setAttribute("selected", true);
addAnEmptyEntry = false; addAnEmptyEntry = false;
@ -1749,9 +1711,12 @@ class PopupAnnotationElement extends AnnotationElement {
rect[0] + this.data.parentRect[2] - this.data.parentRect[0]; rect[0] + this.data.parentRect[2] - this.data.parentRect[0];
const popupTop = rect[1]; const popupTop = rect[1];
this.container.style.transformOrigin = `${-popupLeft}px ${-popupTop}px`; const [pageLLx, pageLLy, pageURx, pageURy] = this.viewport.viewBox;
this.container.style.left = `${popupLeft}px`; const pageWidth = pageURx - pageLLx;
this.container.style.top = `${popupTop}px`; const pageHeight = pageURy - pageLLy;
this.container.style.left = `${(100 * (popupLeft - pageLLx)) / pageWidth}%`;
this.container.style.top = `${(100 * (popupTop - pageLLy)) / pageHeight}%`;
this.container.append(popup.render()); this.container.append(popup.render());
return this.container; return this.container;
@ -1961,7 +1926,11 @@ class LineAnnotationElement extends AnnotationElement {
// trigger the popup, not the entire container. // trigger the popup, not the entire container.
const data = this.data; const data = this.data;
const { width, height } = getRectDims(data.rect); const { width, height } = getRectDims(data.rect);
const svg = this.svgFactory.create(width, height); const svg = this.svgFactory.create(
width,
height,
/* skipDimensions = */ true
);
// PDF coordinates are calculated from a bottom left origin, so transform // PDF coordinates are calculated from a bottom left origin, so transform
// the line coordinates to a top left origin for the SVG element. // the line coordinates to a top left origin for the SVG element.
@ -2006,7 +1975,11 @@ class SquareAnnotationElement extends AnnotationElement {
// popup, not the entire container. // popup, not the entire container.
const data = this.data; const data = this.data;
const { width, height } = getRectDims(data.rect); const { width, height } = getRectDims(data.rect);
const svg = this.svgFactory.create(width, height); const svg = this.svgFactory.create(
width,
height,
/* skipDimensions = */ true
);
// The browser draws half of the borders inside the square and half of // The browser draws half of the borders inside the square and half of
// the borders outside the square by default. This behavior cannot be // the borders outside the square by default. This behavior cannot be
@ -2053,7 +2026,11 @@ class CircleAnnotationElement extends AnnotationElement {
// popup, not the entire container. // popup, not the entire container.
const data = this.data; const data = this.data;
const { width, height } = getRectDims(data.rect); const { width, height } = getRectDims(data.rect);
const svg = this.svgFactory.create(width, height); const svg = this.svgFactory.create(
width,
height,
/* skipDimensions = */ true
);
// The browser draws half of the borders inside the circle and half of // The browser draws half of the borders inside the circle and half of
// the borders outside the circle by default. This behavior cannot be // the borders outside the circle by default. This behavior cannot be
@ -2103,7 +2080,11 @@ class PolylineAnnotationElement extends AnnotationElement {
// popup, not the entire container. // popup, not the entire container.
const data = this.data; const data = this.data;
const { width, height } = getRectDims(data.rect); const { width, height } = getRectDims(data.rect);
const svg = this.svgFactory.create(width, height); const svg = this.svgFactory.create(
width,
height,
/* skipDimensions = */ true
);
// Convert the vertices array to a single points string that the SVG // Convert the vertices array to a single points string that the SVG
// polyline element expects ("x1,y1 x2,y2 ..."). PDF coordinates are // polyline element expects ("x1,y1 x2,y2 ..."). PDF coordinates are
@ -2191,7 +2172,11 @@ class InkAnnotationElement extends AnnotationElement {
// trigger for the popup. // trigger for the popup.
const data = this.data; const data = this.data;
const { width, height } = getRectDims(data.rect); const { width, height } = getRectDims(data.rect);
const svg = this.svgFactory.create(width, height); const svg = this.svgFactory.create(
width,
height,
/* skipDimensions = */ true
);
for (const inkList of data.inkLists) { for (const inkList of data.inkLists) {
// Convert the ink list to a single points string that the SVG // Convert the ink list to a single points string that the SVG
@ -2515,55 +2500,27 @@ class AnnotationLayer {
* @memberof AnnotationLayer * @memberof AnnotationLayer
*/ */
static update(parameters) { static update(parameters) {
const { page, viewport, annotations, annotationCanvasMap, div } = const { annotationCanvasMap, div } = parameters;
parameters;
const transform = viewport.transform;
const matrix = `matrix(${transform.join(",")})`;
let scale, ownMatrix;
for (const data of annotations) {
const elements = div.querySelectorAll(
`[data-annotation-id="${data.id}"]`
);
if (elements) {
for (const element of elements) {
if (data.hasOwnCanvas) {
const rect = Util.normalizeRect([
data.rect[0],
page.view[3] - data.rect[1] + page.view[1],
data.rect[2],
page.view[3] - data.rect[3] + page.view[1],
]);
if (!ownMatrix) {
// When an annotation has its own canvas, then
// the scale has been already applied to the canvas,
// so we musn't scale it twice.
scale = Math.abs(transform[0] || transform[1]);
const ownTransform = transform.slice();
for (let i = 0; i < 4; i++) {
ownTransform[i] = Math.sign(ownTransform[i]);
}
ownMatrix = `matrix(${ownTransform.join(",")})`;
}
const left = rect[0] * scale;
const top = rect[1] * scale;
element.style.left = `${left}px`;
element.style.top = `${top}px`;
element.style.transformOrigin = `${-left}px ${-top}px`;
element.style.transform = ownMatrix;
} else {
element.style.transform = matrix;
}
}
}
}
this.#setAnnotationCanvasMap(div, annotationCanvasMap); this.#setAnnotationCanvasMap(div, annotationCanvasMap);
div.hidden = false; div.hidden = false;
} }
static setDimensions(div, viewport) {
const { width, height, rotation } = viewport;
const { style } = div;
if (rotation === 0 || rotation === 180) {
style.width = `${width}px`;
style.height = `${height}px`;
} else {
style.width = `${height}px`;
style.height = `${width}px`;
}
div.setAttribute("data-annotation-rotation", rotation);
}
static #setAnnotationCanvasMap(div, annotationCanvasMap) { static #setAnnotationCanvasMap(div, annotationCanvasMap) {
if (!annotationCanvasMap) { if (!annotationCanvasMap) {
return; return;

View File

@ -143,14 +143,18 @@ class BaseSVGFactory {
} }
} }
create(width, height) { create(width, height, skipDimensions = false) {
if (width <= 0 || height <= 0) { if (width <= 0 || height <= 0) {
throw new Error("Invalid SVG dimensions"); throw new Error("Invalid SVG dimensions");
} }
const svg = this._createSVG("svg:svg"); const svg = this._createSVG("svg:svg");
svg.setAttribute("version", "1.1"); svg.setAttribute("version", "1.1");
svg.setAttribute("width", `${width}px`);
svg.setAttribute("height", `${height}px`); if (!skipDimensions) {
svg.setAttribute("width", `${width}px`);
svg.setAttribute("height", `${height}px`);
}
svg.setAttribute("preserveAspectRatio", "none"); svg.setAttribute("preserveAspectRatio", "none");
svg.setAttribute("viewBox", `0 0 ${width} ${height}`); svg.setAttribute("viewBox", `0 0 ${width} ${height}`);

View File

@ -3021,9 +3021,6 @@ class CanvasGraphics {
canvasHeight canvasHeight
); );
const { canvas, context } = this.annotationCanvas; const { canvas, context } = this.annotationCanvas;
const viewportScaleFactorStr = `var(--zoom-factor) * ${PixelsPerInch.PDF_TO_CSS_UNITS}`;
canvas.style.width = `calc(${width}px * ${viewportScaleFactorStr})`;
canvas.style.height = `calc(${height}px * ${viewportScaleFactorStr})`;
this.annotationCanvasMap.set(id, canvas); this.annotationCanvasMap.set(id, canvas);
this.annotationCanvas.savedCtx = this.ctx; this.annotationCanvas.savedCtx = this.ctx;
this.ctx = context; this.ctx = context;

View File

@ -24,7 +24,6 @@ import { AnnotationEditorType, Util } from "../../shared/util.js";
import { bindEvents, KeyboardManager } from "./tools.js"; import { bindEvents, KeyboardManager } from "./tools.js";
import { FreeTextEditor } from "./freetext.js"; import { FreeTextEditor } from "./freetext.js";
import { InkEditor } from "./ink.js"; import { InkEditor } from "./ink.js";
import { PixelsPerInch } from "../display_utils.js";
/** /**
* @typedef {Object} AnnotationEditorLayerOptions * @typedef {Object} AnnotationEditorLayerOptions
@ -492,14 +491,6 @@ class AnnotationEditorLayer {
get scaleFactor() { get scaleFactor() {
return this.viewport.scale; return this.viewport.scale;
} }
/**
* Get the zoom factor.
* @returns {number}
*/
get zoomFactor() {
return this.viewport.scale / PixelsPerInch.PDF_TO_CSS_UNITS;
}
} }
export { AnnotationEditorLayer }; export { AnnotationEditorLayer };

View File

@ -21,7 +21,6 @@ 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 { PixelsPerInch } from "../display_utils.js";
/** /**
* Basic text editor in order to create a FreeTex annotation. * Basic text editor in order to create a FreeTex annotation.
@ -94,9 +93,9 @@ class FreeTextEditor extends AnnotationEditor {
getInitialTranslation() { getInitialTranslation() {
// The start of the base line is where the user clicked. // The start of the base line is where the user clicked.
return [ return [
-FreeTextEditor._internalPadding * this.parent.zoomFactor, -FreeTextEditor._internalPadding * this.parent.scaleFactor,
-(FreeTextEditor._internalPadding + this.#fontSize) * -(FreeTextEditor._internalPadding + this.#fontSize) *
this.parent.zoomFactor, this.parent.scaleFactor,
]; ];
} }
@ -217,7 +216,7 @@ class FreeTextEditor extends AnnotationEditor {
this.editorDiv.contentEditable = true; this.editorDiv.contentEditable = true;
const { style } = this.editorDiv; const { style } = this.editorDiv;
style.fontSize = `calc(${this.#fontSize}px * var(--zoom-factor))`; style.fontSize = `${this.#fontSize}%`;
style.color = this.#color; style.color = this.#color;
this.div.append(this.editorDiv); this.div.append(this.editorDiv);
@ -244,7 +243,7 @@ class FreeTextEditor extends AnnotationEditor {
/** @inheritdoc */ /** @inheritdoc */
serialize() { serialize() {
const rect = this.editorDiv.getBoundingClientRect(); const rect = this.editorDiv.getBoundingClientRect();
const padding = FreeTextEditor._internalPadding * this.parent.zoomFactor; const padding = FreeTextEditor._internalPadding * this.parent.scaleFactor;
const [x1, y1] = Util.applyTransform( const [x1, y1] = Util.applyTransform(
[this.x + padding, this.y + padding + rect.height], [this.x + padding, this.y + padding + rect.height],
this.parent.inverseViewportTransform this.parent.inverseViewportTransform
@ -257,7 +256,7 @@ class FreeTextEditor extends AnnotationEditor {
return { return {
annotationType: AnnotationEditorType.FREETEXT, annotationType: AnnotationEditorType.FREETEXT,
color: [0, 0, 0], color: [0, 0, 0],
fontSize: this.#fontSize / PixelsPerInch.PDF_TO_CSS_UNITS, fontSize: this.#fontSize,
value: this.#content, value: this.#content,
pageIndex: this.parent.pageIndex, pageIndex: this.parent.pageIndex,
rect: [x1, y1, x2, y2], rect: [x1, y1, x2, y2],

View File

@ -17,6 +17,13 @@
.annotationLayer { .annotationLayer {
position: absolute; position: absolute;
font-size: calc(100px * var(--scale-factor));
}
.annotationLayer .buttonWidgetAnnotation.pushButton > img {
width: 100%;
height: 100%;
position: absolute;
} }
.annotationLayer .buttonWidgetAnnotation.checkBox input, .annotationLayer .buttonWidgetAnnotation.checkBox input,

View File

@ -215,7 +215,7 @@ class Rasterize {
div.className = "annotationLayer"; div.className = "annotationLayer";
const [common, overrides] = await this.annotationStylePromise; const [common, overrides] = await this.annotationStylePromise;
style.textContent = `${common}\n${overrides}`; style.textContent = `:root { --scale-factor: ${viewport.scale} } ${common}\n${overrides}`;
const annotationViewport = viewport.clone({ dontFlip: true }); const annotationViewport = viewport.clone({ dontFlip: true });
const annotationImageMap = await convertCanvasesToImages( const annotationImageMap = await convertCanvasesToImages(
@ -234,6 +234,7 @@ class Rasterize {
renderForms, renderForms,
annotationCanvasMap: annotationImageMap, annotationCanvasMap: annotationImageMap,
}; };
AnnotationLayer.setDimensions(div, annotationViewport);
AnnotationLayer.render(parameters); AnnotationLayer.render(parameters);
// Inline SVG images from text annotations. // Inline SVG images from text annotations.

View File

@ -42,7 +42,7 @@ describe("Editor", () => {
}); });
const data = "Hello PDF.js World !!"; const data = "Hello PDF.js World !!";
await page.mouse.click(rect.x + 10, rect.y + 10); await page.mouse.click(rect.x + 100, rect.y + 100);
await page.type(`${editorPrefix}0 .internal`, data); await page.type(`${editorPrefix}0 .internal`, data);
const editorRect = await page.$eval(`${editorPrefix}0`, el => { const editorRect = await page.$eval(`${editorPrefix}0`, el => {
@ -151,7 +151,7 @@ describe("Editor", () => {
}); });
const data = "Hello PDF.js World !!"; const data = "Hello PDF.js World !!";
await page.mouse.click(rect.x + 10, rect.y + 10); await page.mouse.click(rect.x + 100, rect.y + 100);
await page.type(`${editorPrefix}5 .internal`, data); await page.type(`${editorPrefix}5 .internal`, data);
const editorRect = await page.$eval(`${editorPrefix}5`, el => { const editorRect = await page.$eval(`${editorPrefix}5`, el => {

View File

@ -27,13 +27,14 @@
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
font-size: calc(100px * var(--scale-factor));
} }
.annotationEditorLayer .freeTextEditor { .annotationEditorLayer .freeTextEditor {
position: absolute; position: absolute;
background: transparent; background: transparent;
border-radius: 3px; border-radius: 3px;
padding: calc(var(--freetext-padding) * var(--zoom-factor)); padding: calc(var(--freetext-padding) * var(--scale-factor));
resize: none; resize: none;
width: auto; width: auto;
height: auto; height: auto;

View File

@ -27,9 +27,30 @@
} }
} }
[data-annotation-rotation="90"] {
transform: rotate(90deg) translateY(-100%);
}
[data-annotation-rotation="180"] {
transform: rotate(180deg) translate(-100%, -100%);
}
[data-annotation-rotation="270"] {
transform: rotate(270deg) translateX(-100%);
}
.annotationLayer {
position: absolute;
top: 0;
left: 0;
font-size: calc(100px * var(--scale-factor));
pointer-events: none;
transform-origin: 0 0;
}
.annotationLayer section { .annotationLayer section {
position: absolute; position: absolute;
text-align: initial; text-align: initial;
pointer-events: auto;
box-sizing: border-box;
} }
.annotationLayer .linkAnnotation > a, .annotationLayer .linkAnnotation > a,
@ -43,10 +64,8 @@
} }
.annotationLayer .buttonWidgetAnnotation.pushButton > canvas { .annotationLayer .buttonWidgetAnnotation.pushButton > canvas {
position: relative; width: 100%;
top: 0; height: 100%;
left: 0;
z-index: -1;
} }
.annotationLayer .linkAnnotation > a:hover, .annotationLayer .linkAnnotation > a:hover,
@ -59,6 +78,8 @@
.annotationLayer .textAnnotation img { .annotationLayer .textAnnotation img {
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
width: 100%;
height: 100%;
} }
.annotationLayer .textWidgetAnnotation input, .annotationLayer .textWidgetAnnotation input,
@ -69,7 +90,7 @@
background-image: var(--annotation-unfocused-field-background); background-image: var(--annotation-unfocused-field-background);
border: 1px solid transparent; border: 1px solid transparent;
box-sizing: border-box; box-sizing: border-box;
font: 9px sans-serif; font: 9% sans-serif;
height: 100%; height: 100%;
margin: 0; margin: 0;
vertical-align: top; vertical-align: top;
@ -186,27 +207,31 @@
.annotationLayer .popupWrapper { .annotationLayer .popupWrapper {
position: absolute; position: absolute;
width: 20em; font-size: calc(9px * var(--scale-factor));
width: 100%;
min-width: calc(180px * var(--scale-factor));
pointer-events: none;
} }
.annotationLayer .popup { .annotationLayer .popup {
position: absolute; position: absolute;
z-index: 200; z-index: 200;
max-width: 20em; max-width: calc(180px * var(--scale-factor));
background-color: rgba(255, 255, 153, 1); background-color: rgba(255, 255, 153, 1);
box-shadow: 0 2px 5px rgba(136, 136, 136, 1); box-shadow: 0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor))
border-radius: 2px; rgba(136, 136, 136, 1);
padding: 6px; border-radius: calc(2px * var(--scale-factor));
margin-left: 5px; padding: calc(6px * var(--scale-factor));
margin-left: calc(5px * var(--scale-factor));
cursor: pointer; cursor: pointer;
font: message-box; font: message-box;
font-size: 9px;
white-space: normal; white-space: normal;
word-wrap: break-word; word-wrap: break-word;
pointer-events: auto;
} }
.annotationLayer .popup > * { .annotationLayer .popup > * {
font-size: 9px; font-size: calc(9px * var(--scale-factor));
} }
.annotationLayer .popup h1 { .annotationLayer .popup h1 {
@ -215,17 +240,18 @@
.annotationLayer .popupDate { .annotationLayer .popupDate {
display: inline-block; display: inline-block;
margin-left: 5px; margin-left: calc(5px * var(--scale-factor));
} }
.annotationLayer .popupContent { .annotationLayer .popupContent {
border-top: 1px solid rgba(51, 51, 51, 1); border-top: 1px solid rgba(51, 51, 51, 1);
margin-top: 2px; margin-top: calc(2px * var(--scale-factor));
padding-top: 2px; padding-top: calc(2px * var(--scale-factor));
} }
.annotationLayer .richText > * { .annotationLayer .richText > * {
white-space: pre-wrap; white-space: pre-wrap;
font-size: calc(9px * var(--scale-factor));
} }
.annotationLayer .highlightAnnotation, .annotationLayer .highlightAnnotation,
@ -244,3 +270,9 @@
.annotationLayer .fileAttachmentAnnotation { .annotationLayer .fileAttachmentAnnotation {
cursor: pointer; cursor: pointer;
} }
.annotationLayer section svg {
position: absolute;
width: 100%;
height: 100%;
}

View File

@ -117,11 +117,14 @@ class AnnotationLayerBuilder {
if (this.div) { if (this.div) {
// If an annotationLayer already exists, refresh its children's // If an annotationLayer already exists, refresh its children's
// transformation matrices. // transformation matrices.
AnnotationLayer.setDimensions(this.div, viewport);
AnnotationLayer.update(parameters); AnnotationLayer.update(parameters);
} else { } else {
// Create an annotation layer div and render the annotations // Create an annotation layer div and render the annotations
// if there is at least one annotation. // if there is at least one annotation.
this.div = document.createElement("div"); this.div = document.createElement("div");
AnnotationLayer.setDimensions(this.div, viewport);
this.div.className = "annotationLayer"; this.div.className = "annotationLayer";
this.pageDiv.append(this.div); this.pageDiv.append(this.div);
parameters.div = this.div; parameters.div = this.div;

View File

@ -1048,7 +1048,10 @@ class BaseViewer {
return; return;
} }
docStyle.setProperty("--zoom-factor", newScale); docStyle.setProperty(
"--scale-factor",
newScale * PixelsPerInch.PDF_TO_CSS_UNITS
);
const updateArgs = { scale: newScale }; const updateArgs = { scale: newScale };
for (const pageView of this._pages) { for (const pageView of this._pages) {

View File

@ -377,7 +377,7 @@ class PDFPageView {
}); });
if (this._isStandalone) { if (this._isStandalone) {
docStyle.setProperty("--zoom-factor", this.scale); docStyle.setProperty("--scale-factor", this.viewport.scale);
} }
if (this.svg) { if (this.svg) {

View File

@ -23,7 +23,7 @@
--page-margin: 1px auto -8px; --page-margin: 1px auto -8px;
--page-border: 9px solid transparent; --page-border: 9px solid transparent;
--spreadHorizontalWrapped-margin-LR: -3.5px; --spreadHorizontalWrapped-margin-LR: -3.5px;
--zoom-factor: 1; --scale-factor: 1;
} }
@media screen and (forced-colors: active) { @media screen and (forced-colors: active) {