Skip to content

Commit

Permalink
Add support for missing appearances for hightlights, strikeout, squig…
Browse files Browse the repository at this point in the history
…gly and underline annotations.
  • Loading branch information
calixteman committed Aug 23, 2020
1 parent 7df8aa3 commit fc25c65
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 5 deletions.
147 changes: 142 additions & 5 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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];
},
});
}
}
}
}
Expand All @@ -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];
},
});
}
}
}
}
Expand All @@ -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];
},
});
}
}
}
}
Expand All @@ -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];
},
});
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions test/pdfs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
!issue7403.pdf
!issue7406.pdf
!issue7426.pdf
!bug1538111.pdf
!issue7439.pdf
!issue7446.pdf
!issue7492.pdf
Expand Down Expand Up @@ -114,6 +115,7 @@
!bug1252420.pdf
!bug1513120_reduced.pdf
!bug1552113.pdf
!annotation_markup_multiline_no_ap.pdf
!issue9949.pdf
!bug1308536.pdf
!bug1337429.pdf
Expand Down
Binary file added test/pdfs/annotation_markup_multiline_no_ap.pdf
Binary file not shown.
Binary file added test/pdfs/bug1538111.pdf
Binary file not shown.
12 changes: 12 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,12 @@
"lastPage": 1,
"type": "eq"
},
{ "id": "bug1538111",
"file": "pdfs/bug1538111.pdf",
"md5": "e2cd397bf67eddb1f5f642f2eaba661d",
"rounds": 1,
"type": "eq"
},
{ "id": "issue5509",
"file": "pdfs/issue5509.pdf",
"md5": "1975ef8db7355b1d691bc79d0749574b",
Expand Down Expand Up @@ -3650,6 +3656,12 @@
"type": "eq",
"forms": true
},
{ "id": "annotation_markup_multiline_no_ap",
"file": "pdfs/annotation_markup_multiline_no_ap.pdf",
"md5": "3f3635cfc25d132fb1054042e520e297",
"rounds": 1,
"type": "eq"
},
{ "id": "annotation-tx3-annotations",
"file": "pdfs/annotation-tx3.pdf",
"md5": "3aec45c6465ca4959c25df17c4356a1c",
Expand Down

0 comments on commit fc25c65

Please sign in to comment.