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