From fc25c654eacf1955012f9c3d0f8dcbcd02b4fad2 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Fri, 7 Aug 2020 20:46:41 +0200 Subject: [PATCH] Add support for missing appearances for hightlights, strikeout, squiggly and underline annotations. --- src/core/annotation.js | 147 +++++++++++++++++- test/pdfs/.gitignore | 2 + .../annotation_markup_multiline_no_ap.pdf | Bin 0 -> 2068 bytes test/pdfs/bug1538111.pdf | Bin 0 -> 14953 bytes test/test_manifest.json | 12 ++ 5 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 test/pdfs/annotation_markup_multiline_no_ap.pdf create mode 100644 test/pdfs/bug1538111.pdf diff --git a/src/core/annotation.js b/src/core/annotation.js index 5fe3d865476209..6015b19f3ed2a9 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -200,7 +200,10 @@ function getQuadPoints(dict, rect) { // The quadpoints should be ignored if any coordinate in the array // lies outside the region specified by the rectangle. - if (x < rect[0] || x > rect[2] || y < rect[1] || y > rect[3]) { + if ( + rect !== null && + (x < rect[0] || x > rect[2] || y < rect[1] || y > rect[3]) + ) { return null; } quadPointsLists[i].push({ x, y }); @@ -791,6 +794,64 @@ class MarkupAnnotation extends Annotation { setCreationDate(creationDate) { this.creationDate = isString(creationDate) ? creationDate : null; } + + _setDefaultAppearance({ + xref, + strokeColor, + fillColor, + blendMode, + pointsCallback, + }) { + let minX = Number.MAX_VALUE; + let minY = Number.MAX_VALUE; + let maxX = Number.MIN_VALUE; + let maxY = Number.MIN_VALUE; + + const buffer = ["q"]; + if (strokeColor) { + buffer.push(`${strokeColor[0]} ${strokeColor[1]} ${strokeColor[2]} RG`); + } + if (fillColor) { + buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`); + } + + for (const points of this.data.quadPoints) { + const [mX, MX, mY, MY] = pointsCallback(buffer, points); + minX = Math.min(minX, mX); + maxX = Math.max(maxX, MX); + minY = Math.min(minY, mY); + maxY = Math.max(maxY, MY); + } + buffer.push("Q"); + + const formDict = new Dict(xref); + const appearanceStreamDict = new Dict(xref); + appearanceStreamDict.set("Subtype", Name.get("Form")); + + const appearanceStream = new StringStream(buffer.join(" ")); + appearanceStream.dict = appearanceStreamDict; + formDict.set("Fm0", appearanceStream); + + const gsDict = new Dict(xref); + if (blendMode) { + gsDict.set("BM", Name.get(blendMode)); + } + + const stateDict = new Dict(xref); + stateDict.set("GS0", gsDict); + + const resources = new Dict(xref); + resources.set("ExtGState", stateDict); + resources.set("XObject", formDict); + + const appearanceDict = new Dict(xref); + appearanceDict.set("Resources", resources); + const bbox = (this.data.rect = [minX, minY, maxX, maxY]); + appearanceDict.set("BBox", bbox); + + this.appearance = new StringStream("/GS0 gs /Fm0 Do"); + this.appearance.dict = appearanceDict; + } } class WidgetAnnotation extends Annotation { @@ -1854,9 +1915,26 @@ class HighlightAnnotation extends MarkupAnnotation { this.data.annotationType = AnnotationType.HIGHLIGHT; - const quadPoints = getQuadPoints(parameters.dict, this.rectangle); + const quadPoints = getQuadPoints(parameters.dict, null); if (quadPoints) { this.data.quadPoints = quadPoints; + if (!this.appearance) { + // Default color is yellow + const fillColor = parameters.dict.getArray("C") || [1, 1, 0]; + this._setDefaultAppearance({ + xref: parameters.xref, + fillColor, + blendMode: "Multiply", + pointsCallback: (buffer, points) => { + buffer.push(`${points[0].x} ${points[0].y} m`); + buffer.push(`${points[1].x} ${points[1].y} l`); + buffer.push(`${points[3].x} ${points[3].y} l`); + buffer.push(`${points[2].x} ${points[2].y} l`); + buffer.push("f"); + return [points[0].x, points[1].x, points[3].y, points[1].y]; + }, + }); + } } } } @@ -1867,9 +1945,23 @@ class UnderlineAnnotation extends MarkupAnnotation { this.data.annotationType = AnnotationType.UNDERLINE; - const quadPoints = getQuadPoints(parameters.dict, this.rectangle); + const quadPoints = getQuadPoints(parameters.dict, null); if (quadPoints) { this.data.quadPoints = quadPoints; + if (!this.appearance) { + // Default color is black + const strokeColor = parameters.dict.getArray("C") || [0, 0, 0]; + this._setDefaultAppearance({ + xref: parameters.xref, + strokeColor, + pointsCallback: (buffer, points) => { + buffer.push(`${points[2].x} ${points[2].y} m`); + buffer.push(`${points[3].x} ${points[3].y} l`); + buffer.push("S"); + return [points[0].x, points[1].x, points[3].y, points[1].y]; + }, + }); + } } } } @@ -1880,9 +1972,32 @@ class SquigglyAnnotation extends MarkupAnnotation { this.data.annotationType = AnnotationType.SQUIGGLY; - const quadPoints = getQuadPoints(parameters.dict, this.rectangle); + const quadPoints = getQuadPoints(parameters.dict, null); if (quadPoints) { this.data.quadPoints = quadPoints; + if (!this.appearance) { + // Default color is black + const strokeColor = parameters.dict.getArray("C") || [0, 0, 0]; + this._setDefaultAppearance({ + xref: parameters.xref, + strokeColor, + pointsCallback: (buffer, points) => { + const dy = (points[0].y - points[2].y) / 6; + let shift = dy; + let x = points[2].x; + const y = points[2].y; + const xEnd = points[3].x; + buffer.push(`${x} ${y + shift} m`); + do { + x += 2; + shift = shift === 0 ? dy : 0; + buffer.push(`${x} ${y + shift} l`); + } while (x < xEnd); + buffer.push("S"); + return [points[2].x, xEnd, y - 2 * dy, y + 2 * dy]; + }, + }); + } } } } @@ -1893,9 +2008,31 @@ class StrikeOutAnnotation extends MarkupAnnotation { this.data.annotationType = AnnotationType.STRIKEOUT; - const quadPoints = getQuadPoints(parameters.dict, this.rectangle); + const quadPoints = getQuadPoints(parameters.dict, null); if (quadPoints) { this.data.quadPoints = quadPoints; + if (!this.appearance) { + // Default color is black + const strokeColor = parameters.dict.getArray("C") || [0, 0, 0]; + this._setDefaultAppearance({ + xref: parameters.xref, + strokeColor, + pointsCallback: (buffer, points) => { + buffer.push( + `${(points[0].x + points[2].x) / 2} ${ + (points[0].y + points[2].y) / 2 + } m` + ); + buffer.push( + `${(points[1].x + points[3].x) / 2} ${ + (points[1].y + points[3].y) / 2 + } l` + ); + 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 3da0e6caba35d9..c0d5e731f77fff 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -40,6 +40,7 @@ !issue7403.pdf !issue7406.pdf !issue7426.pdf +!bug1538111.pdf !issue7439.pdf !issue7446.pdf !issue7492.pdf @@ -114,6 +115,7 @@ !bug1252420.pdf !bug1513120_reduced.pdf !bug1552113.pdf +!annotation_markup_multiline_no_ap.pdf !issue9949.pdf !bug1308536.pdf !bug1337429.pdf diff --git a/test/pdfs/annotation_markup_multiline_no_ap.pdf b/test/pdfs/annotation_markup_multiline_no_ap.pdf new file mode 100644 index 0000000000000000000000000000000000000000..deac036e184c41276e1cc1547a9401d9005ee29c GIT binary patch literal 2068 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