Skip to content

Commit

Permalink
Get rid of CSS transform on each annotation in the annotation layer
Browse files Browse the repository at this point in the history
- 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).
  • Loading branch information
calixteman committed Jun 13, 2022
1 parent 89cebcb commit 333a597
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 116 deletions.
6 changes: 3 additions & 3 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ class Annotation {
this.setAppearance(dict);
this.setBorderAndBackgroundColors(dict.get("MK"));

this._hasOwnCanvas = false;
this._streams = [];
if (this.appearance) {
this._streams.push(this.appearance);
Expand All @@ -450,7 +451,6 @@ class Annotation {
modificationDate: this.modificationDate,
rect: this.rectangle,
subtype: params.subtype,
hasOwnCanvas: false,
};

if (params.collectFields) {
Expand Down Expand Up @@ -849,7 +849,7 @@ class Annotation {
const data = this.data;
let appearance = this.appearance;
const isUsingOwnCanvas =
data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY;
this._hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY;
if (!appearance) {
if (!isUsingOwnCanvas) {
return Promise.resolve(new OperatorList());
Expand Down Expand Up @@ -2163,7 +2163,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
} else if (this.data.radioButton) {
this._processRadioButton(params);
} else if (this.data.pushButton) {
this.data.hasOwnCanvas = true;
this._hasOwnCanvas = true;
this._processPushButton(params);
} else {
warn("Invalid field flags for button widget annotation");
Expand Down
2 changes: 1 addition & 1 deletion src/core/xfa/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -1425,7 +1425,7 @@ class ChoiceList extends XFAObject {
const field = ui[$getParent]();
const fontSize = (field.font && field.font.size) || 10;
const optionStyle = {
fontSize: `calc(${fontSize}px * var(--zoom-factor))`,
fontSize: `calc(${fontSize}px * var(--scale-factor))`,
};
const children = [];

Expand Down
4 changes: 4 additions & 0 deletions src/core/xfa/xhtml.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ function mapStyle(styleStr, node, richText) {
);
}

if (richText && style.fontSize) {
style.fontSize = `calc(${style.fontSize} * var(--scale-factor))`;
}

fixTextIndent(style);
return style;
}
Expand Down
111 changes: 21 additions & 90 deletions src/display/annotation_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ class AnnotationElement {
const container = document.createElement("section");
let { 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);

// Do *not* modify `data.rect`, since that will corrupt the annotation
Expand All @@ -210,27 +214,6 @@ class AnnotationElement {
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) {
container.style.borderWidth = `${data.borderStyle.width}px`;
if (data.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) {
Expand Down Expand Up @@ -286,15 +269,11 @@ class AnnotationElement {
}
}

container.style.left = `${rect[0]}px`;
container.style.top = `${rect[1]}px`;
container.style.left = `${(100 * (rect[0] - pageLLx)) / pageWidth}%`;
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;
}

Expand Down Expand Up @@ -918,21 +897,20 @@ class WidgetAnnotationElement extends AnnotationElement {
// it's instead based on the field height.
// 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).
let computedFontSize;
if (this.data.multiLine) {
const height = Math.abs(this.data.rect[3] - this.data.rect[1]);
const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1;
const lineHeight = height / numberOfLines;
style.fontSize = `${Math.min(
computedFontSize = Math.min(
fontSize,
Math.round(lineHeight / LINE_FACTOR)
)}px`;
);
} else {
const height = Math.abs(this.data.rect[3] - this.data.rect[1]);
style.fontSize = `${Math.min(
fontSize,
Math.round(height / LINE_FACTOR)
)}px`;
computedFontSize = Math.min(fontSize, Math.round(height / LINE_FACTOR));
}
style.fontSize = `${computedFontSize}%`;

style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]);

Expand Down Expand Up @@ -1221,7 +1199,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
const combWidth = fieldWidth / this.data.maxLen;

element.classList.add("comb");
element.style.letterSpacing = `calc(${combWidth}px - 1ch)`;
element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`;
}
} else {
element = document.createElement("div");
Expand Down Expand Up @@ -1449,10 +1427,6 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
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");
GetElementsByNameSet.add(selectElement);
selectElement.disabled = this.data.readOnly;
Expand Down Expand Up @@ -1483,9 +1457,6 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
const optionElement = document.createElement("option");
optionElement.textContent = option.displayValue;
optionElement.value = option.exportValue;
if (this.data.combo) {
optionElement.style.fontSize = fontSizeStyle;
}
if (storedData.value.includes(option.exportValue)) {
optionElement.setAttribute("selected", true);
addAnEmptyEntry = false;
Expand Down Expand Up @@ -1730,9 +1701,12 @@ class PopupAnnotationElement extends AnnotationElement {
rect[0] + this.data.parentRect[2] - this.data.parentRect[0];
const popupTop = rect[1];

this.container.style.transformOrigin = `${-popupLeft}px ${-popupTop}px`;
this.container.style.left = `${popupLeft}px`;
this.container.style.top = `${popupTop}px`;
const [pageLLx, pageLLy, pageURx, pageURy] = this.viewport.viewBox;
const pageWidth = pageURx - pageLLx;
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());
return this.container;
Expand Down Expand Up @@ -2496,50 +2470,7 @@ class AnnotationLayer {
* @memberof AnnotationLayer
*/
static update(parameters) {
const { page, viewport, annotations, annotationCanvasMap, div } =
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;
}
}
}
}
const { annotationCanvasMap, div } = parameters;

this.#setAnnotationCanvasMap(div, annotationCanvasMap);
div.hidden = false;
Expand Down
3 changes: 0 additions & 3 deletions src/display/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -3021,9 +3021,6 @@ class CanvasGraphics {
canvasHeight
);
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.annotationCanvas.savedCtx = this.ctx;
this.ctx = context;
Expand Down
11 changes: 5 additions & 6 deletions src/display/editor/freetext.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
} from "../../shared/util.js";
import { AnnotationEditor } from "./editor.js";
import { bindEvents } from "./tools.js";
import { PixelsPerInch } from "../display_utils.js";

/**
* Basic text editor in order to create a FreeTex annotation.
Expand Down Expand Up @@ -92,9 +91,9 @@ class FreeTextEditor extends AnnotationEditor {
getInitialTranslation() {
// The start of the base line is where the user clicked.
return [
-FreeTextEditor._internalPadding * this.parent.zoomFactor,
-FreeTextEditor._internalPadding * this.parent.scaleFactor,
-(FreeTextEditor._internalPadding + this.#fontSize) *
this.parent.zoomFactor,
this.parent.scaleFactor,
];
}

Expand Down Expand Up @@ -208,7 +207,7 @@ class FreeTextEditor extends AnnotationEditor {
this.editorDiv.contentEditable = true;

const { style } = this.editorDiv;
style.fontSize = `calc(${this.#fontSize}px * var(--zoom-factor))`;
style.fontSize = `${this.#fontSize}%`;
style.color = this.#color;

this.div.append(this.editorDiv);
Expand All @@ -235,7 +234,7 @@ class FreeTextEditor extends AnnotationEditor {
/** @inheritdoc */
serialize() {
const rect = this.editorDiv.getBoundingClientRect();
const padding = FreeTextEditor._internalPadding * this.parent.zoomFactor;
const padding = FreeTextEditor._internalPadding * this.parent.scaleFactor;
const [x1, y1] = Util.applyTransform(
[this.x + padding, this.y + padding + rect.height],
this.parent.inverseViewportTransform
Expand All @@ -248,7 +247,7 @@ class FreeTextEditor extends AnnotationEditor {
return {
annotationType: AnnotationEditorType.FREETEXT,
color: [0, 0, 0],
fontSize: this.#fontSize / PixelsPerInch.PDF_TO_CSS_UNITS,
fontSize: this.#fontSize,
value: this.#content,
pageIndex: this.parent.pageIndex,
rect: [x1, y1, x2, y2],
Expand Down
3 changes: 2 additions & 1 deletion web/annotation_editor_layer_builder.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@
position: absolute;
top: 0;
left: 0;
font-size: calc(100px * var(--scale-factor));
}

.annotationEditorLayer .freeTextEditor {
position: absolute;
background: transparent;
border-radius: 3px;
padding: calc(var(--freetext-padding) * var(--zoom-factor));
padding: calc(var(--freetext-padding) * var(--scale-factor));
resize: none;
width: auto;
height: auto;
Expand Down
32 changes: 24 additions & 8 deletions web/annotation_layer_builder.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,29 @@
}
}

.rotate90 {
transform: rotate(90deg) translateY(-100%);
}
.rotate180 {
transform: rotate(180deg) translate(-100%, -100%);
}
.rotate270 {
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 {
position: absolute;
text-align: initial;
pointer-events: auto;
}

.annotationLayer .linkAnnotation > a,
Expand All @@ -43,9 +63,8 @@
}

.annotationLayer .buttonWidgetAnnotation.pushButton > canvas {
position: relative;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}

Expand Down Expand Up @@ -200,15 +219,11 @@
margin-left: 5px;
cursor: pointer;
font: message-box;
font-size: 9px;
font-size: 9%;
white-space: normal;
word-wrap: break-word;
}

.annotationLayer .popup > * {
font-size: 9px;
}

.annotationLayer .popup h1 {
display: inline-block;
}
Expand All @@ -226,6 +241,7 @@

.annotationLayer .richText > * {
white-space: pre-wrap;
font-size: calc(9px * var(--scale-factor));
}

.annotationLayer .highlightAnnotation,
Expand Down
Loading

0 comments on commit 333a597

Please sign in to comment.