Merge pull request #16492 from calixteman/clip_path
[Annotation] Use the clip-path property when an annotation has some quad points
This commit is contained in:
commit
0702663b7d
@ -22,7 +22,6 @@ import {
|
|||||||
AnnotationBorderStyleType,
|
AnnotationBorderStyleType,
|
||||||
AnnotationEditorType,
|
AnnotationEditorType,
|
||||||
AnnotationType,
|
AnnotationType,
|
||||||
assert,
|
|
||||||
FeatureTest,
|
FeatureTest,
|
||||||
LINE_FACTOR,
|
LINE_FACTOR,
|
||||||
shadow,
|
shadow,
|
||||||
@ -156,6 +155,8 @@ class AnnotationElementFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AnnotationElement {
|
class AnnotationElement {
|
||||||
|
#hasBorder = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
parameters,
|
parameters,
|
||||||
{
|
{
|
||||||
@ -182,7 +183,7 @@ class AnnotationElement {
|
|||||||
this.container = this._createContainer(ignoreBorder);
|
this.container = this._createContainer(ignoreBorder);
|
||||||
}
|
}
|
||||||
if (createQuadrilaterals) {
|
if (createQuadrilaterals) {
|
||||||
this.quadrilaterals = this._createQuadrilaterals(ignoreBorder);
|
this._createQuadrilaterals();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +280,7 @@ class AnnotationElement {
|
|||||||
|
|
||||||
const borderColor = data.borderColor || null;
|
const borderColor = data.borderColor || null;
|
||||||
if (borderColor) {
|
if (borderColor) {
|
||||||
|
this.#hasBorder = true;
|
||||||
container.style.borderColor = Util.makeHexColor(
|
container.style.borderColor = Util.makeHexColor(
|
||||||
borderColor[0] | 0,
|
borderColor[0] | 0,
|
||||||
borderColor[1] | 0,
|
borderColor[1] | 0,
|
||||||
@ -441,31 +443,90 @@ class AnnotationElement {
|
|||||||
* Create quadrilaterals from the annotation's quadpoints.
|
* Create quadrilaterals from the annotation's quadpoints.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {boolean} ignoreBorder
|
|
||||||
* @memberof AnnotationElement
|
* @memberof AnnotationElement
|
||||||
* @returns {Array<HTMLElement>} An array of section elements.
|
|
||||||
*/
|
*/
|
||||||
_createQuadrilaterals(ignoreBorder = false) {
|
_createQuadrilaterals() {
|
||||||
if (!this.data.quadPoints) {
|
if (!this.container) {
|
||||||
return null;
|
return;
|
||||||
|
}
|
||||||
|
const { quadPoints } = this.data;
|
||||||
|
if (!quadPoints) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const quadrilaterals = [];
|
const [rectBlX, rectBlY, rectTrX, rectTrY] = this.data.rect;
|
||||||
const savedRect = this.data.rect;
|
|
||||||
let firstQuadRect = null;
|
if (quadPoints.length === 1) {
|
||||||
for (const quadPoint of this.data.quadPoints) {
|
const [, { x: trX, y: trY }, { x: blX, y: blY }] = quadPoints[0];
|
||||||
this.data.rect = [
|
if (
|
||||||
quadPoint[2].x,
|
rectTrX === trX &&
|
||||||
quadPoint[2].y,
|
rectTrY === trY &&
|
||||||
quadPoint[1].x,
|
rectBlX === blX &&
|
||||||
quadPoint[1].y,
|
rectBlY === blY
|
||||||
];
|
) {
|
||||||
quadrilaterals.push(this._createContainer(ignoreBorder));
|
// The quadpoints cover the whole annotation rectangle, so no need to
|
||||||
firstQuadRect ||= this.data.rect;
|
// create a quadrilateral.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.data.rect = savedRect;
|
|
||||||
this.firstQuadRect = firstQuadRect;
|
const { style } = this.container;
|
||||||
return quadrilaterals;
|
let svgBuffer;
|
||||||
|
if (this.#hasBorder) {
|
||||||
|
const { borderColor, borderWidth } = style;
|
||||||
|
style.borderWidth = 0;
|
||||||
|
svgBuffer = [
|
||||||
|
"url('data:image/svg+xml;utf8,",
|
||||||
|
`<svg xmlns="http://www.w3.org/2000/svg"`,
|
||||||
|
` preserveAspectRatio="none" viewBox="0 0 1 1">`,
|
||||||
|
`<g fill="transparent" stroke="${borderColor}" stroke-width="${borderWidth}">`,
|
||||||
|
];
|
||||||
|
this.container.classList.add("hasBorder");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) {
|
||||||
|
this.container.classList.add("hasClipPath");
|
||||||
|
}
|
||||||
|
|
||||||
|
const width = rectTrX - rectBlX;
|
||||||
|
const height = rectTrY - rectBlY;
|
||||||
|
|
||||||
|
const { svgFactory } = this;
|
||||||
|
const svg = svgFactory.createElement("svg");
|
||||||
|
svg.classList.add("quadrilateralsContainer");
|
||||||
|
svg.setAttribute("width", 0);
|
||||||
|
svg.setAttribute("height", 0);
|
||||||
|
const defs = svgFactory.createElement("defs");
|
||||||
|
svg.append(defs);
|
||||||
|
const clipPath = svgFactory.createElement("clipPath");
|
||||||
|
const id = `clippath_${this.data.id}`;
|
||||||
|
clipPath.setAttribute("id", id);
|
||||||
|
clipPath.setAttribute("clipPathUnits", "objectBoundingBox");
|
||||||
|
defs.append(clipPath);
|
||||||
|
|
||||||
|
for (const [, { x: trX, y: trY }, { x: blX, y: blY }] of quadPoints) {
|
||||||
|
const rect = svgFactory.createElement("rect");
|
||||||
|
const x = (blX - rectBlX) / width;
|
||||||
|
const y = (rectTrY - trY) / height;
|
||||||
|
const rectWidth = (trX - blX) / width;
|
||||||
|
const rectHeight = (trY - blY) / height;
|
||||||
|
rect.setAttribute("x", x);
|
||||||
|
rect.setAttribute("y", y);
|
||||||
|
rect.setAttribute("width", rectWidth);
|
||||||
|
rect.setAttribute("height", rectHeight);
|
||||||
|
clipPath.append(rect);
|
||||||
|
svgBuffer?.push(
|
||||||
|
`<rect vector-effect="non-scaling-stroke" x="${x}" y="${y}" width="${rectWidth}" height="${rectHeight}"/>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#hasBorder) {
|
||||||
|
svgBuffer.push(`</g></svg>')`);
|
||||||
|
style.backgroundImage = svgBuffer.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container.append(svg);
|
||||||
|
this.container.style.clipPath = `url(#${id})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -487,7 +548,7 @@ class AnnotationElement {
|
|||||||
modificationDate: data.modificationDate,
|
modificationDate: data.modificationDate,
|
||||||
contentsObj: data.contentsObj,
|
contentsObj: data.contentsObj,
|
||||||
richText: data.richText,
|
richText: data.richText,
|
||||||
parentRect: this.firstQuadRect || data.rect,
|
parentRect: data.rect,
|
||||||
borderStyle: 0,
|
borderStyle: 0,
|
||||||
id: `popup_${data.id}`,
|
id: `popup_${data.id}`,
|
||||||
rotation: data.rotation,
|
rotation: data.rotation,
|
||||||
@ -498,32 +559,11 @@ class AnnotationElement {
|
|||||||
this.parent.div.append(popup.render());
|
this.parent.div.append(popup.render());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the quadrilaterals of the annotation.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {string} className
|
|
||||||
* @memberof AnnotationElement
|
|
||||||
* @returns {Array<HTMLElement>} An array of section elements.
|
|
||||||
*/
|
|
||||||
_renderQuadrilaterals(className) {
|
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
|
||||||
assert(this.quadrilaterals, "Missing quadrilaterals during rendering");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const quadrilateral of this.quadrilaterals) {
|
|
||||||
quadrilateral.classList.add(className);
|
|
||||||
}
|
|
||||||
return this.quadrilaterals;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the annotation's HTML element(s).
|
* Render the annotation's HTML element(s).
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @memberof AnnotationElement
|
* @memberof AnnotationElement
|
||||||
* @returns {HTMLElement|Array<HTMLElement>|undefined} A section element or
|
|
||||||
* an array of section elements.
|
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
unreachable("Abstract method `AnnotationElement.render` called");
|
unreachable("Abstract method `AnnotationElement.render` called");
|
||||||
@ -591,8 +631,16 @@ class AnnotationElement {
|
|||||||
this.popup?.forceHide();
|
this.popup?.forceHide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the HTML element(s) which can trigger a popup when clicked or hovered.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @memberof AnnotationElement
|
||||||
|
* @returns {Array<HTMLElement>|HTMLElement} An array of elements or an
|
||||||
|
* element.
|
||||||
|
*/
|
||||||
getElementsToTriggerPopup() {
|
getElementsToTriggerPopup() {
|
||||||
return this.quadrilaterals || this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
|
|
||||||
addHighlightArea() {
|
addHighlightArea() {
|
||||||
@ -674,16 +722,6 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
|
||||||
return this._renderQuadrilaterals("linkAnnotation").map(
|
|
||||||
(quadrilateral, index) => {
|
|
||||||
const linkElement = index === 0 ? link : link.cloneNode();
|
|
||||||
quadrilateral.append(linkElement);
|
|
||||||
return quadrilateral;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.container.classList.add("linkAnnotation");
|
this.container.classList.add("linkAnnotation");
|
||||||
if (isBound) {
|
if (isBound) {
|
||||||
this.container.append(link);
|
this.container.append(link);
|
||||||
@ -2632,10 +2670,6 @@ class HighlightAnnotationElement extends AnnotationElement {
|
|||||||
this._createPopup();
|
this._createPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
|
||||||
return this._renderQuadrilaterals("highlightAnnotation");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.container.classList.add("highlightAnnotation");
|
this.container.classList.add("highlightAnnotation");
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
@ -2661,10 +2695,6 @@ class UnderlineAnnotationElement extends AnnotationElement {
|
|||||||
this._createPopup();
|
this._createPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
|
||||||
return this._renderQuadrilaterals("underlineAnnotation");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.container.classList.add("underlineAnnotation");
|
this.container.classList.add("underlineAnnotation");
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
@ -2690,10 +2720,6 @@ class SquigglyAnnotationElement extends AnnotationElement {
|
|||||||
this._createPopup();
|
this._createPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
|
||||||
return this._renderQuadrilaterals("squigglyAnnotation");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.container.classList.add("squigglyAnnotation");
|
this.container.classList.add("squigglyAnnotation");
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
@ -2719,10 +2745,6 @@ class StrikeOutAnnotationElement extends AnnotationElement {
|
|||||||
this._createPopup();
|
this._createPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
|
||||||
return this._renderQuadrilaterals("strikeoutAnnotation");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.container.classList.add("strikeoutAnnotation");
|
this.container.classList.add("strikeoutAnnotation");
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
@ -2971,13 +2993,7 @@ class AnnotationLayer {
|
|||||||
if (data.hidden) {
|
if (data.hidden) {
|
||||||
rendered.style.visibility = "hidden";
|
rendered.style.visibility = "hidden";
|
||||||
}
|
}
|
||||||
if (Array.isArray(rendered)) {
|
this.#appendElement(rendered, data.id);
|
||||||
for (const renderedElement of rendered) {
|
|
||||||
this.#appendElement(renderedElement, data.id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.#appendElement(rendered, data.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#setAnnotationCanvasMap();
|
this.#setAnnotationCanvasMap();
|
||||||
|
@ -29,7 +29,9 @@
|
|||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a,
|
.annotationLayer
|
||||||
|
:is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder)
|
||||||
|
> a,
|
||||||
.annotationLayer .popupTriggerArea::after,
|
.annotationLayer .popupTriggerArea::after,
|
||||||
.annotationLayer .fileAttachmentAnnotation .popupTriggerArea {
|
.annotationLayer .fileAttachmentAnnotation .popupTriggerArea {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
@ -37,6 +39,14 @@
|
|||||||
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
|
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.annotationLayer .hasClipPath::after {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annotationLayer .linkAnnotation.hasBorder {
|
||||||
|
background-color: rgba(255, 255, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.annotationLayer .popupTriggerArea::after {
|
.annotationLayer .popupTriggerArea::after {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -84,6 +84,9 @@ async function writeSVG(svgElement, ctx) {
|
|||||||
// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1844414
|
// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1844414
|
||||||
// we load the image two times.
|
// we load the image two times.
|
||||||
await loadImage(svg_xml, null);
|
await loadImage(svg_xml, null);
|
||||||
|
await new Promise(resolve => {
|
||||||
|
setTimeout(resolve, 10);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return loadImage(svg_xml, ctx);
|
return loadImage(svg_xml, ctx);
|
||||||
}
|
}
|
||||||
|
@ -120,13 +120,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.annotationLayer
|
.annotationLayer
|
||||||
:is(.linkAnnotation, .buttonWidgetAnnotation.pushButton)
|
:is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder)
|
||||||
> a:hover {
|
> a:hover {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
background: rgba(255, 255, 0, 1);
|
background-color: rgba(255, 255, 0, 1);
|
||||||
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
|
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.annotationLayer .linkAnnotation.hasBorder:hover {
|
||||||
|
background-color: rgba(255, 255, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.annotationLayer .hasBorder {
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.annotationLayer .textAnnotation img {
|
.annotationLayer .textAnnotation img {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -368,3 +376,13 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.annotationLayer svg.quadrilateralsContainer {
|
||||||
|
contain: strict;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user