Merge pull request #12696 from timvandermeij/annotation-quadpoints
Fix non-standard quadpoints orders for annotations
This commit is contained in:
commit
d784af3f38
@ -227,7 +227,36 @@ function getQuadPoints(dict, rect) {
|
|||||||
quadPointsLists[i].push({ x, y });
|
quadPointsLists[i].push({ x, y });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return quadPointsLists;
|
|
||||||
|
// The PDF specification states in section 12.5.6.10 (figure 64) that the
|
||||||
|
// order of the quadpoints should be bottom left, bottom right, top right
|
||||||
|
// and top left. However, in practice PDF files use a different order,
|
||||||
|
// namely bottom left, bottom right, top left and top right (this is also
|
||||||
|
// mentioned on https://github.com/highkite/pdfAnnotate#QuadPoints), so
|
||||||
|
// this is the actual order we should work with. However, the situation is
|
||||||
|
// even worse since Adobe's own applications and other applications violate
|
||||||
|
// the specification and create annotations with other orders, namely top
|
||||||
|
// left, top right, bottom left and bottom right or even top left, top right,
|
||||||
|
// bottom right and bottom left. To avoid inconsistency and broken rendering,
|
||||||
|
// we normalize all lists to put the quadpoints in the same standard order
|
||||||
|
// (see https://stackoverflow.com/a/10729881).
|
||||||
|
return quadPointsLists.map(quadPointsList => {
|
||||||
|
const [minX, maxX, minY, maxY] = quadPointsList.reduce(
|
||||||
|
([mX, MX, mY, MY], quadPoint) => [
|
||||||
|
Math.min(mX, quadPoint.x),
|
||||||
|
Math.max(MX, quadPoint.x),
|
||||||
|
Math.min(mY, quadPoint.y),
|
||||||
|
Math.max(MY, quadPoint.y),
|
||||||
|
],
|
||||||
|
[Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MIN_VALUE]
|
||||||
|
);
|
||||||
|
return [
|
||||||
|
{ x: minX, y: maxY },
|
||||||
|
{ x: maxX, y: maxY },
|
||||||
|
{ x: minX, y: minY },
|
||||||
|
{ x: maxX, y: minY },
|
||||||
|
];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTransformMatrix(rect, bbox, matrix) {
|
function getTransformMatrix(rect, bbox, matrix) {
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
AnnotationBorderStyleType,
|
AnnotationBorderStyleType,
|
||||||
AnnotationType,
|
AnnotationType,
|
||||||
|
assert,
|
||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
unreachable,
|
unreachable,
|
||||||
Util,
|
Util,
|
||||||
@ -133,7 +134,14 @@ class AnnotationElementFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AnnotationElement {
|
class AnnotationElement {
|
||||||
constructor(parameters, isRenderable = false, ignoreBorder = false) {
|
constructor(
|
||||||
|
parameters,
|
||||||
|
{
|
||||||
|
isRenderable = false,
|
||||||
|
ignoreBorder = false,
|
||||||
|
createQuadrilaterals = false,
|
||||||
|
} = {}
|
||||||
|
) {
|
||||||
this.isRenderable = isRenderable;
|
this.isRenderable = isRenderable;
|
||||||
this.data = parameters.data;
|
this.data = parameters.data;
|
||||||
this.layer = parameters.layer;
|
this.layer = parameters.layer;
|
||||||
@ -151,6 +159,9 @@ class AnnotationElement {
|
|||||||
if (isRenderable) {
|
if (isRenderable) {
|
||||||
this.container = this._createContainer(ignoreBorder);
|
this.container = this._createContainer(ignoreBorder);
|
||||||
}
|
}
|
||||||
|
if (createQuadrilaterals) {
|
||||||
|
this.quadrilaterals = this._createQuadrilaterals(ignoreBorder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,12 +256,12 @@ class AnnotationElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create quadrilaterals for the quadPoints.
|
* Create quadrilaterals from the annotation's quadpoints.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {boolean} ignoreBorder
|
* @param {boolean} ignoreBorder
|
||||||
* @memberof AnnotationElement
|
* @memberof AnnotationElement
|
||||||
* @returns {HTMLSectionElement}
|
* @returns {Array<HTMLSectionElement>}
|
||||||
*/
|
*/
|
||||||
_createQuadrilaterals(ignoreBorder = false) {
|
_createQuadrilaterals(ignoreBorder = false) {
|
||||||
if (!this.data.quadPoints) {
|
if (!this.data.quadPoints) {
|
||||||
@ -315,10 +326,33 @@ class AnnotationElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the annotation's HTML element in the empty container.
|
* Render the quadrilaterals of the annotation.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} className
|
||||||
|
* @memberof AnnotationElement
|
||||||
|
* @returns {Array<HTMLSectionElement>}
|
||||||
|
*/
|
||||||
|
_renderQuadrilaterals(className) {
|
||||||
|
if (
|
||||||
|
typeof PDFJSDev === "undefined" ||
|
||||||
|
PDFJSDev.test("!PRODUCTION || TESTING")
|
||||||
|
) {
|
||||||
|
assert(this.quadrilaterals, "Missing quadrilaterals during rendering");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.quadrilaterals.forEach(quadrilateral => {
|
||||||
|
quadrilateral.className = className;
|
||||||
|
});
|
||||||
|
return this.quadrilaterals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the annotation's HTML element(s).
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @memberof AnnotationElement
|
* @memberof AnnotationElement
|
||||||
|
* @returns {HTMLSectionElement|Array<HTMLSectionElement>}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
unreachable("Abstract method `AnnotationElement.render` called");
|
unreachable("Abstract method `AnnotationElement.render` called");
|
||||||
@ -333,19 +367,10 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.action ||
|
parameters.data.action ||
|
||||||
parameters.data.isTooltipOnly
|
parameters.data.isTooltipOnly
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable);
|
super(parameters, { isRenderable, createQuadrilaterals: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the link annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof LinkAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "linkAnnotation";
|
|
||||||
|
|
||||||
const { data, linkService } = this;
|
const { data, linkService } = this;
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
|
|
||||||
@ -366,6 +391,17 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||||||
this._bindLink(link, "");
|
this._bindLink(link, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.quadrilaterals) {
|
||||||
|
return this._renderQuadrilaterals("linkAnnotation").map(
|
||||||
|
(quadrilateral, index) => {
|
||||||
|
const linkElement = index === 0 ? link : link.cloneNode();
|
||||||
|
quadrilateral.appendChild(linkElement);
|
||||||
|
return quadrilateral;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container.className = "linkAnnotation";
|
||||||
this.container.appendChild(link);
|
this.container.appendChild(link);
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
@ -416,16 +452,9 @@ class TextAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable);
|
super(parameters, { isRenderable });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the text annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof TextAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "textAnnotation";
|
this.container.className = "textAnnotation";
|
||||||
|
|
||||||
@ -451,13 +480,6 @@ class TextAnnotationElement extends AnnotationElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WidgetAnnotationElement extends AnnotationElement {
|
class WidgetAnnotationElement extends AnnotationElement {
|
||||||
/**
|
|
||||||
* Render the widget annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof WidgetAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
// Show only the container for unsupported field types.
|
// Show only the container for unsupported field types.
|
||||||
if (this.data.alternativeText) {
|
if (this.data.alternativeText) {
|
||||||
@ -473,16 +495,9 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
const isRenderable =
|
const isRenderable =
|
||||||
parameters.renderInteractiveForms ||
|
parameters.renderInteractiveForms ||
|
||||||
(!parameters.data.hasAppearance && !!parameters.data.fieldValue);
|
(!parameters.data.hasAppearance && !!parameters.data.fieldValue);
|
||||||
super(parameters, isRenderable);
|
super(parameters, { isRenderable });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the text widget annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof TextWidgetAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
const TEXT_ALIGNMENT = ["left", "center", "right"];
|
const TEXT_ALIGNMENT = ["left", "center", "right"];
|
||||||
const storage = this.annotationStorage;
|
const storage = this.annotationStorage;
|
||||||
@ -680,17 +695,9 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
|
|
||||||
class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
|
class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
|
||||||
constructor(parameters) {
|
constructor(parameters) {
|
||||||
super(parameters, parameters.renderInteractiveForms);
|
super(parameters, { isRenderable: parameters.renderInteractiveForms });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the checkbox widget annotation's HTML element
|
|
||||||
* in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof CheckboxWidgetAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
const storage = this.annotationStorage;
|
const storage = this.annotationStorage;
|
||||||
const data = this.data;
|
const data = this.data;
|
||||||
@ -720,17 +727,9 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
|
|
||||||
class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
|
class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
|
||||||
constructor(parameters) {
|
constructor(parameters) {
|
||||||
super(parameters, parameters.renderInteractiveForms);
|
super(parameters, { isRenderable: parameters.renderInteractiveForms });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the radio button widget annotation's HTML element
|
|
||||||
* in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof RadioButtonWidgetAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "buttonWidgetAnnotation radioButton";
|
this.container.className = "buttonWidgetAnnotation radioButton";
|
||||||
const storage = this.annotationStorage;
|
const storage = this.annotationStorage;
|
||||||
@ -767,14 +766,6 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
|
class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
|
||||||
/**
|
|
||||||
* Render the push button widget annotation's HTML element
|
|
||||||
* in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof PushButtonWidgetAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
// The rendering and functionality of a push button widget annotation is
|
// The rendering and functionality of a push button widget annotation is
|
||||||
// equal to that of a link annotation, but may have more functionality, such
|
// equal to that of a link annotation, but may have more functionality, such
|
||||||
@ -792,17 +783,9 @@ class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
|
|||||||
|
|
||||||
class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
|
class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
|
||||||
constructor(parameters) {
|
constructor(parameters) {
|
||||||
super(parameters, parameters.renderInteractiveForms);
|
super(parameters, { isRenderable: parameters.renderInteractiveForms });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the choice widget annotation's HTML element in the empty
|
|
||||||
* container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof ChoiceWidgetAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "choiceWidgetAnnotation";
|
this.container.className = "choiceWidgetAnnotation";
|
||||||
const storage = this.annotationStorage;
|
const storage = this.annotationStorage;
|
||||||
@ -857,16 +840,9 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
class PopupAnnotationElement extends AnnotationElement {
|
class PopupAnnotationElement extends AnnotationElement {
|
||||||
constructor(parameters) {
|
constructor(parameters) {
|
||||||
const isRenderable = !!(parameters.data.title || parameters.data.contents);
|
const isRenderable = !!(parameters.data.title || parameters.data.contents);
|
||||||
super(parameters, isRenderable);
|
super(parameters, { isRenderable });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the popup annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof PopupAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
// Do not render popup annotations for parent elements with these types as
|
// Do not render popup annotations for parent elements with these types as
|
||||||
// they create the popups themselves (because of custom trigger divs).
|
// they create the popups themselves (because of custom trigger divs).
|
||||||
@ -935,13 +911,6 @@ class PopupElement {
|
|||||||
this.pinned = false;
|
this.pinned = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the popup's HTML element.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof PopupElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
const BACKGROUND_ENLIGHT = 0.7;
|
const BACKGROUND_ENLIGHT = 0.7;
|
||||||
|
|
||||||
@ -1082,16 +1051,9 @@ class FreeTextAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the free text annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof FreeTextAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "freeTextAnnotation";
|
this.container.className = "freeTextAnnotation";
|
||||||
|
|
||||||
@ -1109,16 +1071,9 @@ class LineAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the line annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof LineAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "lineAnnotation";
|
this.container.className = "lineAnnotation";
|
||||||
|
|
||||||
@ -1160,16 +1115,9 @@ class SquareAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the square annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof SquareAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "squareAnnotation";
|
this.container.className = "squareAnnotation";
|
||||||
|
|
||||||
@ -1214,16 +1162,9 @@ class CircleAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the circle annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof CircleAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "circleAnnotation";
|
this.container.className = "circleAnnotation";
|
||||||
|
|
||||||
@ -1268,19 +1209,12 @@ class PolylineAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
|
|
||||||
this.containerClassName = "polylineAnnotation";
|
this.containerClassName = "polylineAnnotation";
|
||||||
this.svgElementName = "svg:polyline";
|
this.svgElementName = "svg:polyline";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the polyline annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof PolylineAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = this.containerClassName;
|
this.container.className = this.containerClassName;
|
||||||
|
|
||||||
@ -1340,16 +1274,9 @@ class CaretAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the caret annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof CaretAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "caretAnnotation";
|
this.container.className = "caretAnnotation";
|
||||||
|
|
||||||
@ -1367,7 +1294,7 @@ class InkAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
|
|
||||||
this.containerClassName = "inkAnnotation";
|
this.containerClassName = "inkAnnotation";
|
||||||
|
|
||||||
@ -1376,13 +1303,6 @@ class InkAnnotationElement extends AnnotationElement {
|
|||||||
this.svgElementName = "svg:polyline";
|
this.svgElementName = "svg:polyline";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the ink annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof InkAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = this.containerClassName;
|
this.container.className = this.containerClassName;
|
||||||
|
|
||||||
@ -1433,27 +1353,20 @@ class HighlightAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, {
|
||||||
this.quadrilaterals = this._createQuadrilaterals(/* ignoreBorder = */ true);
|
isRenderable,
|
||||||
|
ignoreBorder: true,
|
||||||
|
createQuadrilaterals: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the highlight annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof HighlightAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.data.hasPopup) {
|
if (!this.data.hasPopup) {
|
||||||
this._createPopup(null, this.data);
|
this._createPopup(null, this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
if (this.quadrilaterals) {
|
||||||
this.quadrilaterals.forEach(quadrilateral => {
|
return this._renderQuadrilaterals("highlightAnnotation");
|
||||||
quadrilateral.className = "highlightAnnotation";
|
|
||||||
});
|
|
||||||
return this.quadrilaterals;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container.className = "highlightAnnotation";
|
this.container.className = "highlightAnnotation";
|
||||||
@ -1468,27 +1381,20 @@ class UnderlineAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, {
|
||||||
this.quadrilaterals = this._createQuadrilaterals(/* ignoreBorder = */ true);
|
isRenderable,
|
||||||
|
ignoreBorder: true,
|
||||||
|
createQuadrilaterals: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the underline annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof UnderlineAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.data.hasPopup) {
|
if (!this.data.hasPopup) {
|
||||||
this._createPopup(null, this.data);
|
this._createPopup(null, this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
if (this.quadrilaterals) {
|
||||||
this.quadrilaterals.forEach(quadrilateral => {
|
return this._renderQuadrilaterals("underlineAnnotation");
|
||||||
quadrilateral.className = "underlineAnnotation";
|
|
||||||
});
|
|
||||||
return this.quadrilaterals;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container.className = "underlineAnnotation";
|
this.container.className = "underlineAnnotation";
|
||||||
@ -1503,27 +1409,20 @@ class SquigglyAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, {
|
||||||
this.quadrilaterals = this._createQuadrilaterals(/* ignoreBorder = */ true);
|
isRenderable,
|
||||||
|
ignoreBorder: true,
|
||||||
|
createQuadrilaterals: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the squiggly annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof SquigglyAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.data.hasPopup) {
|
if (!this.data.hasPopup) {
|
||||||
this._createPopup(null, this.data);
|
this._createPopup(null, this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
if (this.quadrilaterals) {
|
||||||
this.quadrilaterals.forEach(quadrilateral => {
|
return this._renderQuadrilaterals("squigglyAnnotation");
|
||||||
quadrilateral.className = "squigglyAnnotation";
|
|
||||||
});
|
|
||||||
return this.quadrilaterals;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container.className = "squigglyAnnotation";
|
this.container.className = "squigglyAnnotation";
|
||||||
@ -1538,27 +1437,20 @@ class StrikeOutAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, {
|
||||||
this.quadrilaterals = this._createQuadrilaterals(/* ignoreBorder = */ true);
|
isRenderable,
|
||||||
|
ignoreBorder: true,
|
||||||
|
createQuadrilaterals: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the strikeout annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof StrikeOutAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.data.hasPopup) {
|
if (!this.data.hasPopup) {
|
||||||
this._createPopup(null, this.data);
|
this._createPopup(null, this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quadrilaterals) {
|
if (this.quadrilaterals) {
|
||||||
this.quadrilaterals.forEach(quadrilateral => {
|
return this._renderQuadrilaterals("strikeoutAnnotation");
|
||||||
quadrilateral.className = "strikeoutAnnotation";
|
|
||||||
});
|
|
||||||
return this.quadrilaterals;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container.className = "strikeoutAnnotation";
|
this.container.className = "strikeoutAnnotation";
|
||||||
@ -1573,16 +1465,9 @@ class StampAnnotationElement extends AnnotationElement {
|
|||||||
parameters.data.title ||
|
parameters.data.title ||
|
||||||
parameters.data.contents
|
parameters.data.contents
|
||||||
);
|
);
|
||||||
super(parameters, isRenderable, /* ignoreBorder = */ true);
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the stamp annotation's HTML element in the empty container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof StampAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "stampAnnotation";
|
this.container.className = "stampAnnotation";
|
||||||
|
|
||||||
@ -1595,7 +1480,7 @@ class StampAnnotationElement extends AnnotationElement {
|
|||||||
|
|
||||||
class FileAttachmentAnnotationElement extends AnnotationElement {
|
class FileAttachmentAnnotationElement extends AnnotationElement {
|
||||||
constructor(parameters) {
|
constructor(parameters) {
|
||||||
super(parameters, /* isRenderable = */ true);
|
super(parameters, { isRenderable: true });
|
||||||
|
|
||||||
const { filename, content } = this.data.file;
|
const { filename, content } = this.data.file;
|
||||||
this.filename = getFilenameFromUrl(filename);
|
this.filename = getFilenameFromUrl(filename);
|
||||||
@ -1611,14 +1496,6 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the file attachment annotation's HTML element in the empty
|
|
||||||
* container.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @memberof FileAttachmentAnnotationElement
|
|
||||||
* @returns {HTMLSectionElement}
|
|
||||||
*/
|
|
||||||
render() {
|
render() {
|
||||||
this.container.className = "fileAttachmentAnnotation";
|
this.container.className = "fileAttachmentAnnotation";
|
||||||
|
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -392,6 +392,7 @@
|
|||||||
!issue11442_reduced.pdf
|
!issue11442_reduced.pdf
|
||||||
!issue11549_reduced.pdf
|
!issue11549_reduced.pdf
|
||||||
!issue8097_reduced.pdf
|
!issue8097_reduced.pdf
|
||||||
|
!quadpoints.pdf
|
||||||
!transparent.pdf
|
!transparent.pdf
|
||||||
!xobject-image.pdf
|
!xobject-image.pdf
|
||||||
!ccitt_EndOfBlock_false.pdf
|
!ccitt_EndOfBlock_false.pdf
|
||||||
|
BIN
test/pdfs/quadpoints.pdf
Normal file
BIN
test/pdfs/quadpoints.pdf
Normal file
Binary file not shown.
@ -4805,6 +4805,13 @@
|
|||||||
"lastPage": 1,
|
"lastPage": 1,
|
||||||
"type": "text"
|
"type": "text"
|
||||||
},
|
},
|
||||||
|
{ "id": "quadpoints",
|
||||||
|
"file": "pdfs/quadpoints.pdf",
|
||||||
|
"md5": "aadbc9bf826b4604c49a994fc8cd72c1",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"annotations": true
|
||||||
|
},
|
||||||
{ "id": "operator-in-TJ-array",
|
{ "id": "operator-in-TJ-array",
|
||||||
"file": "pdfs/operator-in-TJ-array.pdf",
|
"file": "pdfs/operator-in-TJ-array.pdf",
|
||||||
"md5": "dfe0f15a45be18eca142adaf760984ee",
|
"md5": "dfe0f15a45be18eca142adaf760984ee",
|
||||||
|
@ -216,41 +216,67 @@ describe("annotation", function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should process valid quadpoints arrays", function () {
|
it("should process quadpoints in the standard order", function () {
|
||||||
rect = [10, 10, 20, 20];
|
rect = [10, 10, 20, 20];
|
||||||
dict.set("QuadPoints", [
|
dict.set("QuadPoints", [
|
||||||
|
10,
|
||||||
|
20,
|
||||||
|
20,
|
||||||
|
20,
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
20,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
19,
|
||||||
|
19,
|
||||||
|
19,
|
||||||
11,
|
11,
|
||||||
11,
|
11,
|
||||||
12,
|
19,
|
||||||
12,
|
11,
|
||||||
13,
|
|
||||||
13,
|
|
||||||
14,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
15,
|
|
||||||
16,
|
|
||||||
16,
|
|
||||||
17,
|
|
||||||
17,
|
|
||||||
18,
|
|
||||||
18,
|
|
||||||
]);
|
]);
|
||||||
expect(getQuadPoints(dict, rect)).toEqual([
|
expect(getQuadPoints(dict, rect)).toEqual([
|
||||||
[
|
[
|
||||||
{ x: 11, y: 11 },
|
{ x: 10, y: 20 },
|
||||||
{ x: 12, y: 12 },
|
{ x: 20, y: 20 },
|
||||||
{ x: 13, y: 13 },
|
{ x: 10, y: 10 },
|
||||||
{ x: 14, y: 14 },
|
{ x: 20, y: 10 },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ x: 15, y: 15 },
|
{ x: 11, y: 19 },
|
||||||
{ x: 16, y: 16 },
|
{ x: 19, y: 19 },
|
||||||
{ x: 17, y: 17 },
|
{ x: 11, y: 11 },
|
||||||
{ x: 18, y: 18 },
|
{ x: 19, y: 11 },
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should normalize and process quadpoints in non-standard orders", function () {
|
||||||
|
rect = [10, 10, 20, 20];
|
||||||
|
const nonStandardOrders = [
|
||||||
|
// Bottom left, bottom right, top right and top left.
|
||||||
|
[10, 20, 20, 20, 20, 10, 10, 10],
|
||||||
|
|
||||||
|
// Top left, top right, bottom left and bottom right.
|
||||||
|
[10, 10, 20, 10, 10, 20, 20, 20],
|
||||||
|
|
||||||
|
// Top left, top right, bottom right and bottom left.
|
||||||
|
[10, 10, 20, 10, 20, 20, 10, 20],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const nonStandardOrder of nonStandardOrders) {
|
||||||
|
dict.set("QuadPoints", nonStandardOrder);
|
||||||
|
expect(getQuadPoints(dict, rect)).toEqual([
|
||||||
|
[
|
||||||
|
{ x: 10, y: 20 },
|
||||||
|
{ x: 20, y: 20 },
|
||||||
|
{ x: 10, y: 10 },
|
||||||
|
{ x: 20, y: 10 },
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Annotation", function () {
|
describe("Annotation", function () {
|
||||||
@ -1265,7 +1291,7 @@ describe("annotation", function () {
|
|||||||
annotationDict.set("Type", Name.get("Annot"));
|
annotationDict.set("Type", Name.get("Annot"));
|
||||||
annotationDict.set("Subtype", Name.get("Link"));
|
annotationDict.set("Subtype", Name.get("Link"));
|
||||||
annotationDict.set("Rect", [10, 10, 20, 20]);
|
annotationDict.set("Rect", [10, 10, 20, 20]);
|
||||||
annotationDict.set("QuadPoints", [11, 11, 12, 12, 13, 13, 14, 14]);
|
annotationDict.set("QuadPoints", [10, 20, 20, 20, 10, 10, 20, 10]);
|
||||||
|
|
||||||
const annotationRef = Ref.get(121, 0);
|
const annotationRef = Ref.get(121, 0);
|
||||||
const xref = new XRefMock([{ ref: annotationRef, data: annotationDict }]);
|
const xref = new XRefMock([{ ref: annotationRef, data: annotationDict }]);
|
||||||
@ -1279,10 +1305,10 @@ describe("annotation", function () {
|
|||||||
expect(data.annotationType).toEqual(AnnotationType.LINK);
|
expect(data.annotationType).toEqual(AnnotationType.LINK);
|
||||||
expect(data.quadPoints).toEqual([
|
expect(data.quadPoints).toEqual([
|
||||||
[
|
[
|
||||||
{ x: 11, y: 11 },
|
{ x: 10, y: 20 },
|
||||||
{ x: 12, y: 12 },
|
{ x: 20, y: 20 },
|
||||||
{ x: 13, y: 13 },
|
{ x: 10, y: 10 },
|
||||||
{ x: 14, y: 14 },
|
{ x: 20, y: 10 },
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
@ -3636,7 +3662,7 @@ describe("annotation", function () {
|
|||||||
highlightDict.set("Type", Name.get("Annot"));
|
highlightDict.set("Type", Name.get("Annot"));
|
||||||
highlightDict.set("Subtype", Name.get("Highlight"));
|
highlightDict.set("Subtype", Name.get("Highlight"));
|
||||||
highlightDict.set("Rect", [10, 10, 20, 20]);
|
highlightDict.set("Rect", [10, 10, 20, 20]);
|
||||||
highlightDict.set("QuadPoints", [11, 11, 12, 12, 13, 13, 14, 14]);
|
highlightDict.set("QuadPoints", [10, 20, 20, 20, 10, 10, 20, 10]);
|
||||||
|
|
||||||
const highlightRef = Ref.get(121, 0);
|
const highlightRef = Ref.get(121, 0);
|
||||||
const xref = new XRefMock([{ ref: highlightRef, data: highlightDict }]);
|
const xref = new XRefMock([{ ref: highlightRef, data: highlightDict }]);
|
||||||
@ -3650,10 +3676,10 @@ describe("annotation", function () {
|
|||||||
expect(data.annotationType).toEqual(AnnotationType.HIGHLIGHT);
|
expect(data.annotationType).toEqual(AnnotationType.HIGHLIGHT);
|
||||||
expect(data.quadPoints).toEqual([
|
expect(data.quadPoints).toEqual([
|
||||||
[
|
[
|
||||||
{ x: 11, y: 11 },
|
{ x: 10, y: 20 },
|
||||||
{ x: 12, y: 12 },
|
{ x: 20, y: 20 },
|
||||||
{ x: 13, y: 13 },
|
{ x: 10, y: 10 },
|
||||||
{ x: 14, y: 14 },
|
{ x: 20, y: 10 },
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
@ -3709,7 +3735,7 @@ describe("annotation", function () {
|
|||||||
underlineDict.set("Type", Name.get("Annot"));
|
underlineDict.set("Type", Name.get("Annot"));
|
||||||
underlineDict.set("Subtype", Name.get("Underline"));
|
underlineDict.set("Subtype", Name.get("Underline"));
|
||||||
underlineDict.set("Rect", [10, 10, 20, 20]);
|
underlineDict.set("Rect", [10, 10, 20, 20]);
|
||||||
underlineDict.set("QuadPoints", [11, 11, 12, 12, 13, 13, 14, 14]);
|
underlineDict.set("QuadPoints", [10, 20, 20, 20, 10, 10, 20, 10]);
|
||||||
|
|
||||||
const underlineRef = Ref.get(121, 0);
|
const underlineRef = Ref.get(121, 0);
|
||||||
const xref = new XRefMock([{ ref: underlineRef, data: underlineDict }]);
|
const xref = new XRefMock([{ ref: underlineRef, data: underlineDict }]);
|
||||||
@ -3723,10 +3749,10 @@ describe("annotation", function () {
|
|||||||
expect(data.annotationType).toEqual(AnnotationType.UNDERLINE);
|
expect(data.annotationType).toEqual(AnnotationType.UNDERLINE);
|
||||||
expect(data.quadPoints).toEqual([
|
expect(data.quadPoints).toEqual([
|
||||||
[
|
[
|
||||||
{ x: 11, y: 11 },
|
{ x: 10, y: 20 },
|
||||||
{ x: 12, y: 12 },
|
{ x: 20, y: 20 },
|
||||||
{ x: 13, y: 13 },
|
{ x: 10, y: 10 },
|
||||||
{ x: 14, y: 14 },
|
{ x: 20, y: 10 },
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
@ -3760,7 +3786,7 @@ describe("annotation", function () {
|
|||||||
squigglyDict.set("Type", Name.get("Annot"));
|
squigglyDict.set("Type", Name.get("Annot"));
|
||||||
squigglyDict.set("Subtype", Name.get("Squiggly"));
|
squigglyDict.set("Subtype", Name.get("Squiggly"));
|
||||||
squigglyDict.set("Rect", [10, 10, 20, 20]);
|
squigglyDict.set("Rect", [10, 10, 20, 20]);
|
||||||
squigglyDict.set("QuadPoints", [11, 11, 12, 12, 13, 13, 14, 14]);
|
squigglyDict.set("QuadPoints", [10, 20, 20, 20, 10, 10, 20, 10]);
|
||||||
|
|
||||||
const squigglyRef = Ref.get(121, 0);
|
const squigglyRef = Ref.get(121, 0);
|
||||||
const xref = new XRefMock([{ ref: squigglyRef, data: squigglyDict }]);
|
const xref = new XRefMock([{ ref: squigglyRef, data: squigglyDict }]);
|
||||||
@ -3774,10 +3800,10 @@ describe("annotation", function () {
|
|||||||
expect(data.annotationType).toEqual(AnnotationType.SQUIGGLY);
|
expect(data.annotationType).toEqual(AnnotationType.SQUIGGLY);
|
||||||
expect(data.quadPoints).toEqual([
|
expect(data.quadPoints).toEqual([
|
||||||
[
|
[
|
||||||
{ x: 11, y: 11 },
|
{ x: 10, y: 20 },
|
||||||
{ x: 12, y: 12 },
|
{ x: 20, y: 20 },
|
||||||
{ x: 13, y: 13 },
|
{ x: 10, y: 10 },
|
||||||
{ x: 14, y: 14 },
|
{ x: 20, y: 10 },
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
@ -3811,7 +3837,7 @@ describe("annotation", function () {
|
|||||||
strikeOutDict.set("Type", Name.get("Annot"));
|
strikeOutDict.set("Type", Name.get("Annot"));
|
||||||
strikeOutDict.set("Subtype", Name.get("StrikeOut"));
|
strikeOutDict.set("Subtype", Name.get("StrikeOut"));
|
||||||
strikeOutDict.set("Rect", [10, 10, 20, 20]);
|
strikeOutDict.set("Rect", [10, 10, 20, 20]);
|
||||||
strikeOutDict.set("QuadPoints", [11, 11, 12, 12, 13, 13, 14, 14]);
|
strikeOutDict.set("QuadPoints", [10, 20, 20, 20, 10, 10, 20, 10]);
|
||||||
|
|
||||||
const strikeOutRef = Ref.get(121, 0);
|
const strikeOutRef = Ref.get(121, 0);
|
||||||
const xref = new XRefMock([{ ref: strikeOutRef, data: strikeOutDict }]);
|
const xref = new XRefMock([{ ref: strikeOutRef, data: strikeOutDict }]);
|
||||||
@ -3825,10 +3851,10 @@ describe("annotation", function () {
|
|||||||
expect(data.annotationType).toEqual(AnnotationType.STRIKEOUT);
|
expect(data.annotationType).toEqual(AnnotationType.STRIKEOUT);
|
||||||
expect(data.quadPoints).toEqual([
|
expect(data.quadPoints).toEqual([
|
||||||
[
|
[
|
||||||
{ x: 11, y: 11 },
|
{ x: 10, y: 20 },
|
||||||
{ x: 12, y: 12 },
|
{ x: 20, y: 20 },
|
||||||
{ x: 13, y: 13 },
|
{ x: 10, y: 10 },
|
||||||
{ x: 14, y: 14 },
|
{ x: 20, y: 10 },
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user