From 02730800313deb5451b98d74fa58782faeb5f73f Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 5 Dec 2020 19:48:05 +0100 Subject: [PATCH 1/5] Move quadrilateral creation logic to the constructor of the `AnnotationElement` class Using an object for the various constructor options makes extensions easier and makes the code self-documenting. --- src/display/annotation_layer.js | 72 +++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 9890206659493..f91973af8db60 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -133,7 +133,14 @@ class AnnotationElementFactory { } class AnnotationElement { - constructor(parameters, isRenderable = false, ignoreBorder = false) { + constructor( + parameters, + { + isRenderable = false, + ignoreBorder = false, + createQuadrilaterals = false, + } = {} + ) { this.isRenderable = isRenderable; this.data = parameters.data; this.layer = parameters.layer; @@ -151,6 +158,9 @@ class AnnotationElement { if (isRenderable) { this.container = this._createContainer(ignoreBorder); } + if (createQuadrilaterals) { + this.quadrilaterals = this._createQuadrilaterals(ignoreBorder); + } } /** @@ -333,7 +343,7 @@ class LinkAnnotationElement extends AnnotationElement { parameters.data.action || parameters.data.isTooltipOnly ); - super(parameters, isRenderable); + super(parameters, { isRenderable }); } /** @@ -416,7 +426,7 @@ class TextAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable); + super(parameters, { isRenderable }); } /** @@ -473,7 +483,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { const isRenderable = parameters.renderInteractiveForms || (!parameters.data.hasAppearance && !!parameters.data.fieldValue); - super(parameters, isRenderable); + super(parameters, { isRenderable }); } /** @@ -680,7 +690,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { constructor(parameters) { - super(parameters, parameters.renderInteractiveForms); + super(parameters, { isRenderable: parameters.renderInteractiveForms }); } /** @@ -720,7 +730,7 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { constructor(parameters) { - super(parameters, parameters.renderInteractiveForms); + super(parameters, { isRenderable: parameters.renderInteractiveForms }); } /** @@ -792,7 +802,7 @@ class PushButtonWidgetAnnotationElement extends LinkAnnotationElement { class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { constructor(parameters) { - super(parameters, parameters.renderInteractiveForms); + super(parameters, { isRenderable: parameters.renderInteractiveForms }); } /** @@ -857,7 +867,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { class PopupAnnotationElement extends AnnotationElement { constructor(parameters) { const isRenderable = !!(parameters.data.title || parameters.data.contents); - super(parameters, isRenderable); + super(parameters, { isRenderable }); } /** @@ -1082,7 +1092,7 @@ class FreeTextAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); + super(parameters, { isRenderable, ignoreBorder: true }); } /** @@ -1109,7 +1119,7 @@ class LineAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); + super(parameters, { isRenderable, ignoreBorder: true }); } /** @@ -1160,7 +1170,7 @@ class SquareAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); + super(parameters, { isRenderable, ignoreBorder: true }); } /** @@ -1214,7 +1224,7 @@ class CircleAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); + super(parameters, { isRenderable, ignoreBorder: true }); } /** @@ -1268,7 +1278,7 @@ class PolylineAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); + super(parameters, { isRenderable, ignoreBorder: true }); this.containerClassName = "polylineAnnotation"; this.svgElementName = "svg:polyline"; @@ -1340,7 +1350,7 @@ class CaretAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); + super(parameters, { isRenderable, ignoreBorder: true }); } /** @@ -1367,7 +1377,7 @@ class InkAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); + super(parameters, { isRenderable, ignoreBorder: true }); this.containerClassName = "inkAnnotation"; @@ -1433,8 +1443,11 @@ class HighlightAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); - this.quadrilaterals = this._createQuadrilaterals(/* ignoreBorder = */ true); + super(parameters, { + isRenderable, + ignoreBorder: true, + createQuadrilaterals: true, + }); } /** @@ -1468,8 +1481,11 @@ class UnderlineAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); - this.quadrilaterals = this._createQuadrilaterals(/* ignoreBorder = */ true); + super(parameters, { + isRenderable, + ignoreBorder: true, + createQuadrilaterals: true, + }); } /** @@ -1503,8 +1519,11 @@ class SquigglyAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); - this.quadrilaterals = this._createQuadrilaterals(/* ignoreBorder = */ true); + super(parameters, { + isRenderable, + ignoreBorder: true, + createQuadrilaterals: true, + }); } /** @@ -1538,8 +1557,11 @@ class StrikeOutAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); - this.quadrilaterals = this._createQuadrilaterals(/* ignoreBorder = */ true); + super(parameters, { + isRenderable, + ignoreBorder: true, + createQuadrilaterals: true, + }); } /** @@ -1573,7 +1595,7 @@ class StampAnnotationElement extends AnnotationElement { parameters.data.title || parameters.data.contents ); - super(parameters, isRenderable, /* ignoreBorder = */ true); + super(parameters, { isRenderable, ignoreBorder: true }); } /** @@ -1595,7 +1617,7 @@ class StampAnnotationElement extends AnnotationElement { class FileAttachmentAnnotationElement extends AnnotationElement { constructor(parameters) { - super(parameters, /* isRenderable = */ true); + super(parameters, { isRenderable: true }); const { filename, content } = this.data.file; this.filename = getFilenameFromUrl(filename); From cb422a80b0b40555c4fe58330b3552f6e6faace6 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 5 Dec 2020 19:32:13 +0100 Subject: [PATCH 2/5] Move quadrilateral rendering logic into a method on the `AnnotationElement` class Doing so avoids some code duplication. --- src/display/annotation_layer.js | 43 +++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index f91973af8db60..7b3ccffb6711d 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -23,6 +23,7 @@ import { import { AnnotationBorderStyleType, AnnotationType, + assert, stringToPDFString, unreachable, Util, @@ -324,6 +325,28 @@ class AnnotationElement { container.appendChild(popup); } + /** + * Render the quadrilaterals of the annotation. + * + * @private + * @param {string} className + * @memberof AnnotationElement + * @returns {Array} + */ + _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 in the empty container. * @@ -1463,10 +1486,7 @@ class HighlightAnnotationElement extends AnnotationElement { } if (this.quadrilaterals) { - this.quadrilaterals.forEach(quadrilateral => { - quadrilateral.className = "highlightAnnotation"; - }); - return this.quadrilaterals; + return this._renderQuadrilaterals("highlightAnnotation"); } this.container.className = "highlightAnnotation"; @@ -1501,10 +1521,7 @@ class UnderlineAnnotationElement extends AnnotationElement { } if (this.quadrilaterals) { - this.quadrilaterals.forEach(quadrilateral => { - quadrilateral.className = "underlineAnnotation"; - }); - return this.quadrilaterals; + return this._renderQuadrilaterals("underlineAnnotation"); } this.container.className = "underlineAnnotation"; @@ -1539,10 +1556,7 @@ class SquigglyAnnotationElement extends AnnotationElement { } if (this.quadrilaterals) { - this.quadrilaterals.forEach(quadrilateral => { - quadrilateral.className = "squigglyAnnotation"; - }); - return this.quadrilaterals; + return this._renderQuadrilaterals("squigglyAnnotation"); } this.container.className = "squigglyAnnotation"; @@ -1577,10 +1591,7 @@ class StrikeOutAnnotationElement extends AnnotationElement { } if (this.quadrilaterals) { - this.quadrilaterals.forEach(quadrilateral => { - quadrilateral.className = "strikeoutAnnotation"; - }); - return this.quadrilaterals; + return this._renderQuadrilaterals("strikeoutAnnotation"); } this.container.className = "strikeoutAnnotation"; From 7be4a14d8755fa9393b20cecdbbc66626cf9a937 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 5 Dec 2020 20:16:40 +0100 Subject: [PATCH 3/5] Move documentation of the `render` methods to the `AnnotationElement` class Not only does this reduce boilerplate since the documentation is the same for all annotation classes, it also wasn't correct for the annotation types that support quadpoints since they return an array of section elements instead of a single one. --- src/display/annotation_layer.js | 173 +------------------------------- 1 file changed, 4 insertions(+), 169 deletions(-) diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 7b3ccffb6711d..fcf7b750668c8 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -256,12 +256,12 @@ class AnnotationElement { } /** - * Create quadrilaterals for the quadPoints. + * Create quadrilaterals from the annotation's quadpoints. * * @private * @param {boolean} ignoreBorder * @memberof AnnotationElement - * @returns {HTMLSectionElement} + * @returns {Array} */ _createQuadrilaterals(ignoreBorder = false) { if (!this.data.quadPoints) { @@ -348,10 +348,11 @@ class AnnotationElement { } /** - * Render the annotation's HTML element in the empty container. + * Render the annotation's HTML element(s). * * @public * @memberof AnnotationElement + * @returns {HTMLSectionElement|Array} */ render() { unreachable("Abstract method `AnnotationElement.render` called"); @@ -369,13 +370,6 @@ class LinkAnnotationElement extends AnnotationElement { super(parameters, { isRenderable }); } - /** - * Render the link annotation's HTML element in the empty container. - * - * @public - * @memberof LinkAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "linkAnnotation"; @@ -452,13 +446,6 @@ class TextAnnotationElement extends AnnotationElement { super(parameters, { isRenderable }); } - /** - * Render the text annotation's HTML element in the empty container. - * - * @public - * @memberof TextAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "textAnnotation"; @@ -484,13 +471,6 @@ class TextAnnotationElement extends AnnotationElement { } class WidgetAnnotationElement extends AnnotationElement { - /** - * Render the widget annotation's HTML element in the empty container. - * - * @public - * @memberof WidgetAnnotationElement - * @returns {HTMLSectionElement} - */ render() { // Show only the container for unsupported field types. if (this.data.alternativeText) { @@ -509,13 +489,6 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { super(parameters, { isRenderable }); } - /** - * Render the text widget annotation's HTML element in the empty container. - * - * @public - * @memberof TextWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ render() { const TEXT_ALIGNMENT = ["left", "center", "right"]; const storage = this.annotationStorage; @@ -716,14 +689,6 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { super(parameters, { isRenderable: parameters.renderInteractiveForms }); } - /** - * Render the checkbox widget annotation's HTML element - * in the empty container. - * - * @public - * @memberof CheckboxWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ render() { const storage = this.annotationStorage; const data = this.data; @@ -756,14 +721,6 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { super(parameters, { isRenderable: parameters.renderInteractiveForms }); } - /** - * Render the radio button widget annotation's HTML element - * in the empty container. - * - * @public - * @memberof RadioButtonWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "buttonWidgetAnnotation radioButton"; const storage = this.annotationStorage; @@ -800,14 +757,6 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { } class PushButtonWidgetAnnotationElement extends LinkAnnotationElement { - /** - * Render the push button widget annotation's HTML element - * in the empty container. - * - * @public - * @memberof PushButtonWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ render() { // The rendering and functionality of a push button widget annotation is // equal to that of a link annotation, but may have more functionality, such @@ -828,14 +777,6 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { super(parameters, { isRenderable: parameters.renderInteractiveForms }); } - /** - * Render the choice widget annotation's HTML element in the empty - * container. - * - * @public - * @memberof ChoiceWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "choiceWidgetAnnotation"; const storage = this.annotationStorage; @@ -893,13 +834,6 @@ class PopupAnnotationElement extends AnnotationElement { super(parameters, { isRenderable }); } - /** - * Render the popup annotation's HTML element in the empty container. - * - * @public - * @memberof PopupAnnotationElement - * @returns {HTMLSectionElement} - */ render() { // Do not render popup annotations for parent elements with these types as // they create the popups themselves (because of custom trigger divs). @@ -968,13 +902,6 @@ class PopupElement { this.pinned = false; } - /** - * Render the popup's HTML element. - * - * @public - * @memberof PopupElement - * @returns {HTMLSectionElement} - */ render() { const BACKGROUND_ENLIGHT = 0.7; @@ -1118,13 +1045,6 @@ class FreeTextAnnotationElement extends AnnotationElement { super(parameters, { isRenderable, ignoreBorder: true }); } - /** - * Render the free text annotation's HTML element in the empty container. - * - * @public - * @memberof FreeTextAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "freeTextAnnotation"; @@ -1145,13 +1065,6 @@ class LineAnnotationElement extends AnnotationElement { super(parameters, { isRenderable, ignoreBorder: true }); } - /** - * Render the line annotation's HTML element in the empty container. - * - * @public - * @memberof LineAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "lineAnnotation"; @@ -1196,13 +1109,6 @@ class SquareAnnotationElement extends AnnotationElement { super(parameters, { isRenderable, ignoreBorder: true }); } - /** - * Render the square annotation's HTML element in the empty container. - * - * @public - * @memberof SquareAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "squareAnnotation"; @@ -1250,13 +1156,6 @@ class CircleAnnotationElement extends AnnotationElement { super(parameters, { isRenderable, ignoreBorder: true }); } - /** - * Render the circle annotation's HTML element in the empty container. - * - * @public - * @memberof CircleAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "circleAnnotation"; @@ -1307,13 +1206,6 @@ class PolylineAnnotationElement extends AnnotationElement { this.svgElementName = "svg:polyline"; } - /** - * Render the polyline annotation's HTML element in the empty container. - * - * @public - * @memberof PolylineAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = this.containerClassName; @@ -1376,13 +1268,6 @@ class CaretAnnotationElement extends AnnotationElement { super(parameters, { isRenderable, ignoreBorder: true }); } - /** - * Render the caret annotation's HTML element in the empty container. - * - * @public - * @memberof CaretAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "caretAnnotation"; @@ -1409,13 +1294,6 @@ class InkAnnotationElement extends AnnotationElement { this.svgElementName = "svg:polyline"; } - /** - * Render the ink annotation's HTML element in the empty container. - * - * @public - * @memberof InkAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = this.containerClassName; @@ -1473,13 +1351,6 @@ class HighlightAnnotationElement extends AnnotationElement { }); } - /** - * Render the highlight annotation's HTML element in the empty container. - * - * @public - * @memberof HighlightAnnotationElement - * @returns {HTMLSectionElement} - */ render() { if (!this.data.hasPopup) { this._createPopup(null, this.data); @@ -1508,13 +1379,6 @@ class UnderlineAnnotationElement extends AnnotationElement { }); } - /** - * Render the underline annotation's HTML element in the empty container. - * - * @public - * @memberof UnderlineAnnotationElement - * @returns {HTMLSectionElement} - */ render() { if (!this.data.hasPopup) { this._createPopup(null, this.data); @@ -1543,13 +1407,6 @@ class SquigglyAnnotationElement extends AnnotationElement { }); } - /** - * Render the squiggly annotation's HTML element in the empty container. - * - * @public - * @memberof SquigglyAnnotationElement - * @returns {HTMLSectionElement} - */ render() { if (!this.data.hasPopup) { this._createPopup(null, this.data); @@ -1578,13 +1435,6 @@ class StrikeOutAnnotationElement extends AnnotationElement { }); } - /** - * Render the strikeout annotation's HTML element in the empty container. - * - * @public - * @memberof StrikeOutAnnotationElement - * @returns {HTMLSectionElement} - */ render() { if (!this.data.hasPopup) { this._createPopup(null, this.data); @@ -1609,13 +1459,6 @@ class StampAnnotationElement extends AnnotationElement { super(parameters, { isRenderable, ignoreBorder: true }); } - /** - * Render the stamp annotation's HTML element in the empty container. - * - * @public - * @memberof StampAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "stampAnnotation"; @@ -1644,14 +1487,6 @@ class FileAttachmentAnnotationElement extends AnnotationElement { } } - /** - * Render the file attachment annotation's HTML element in the empty - * container. - * - * @public - * @memberof FileAttachmentAnnotationElement - * @returns {HTMLSectionElement} - */ render() { this.container.className = "fileAttachmentAnnotation"; From e3b6a9fb23ffb0652b25322348bc8908654a9027 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 5 Dec 2020 20:29:21 +0100 Subject: [PATCH 4/5] Implement quadpoints rendering for link annotations Each quadrilateral needs to have its own link element, so the first quadrilateral can use the already created element, but the next quadrilaterals need to clone that element. --- src/display/annotation_layer.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index fcf7b750668c8..cb1b42800bfe6 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -367,12 +367,10 @@ class LinkAnnotationElement extends AnnotationElement { parameters.data.action || parameters.data.isTooltipOnly ); - super(parameters, { isRenderable }); + super(parameters, { isRenderable, createQuadrilaterals: true }); } render() { - this.container.className = "linkAnnotation"; - const { data, linkService } = this; const link = document.createElement("a"); @@ -393,6 +391,17 @@ class LinkAnnotationElement extends AnnotationElement { 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); return this.container; } From 012e15f7a36133994cf4c078f2888701f998ce5d Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 5 Dec 2020 21:27:38 +0100 Subject: [PATCH 5/5] Fix non-standard quadpoints orders for annotations This change requires us to use valid quadpoints arrays in the existing unit tests too due to the normalization. --- src/core/annotation.js | 31 ++++++++- test/pdfs/.gitignore | 1 + test/pdfs/quadpoints.pdf | Bin 0 -> 33054 bytes test/test_manifest.json | 7 ++ test/unit/annotation_spec.js | 122 +++++++++++++++++++++-------------- 5 files changed, 112 insertions(+), 49 deletions(-) create mode 100644 test/pdfs/quadpoints.pdf diff --git a/src/core/annotation.js b/src/core/annotation.js index 2e4cbbb6287b1..d26ddeafbc1aa 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -227,7 +227,36 @@ function getQuadPoints(dict, rect) { 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) { diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 82936b8a7054e..6b7ae9e9a13d9 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -392,6 +392,7 @@ !issue11442_reduced.pdf !issue11549_reduced.pdf !issue8097_reduced.pdf +!quadpoints.pdf !transparent.pdf !xobject-image.pdf !ccitt_EndOfBlock_false.pdf diff --git a/test/pdfs/quadpoints.pdf b/test/pdfs/quadpoints.pdf new file mode 100644 index 0000000000000000000000000000000000000000..72ac73dae02fe2d1227209e4e4751d8b0b2050e7 GIT binary patch literal 33054 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