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.
This commit is contained in:
parent
e3b6a9fb23
commit
012e15f7a3
@ -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) {
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -392,6 +392,7 @@
|
||||
!issue11442_reduced.pdf
|
||||
!issue11549_reduced.pdf
|
||||
!issue8097_reduced.pdf
|
||||
!quadpoints.pdf
|
||||
!transparent.pdf
|
||||
!xobject-image.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,
|
||||
"type": "text"
|
||||
},
|
||||
{ "id": "quadpoints",
|
||||
"file": "pdfs/quadpoints.pdf",
|
||||
"md5": "aadbc9bf826b4604c49a994fc8cd72c1",
|
||||
"rounds": 1,
|
||||
"type": "eq",
|
||||
"annotations": true
|
||||
},
|
||||
{ "id": "operator-in-TJ-array",
|
||||
"file": "pdfs/operator-in-TJ-array.pdf",
|
||||
"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];
|
||||
dict.set("QuadPoints", [
|
||||
10,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
10,
|
||||
10,
|
||||
20,
|
||||
10,
|
||||
11,
|
||||
19,
|
||||
19,
|
||||
19,
|
||||
11,
|
||||
11,
|
||||
12,
|
||||
12,
|
||||
13,
|
||||
13,
|
||||
14,
|
||||
14,
|
||||
15,
|
||||
15,
|
||||
16,
|
||||
16,
|
||||
17,
|
||||
17,
|
||||
18,
|
||||
18,
|
||||
19,
|
||||
11,
|
||||
]);
|
||||
expect(getQuadPoints(dict, rect)).toEqual([
|
||||
[
|
||||
{ x: 11, y: 11 },
|
||||
{ x: 12, y: 12 },
|
||||
{ x: 13, y: 13 },
|
||||
{ x: 14, y: 14 },
|
||||
{ x: 10, y: 20 },
|
||||
{ x: 20, y: 20 },
|
||||
{ x: 10, y: 10 },
|
||||
{ x: 20, y: 10 },
|
||||
],
|
||||
[
|
||||
{ x: 15, y: 15 },
|
||||
{ x: 16, y: 16 },
|
||||
{ x: 17, y: 17 },
|
||||
{ x: 18, y: 18 },
|
||||
{ x: 11, y: 19 },
|
||||
{ x: 19, y: 19 },
|
||||
{ x: 11, y: 11 },
|
||||
{ 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 () {
|
||||
@ -1265,7 +1291,7 @@ describe("annotation", function () {
|
||||
annotationDict.set("Type", Name.get("Annot"));
|
||||
annotationDict.set("Subtype", Name.get("Link"));
|
||||
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 xref = new XRefMock([{ ref: annotationRef, data: annotationDict }]);
|
||||
@ -1279,10 +1305,10 @@ describe("annotation", function () {
|
||||
expect(data.annotationType).toEqual(AnnotationType.LINK);
|
||||
expect(data.quadPoints).toEqual([
|
||||
[
|
||||
{ x: 11, y: 11 },
|
||||
{ x: 12, y: 12 },
|
||||
{ x: 13, y: 13 },
|
||||
{ x: 14, y: 14 },
|
||||
{ x: 10, y: 20 },
|
||||
{ x: 20, y: 20 },
|
||||
{ x: 10, y: 10 },
|
||||
{ x: 20, y: 10 },
|
||||
],
|
||||
]);
|
||||
done();
|
||||
@ -3636,7 +3662,7 @@ describe("annotation", function () {
|
||||
highlightDict.set("Type", Name.get("Annot"));
|
||||
highlightDict.set("Subtype", Name.get("Highlight"));
|
||||
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 xref = new XRefMock([{ ref: highlightRef, data: highlightDict }]);
|
||||
@ -3650,10 +3676,10 @@ describe("annotation", function () {
|
||||
expect(data.annotationType).toEqual(AnnotationType.HIGHLIGHT);
|
||||
expect(data.quadPoints).toEqual([
|
||||
[
|
||||
{ x: 11, y: 11 },
|
||||
{ x: 12, y: 12 },
|
||||
{ x: 13, y: 13 },
|
||||
{ x: 14, y: 14 },
|
||||
{ x: 10, y: 20 },
|
||||
{ x: 20, y: 20 },
|
||||
{ x: 10, y: 10 },
|
||||
{ x: 20, y: 10 },
|
||||
],
|
||||
]);
|
||||
done();
|
||||
@ -3709,7 +3735,7 @@ describe("annotation", function () {
|
||||
underlineDict.set("Type", Name.get("Annot"));
|
||||
underlineDict.set("Subtype", Name.get("Underline"));
|
||||
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 xref = new XRefMock([{ ref: underlineRef, data: underlineDict }]);
|
||||
@ -3723,10 +3749,10 @@ describe("annotation", function () {
|
||||
expect(data.annotationType).toEqual(AnnotationType.UNDERLINE);
|
||||
expect(data.quadPoints).toEqual([
|
||||
[
|
||||
{ x: 11, y: 11 },
|
||||
{ x: 12, y: 12 },
|
||||
{ x: 13, y: 13 },
|
||||
{ x: 14, y: 14 },
|
||||
{ x: 10, y: 20 },
|
||||
{ x: 20, y: 20 },
|
||||
{ x: 10, y: 10 },
|
||||
{ x: 20, y: 10 },
|
||||
],
|
||||
]);
|
||||
done();
|
||||
@ -3760,7 +3786,7 @@ describe("annotation", function () {
|
||||
squigglyDict.set("Type", Name.get("Annot"));
|
||||
squigglyDict.set("Subtype", Name.get("Squiggly"));
|
||||
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 xref = new XRefMock([{ ref: squigglyRef, data: squigglyDict }]);
|
||||
@ -3774,10 +3800,10 @@ describe("annotation", function () {
|
||||
expect(data.annotationType).toEqual(AnnotationType.SQUIGGLY);
|
||||
expect(data.quadPoints).toEqual([
|
||||
[
|
||||
{ x: 11, y: 11 },
|
||||
{ x: 12, y: 12 },
|
||||
{ x: 13, y: 13 },
|
||||
{ x: 14, y: 14 },
|
||||
{ x: 10, y: 20 },
|
||||
{ x: 20, y: 20 },
|
||||
{ x: 10, y: 10 },
|
||||
{ x: 20, y: 10 },
|
||||
],
|
||||
]);
|
||||
done();
|
||||
@ -3811,7 +3837,7 @@ describe("annotation", function () {
|
||||
strikeOutDict.set("Type", Name.get("Annot"));
|
||||
strikeOutDict.set("Subtype", Name.get("StrikeOut"));
|
||||
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 xref = new XRefMock([{ ref: strikeOutRef, data: strikeOutDict }]);
|
||||
@ -3825,10 +3851,10 @@ describe("annotation", function () {
|
||||
expect(data.annotationType).toEqual(AnnotationType.STRIKEOUT);
|
||||
expect(data.quadPoints).toEqual([
|
||||
[
|
||||
{ x: 11, y: 11 },
|
||||
{ x: 12, y: 12 },
|
||||
{ x: 13, y: 13 },
|
||||
{ x: 14, y: 14 },
|
||||
{ x: 10, y: 20 },
|
||||
{ x: 20, y: 20 },
|
||||
{ x: 10, y: 10 },
|
||||
{ x: 20, y: 10 },
|
||||
],
|
||||
]);
|
||||
done();
|
||||
|
Loading…
Reference in New Issue
Block a user