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:
parent
6dc8d1f532
commit
e2db9bacef
@ -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");
|
||||||
|
@ -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 = [];
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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}`);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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 };
|
||||||
|
@ -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],
|
||||||
|
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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 => {
|
||||||
|
@ -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;
|
||||||
|
@ -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%;
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user