From fa6cebf0454093ba46f874487b5f788337e30f9d Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sun, 21 Feb 2021 17:10:35 +0100 Subject: [PATCH] Implement rendering square/circle annotations without appearance stream --- src/core/annotation.js | 173 ++++++++++++++---- test/pdfs/.gitignore | 1 + ...ation-square-circle-without-appearance.pdf | Bin 0 -> 16007 bytes test/test_manifest.json | 7 + 4 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 test/pdfs/annotation-square-circle-without-appearance.pdf diff --git a/src/core/annotation.js b/src/core/annotation.js index f1b82f7e0..02dc2c273 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -192,6 +192,33 @@ class AnnotationFactory { } } +function getRgbColor(color) { + const rgbColor = new Uint8ClampedArray(3); + if (!Array.isArray(color)) { + return rgbColor; + } + + switch (color.length) { + case 0: // Transparent, which we indicate with a null value + return null; + + case 1: // Convert grayscale to RGB + ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); + return rgbColor; + + case 3: // Convert RGB percentages to RGB + ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); + return rgbColor; + + case 4: // Convert CMYK to RGB + ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); + return rgbColor; + + default: + return rgbColor; + } +} + function getQuadPoints(dict, rect) { if (!dict.has("QuadPoints")) { return null; @@ -462,36 +489,7 @@ class Annotation { * 4 (CMYK) elements */ setColor(color) { - const rgbColor = new Uint8ClampedArray(3); - if (!Array.isArray(color)) { - this.color = rgbColor; - return; - } - - switch (color.length) { - case 0: // Transparent, which we indicate with a null value - this.color = null; - break; - - case 1: // Convert grayscale to RGB - ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - - case 3: // Convert RGB percentages to RGB - ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - - case 4: // Convert CMYK to RGB - ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - - default: - this.color = rgbColor; - break; - } + this.color = getRgbColor(color); } /** @@ -929,7 +927,22 @@ class MarkupAnnotation extends Annotation { buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`); } - for (const points of this.data.quadPoints) { + let pointsArray = this.data.quadPoints; + if (!pointsArray) { + // If there are no quadpoints, the rectangle should be used instead. + // Convert the rectangle definition to a points array similar to how the + // quadpoints are defined. + pointsArray = [ + [ + { x: this.rectangle[0], y: this.rectangle[3] }, + { x: this.rectangle[2], y: this.rectangle[3] }, + { x: this.rectangle[0], y: this.rectangle[1] }, + { x: this.rectangle[2], y: this.rectangle[1] }, + ], + ]; + } + + for (const points of pointsArray) { const [mX, MX, mY, MY] = pointsCallback(buffer, points); minX = Math.min(minX, mX); maxX = Math.max(maxX, MX); @@ -2278,6 +2291,43 @@ class SquareAnnotation extends MarkupAnnotation { super(parameters); this.data.annotationType = AnnotationType.SQUARE; + + if (!this.appearance) { + // The default stroke color is black. + const strokeColor = this.color + ? Array.from(this.color).map(c => c / 255) + : [0, 0, 0]; + + // The default fill color is transparent. + let fillColor = null; + let interiorColor = parameters.dict.getArray("IC"); + if (interiorColor) { + interiorColor = getRgbColor(interiorColor); + fillColor = interiorColor + ? Array.from(interiorColor).map(c => c / 255) + : null; + } + + this._setDefaultAppearance({ + xref: parameters.xref, + extra: `${this.borderStyle.width} w`, + strokeColor, + fillColor, + pointsCallback: (buffer, points) => { + const x = points[2].x + this.borderStyle.width / 2; + const y = points[2].y + this.borderStyle.width / 2; + const width = points[3].x - points[2].x - this.borderStyle.width; + const height = points[1].y - points[3].y - this.borderStyle.width; + buffer.push(`${x} ${y} ${width} ${height} re`); + if (fillColor) { + buffer.push("B"); + } else { + buffer.push("S"); + } + return [points[0].x, points[1].x, points[3].y, points[1].y]; + }, + }); + } } } @@ -2286,6 +2336,67 @@ class CircleAnnotation extends MarkupAnnotation { super(parameters); this.data.annotationType = AnnotationType.CIRCLE; + + if (!this.appearance) { + // The default stroke color is black. + const strokeColor = this.color + ? Array.from(this.color).map(c => c / 255) + : [0, 0, 0]; + + // The default fill color is transparent. + let fillColor = null; + let interiorColor = parameters.dict.getArray("IC"); + if (interiorColor) { + interiorColor = getRgbColor(interiorColor); + fillColor = interiorColor + ? Array.from(interiorColor).map(c => c / 255) + : null; + } + + // Circles are approximated by Bézier curves with four segments since + // there is no circle primitive in the PDF specification. For the control + // points distance, see https://stackoverflow.com/a/27863181. + const controlPointsDistance = (4 / 3) * Math.tan(Math.PI / (2 * 4)); + + this._setDefaultAppearance({ + xref: parameters.xref, + extra: `${this.borderStyle.width} w`, + strokeColor, + fillColor, + pointsCallback: (buffer, points) => { + const x0 = points[0].x + this.borderStyle.width / 2; + const y0 = points[0].y - this.borderStyle.width / 2; + const x1 = points[3].x - this.borderStyle.width / 2; + const y1 = points[3].y + this.borderStyle.width / 2; + const xMid = x0 + (x1 - x0) / 2; + const yMid = y0 + (y1 - y0) / 2; + const xOffset = ((x1 - x0) / 2) * controlPointsDistance; + const yOffset = ((y1 - y0) / 2) * controlPointsDistance; + + buffer.push(`${xMid} ${y1} m`); + buffer.push( + `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c` + ); + buffer.push( + `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c` + ); + buffer.push( + `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c` + ); + buffer.push( + `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c` + ); + + buffer.push("h"); + if (fillColor) { + buffer.push("B"); + } else { + buffer.push("S"); + } + return [points[0].x, points[1].x, points[3].y, points[1].y]; + }, + }); + } } } diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index f5e03e23c..324651782 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -395,6 +395,7 @@ !annotation-line.pdf !bug1669099.pdf !annotation-square-circle.pdf +!annotation-square-circle-without-appearance.pdf !annotation-stamp.pdf !annotation-fileattachment.pdf !annotation-text-widget.pdf diff --git a/test/pdfs/annotation-square-circle-without-appearance.pdf b/test/pdfs/annotation-square-circle-without-appearance.pdf new file mode 100644 index 0000000000000000000000000000000000000000..263f4f262530fbdea8b1c5cb81a969dcf246098e GIT binary patch literal 16007 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

e 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