1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-20 15:18:08 +02:00

Merge pull request #15060 from calixteman/annotation_rotation

Rotate annotations based on the MK::R value (bug 1675139)
This commit is contained in:
calixteman 2022-06-21 18:03:09 +02:00 committed by GitHub
commit 8d466f5dac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 562 additions and 78 deletions

View file

@ -1401,4 +1401,47 @@ describe("Interaction", () => {
);
});
});
describe("in bug1675139.pdf", () => {
let pages;
beforeAll(async () => {
pages = await loadAndWait("bug1675139.pdf", getSelector("48R"));
});
afterAll(async () => {
await closePages(pages);
});
it("must check that data-annotation-rotation is correc", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await page.waitForFunction(
"window.PDFViewerApplication.scriptingReady === true"
);
let base = 0;
while (base !== 360) {
for (const [ref, angle] of [
[47, 0],
[42, 90],
[45, 180],
[46, 270],
]) {
const rotation = await page.$eval(
`[data-annotation-id='${ref}R']`,
el => parseInt(el.getAttribute("data-annotation-rotation") || 0)
);
expect(rotation)
.withContext(`In ${browserName}`)
.toEqual((360 + ((360 - (base + angle)) % 360)) % 360);
}
base += 90;
await page.click(getSelector("48R"));
}
})
);
});
});
});

View file

@ -528,3 +528,4 @@
!bug1771477.pdf
!bug1724918.pdf
!issue15053.pdf
!bug1675139.pdf

BIN
test/pdfs/bug1675139.pdf Executable file

Binary file not shown.

View file

@ -6583,5 +6583,44 @@
"rounds": 1,
"type": "eq",
"annotations": true
},
{ "id": "bug1675139",
"file": "pdfs/bug1675139.pdf",
"md5": "052c2c3dcc7ef4d4ac622282cb0fb17a",
"rounds": 1,
"type": "eq",
"annotations": true
},
{ "id": "bug1675139-print",
"file": "pdfs/bug1675139.pdf",
"md5": "052c2c3dcc7ef4d4ac622282cb0fb17a",
"rounds": 1,
"type": "eq",
"print": true,
"annotationStorage": {
"42R": {
"value": "pi/2"
},
"46R": {
"value": "3*pi/2",
"rotation": 180
},
"47R": {
"value": "0*pi/2"
},
"45R": {
"value": "pi"
},
"55R": {
"value": "C",
"rotation": 90
},
"52R": {
"value": "Yes"
},
"56R": {
"rotation": 270
}
}
}
]

View file

@ -2058,6 +2058,52 @@ describe("annotation", function () {
);
});
it("should save rotated text", async function () {
const textWidgetRef = Ref.get(123, 0);
const xref = new XRefMock([
{ ref: textWidgetRef, data: textWidgetDict },
helvRefObj,
]);
partialEvaluator.xref = xref;
const task = new WorkerTask("test save");
const annotation = await AnnotationFactory.create(
xref,
textWidgetRef,
pdfManagerMock,
idFactoryMock
);
const annotationStorage = new Map();
annotationStorage.set(annotation.data.id, {
value: "hello world",
rotation: 90,
});
const data = await annotation.save(
partialEvaluator,
task,
annotationStorage
);
expect(data.length).toEqual(2);
const [oldData, newData] = data;
expect(oldData.ref).toEqual(Ref.get(123, 0));
expect(newData.ref).toEqual(Ref.get(2, 0));
oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)");
expect(oldData.data).toEqual(
"123 0 obj\n" +
"<< /Type /Annot /Subtype /Widget /FT /Tx /DA (/Helv 5 Tf) /DR " +
"<< /Font << /Helv 314 0 R>>>> /Rect [0 0 32 10] " +
"/V (hello world) /AP << /N 2 0 R>> /M (date) /MK << /R 90>>>>\nendobj\n"
);
expect(newData.data).toEqual(
"2 0 obj\n<< /Length 74 /Subtype /Form /Resources " +
"<< /Font << /Helv 314 0 R>>>> /BBox [0 0 32 10] /Matrix [0 1 -1 0 32 0]>> stream\n" +
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 0 Tm 2 3.04 Td (hello world) Tj " +
"ET Q EMC\nendstream\nendobj\n"
);
});
it("should get field object for usage in JS sandbox", async function () {
const textWidgetRef = Ref.get(123, 0);
const xDictRef = Ref.get(141, 0);
@ -2612,6 +2658,57 @@ describe("annotation", function () {
expect(data).toEqual(null);
});
it("should save rotated checkboxes", async function () {
const appearanceStatesDict = new Dict();
const normalAppearanceDict = new Dict();
normalAppearanceDict.set("Checked", Ref.get(314, 0));
normalAppearanceDict.set("Off", Ref.get(271, 0));
appearanceStatesDict.set("N", normalAppearanceDict);
buttonWidgetDict.set("AP", appearanceStatesDict);
buttonWidgetDict.set("V", Name.get("Off"));
const buttonWidgetRef = Ref.get(123, 0);
const xref = new XRefMock([
{ ref: buttonWidgetRef, data: buttonWidgetDict },
]);
partialEvaluator.xref = xref;
const task = new WorkerTask("test save");
const annotation = await AnnotationFactory.create(
xref,
buttonWidgetRef,
pdfManagerMock,
idFactoryMock
);
const annotationStorage = new Map();
annotationStorage.set(annotation.data.id, { value: true, rotation: 180 });
const [oldData] = await annotation.save(
partialEvaluator,
task,
annotationStorage
);
oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)");
expect(oldData.ref).toEqual(Ref.get(123, 0));
expect(oldData.data).toEqual(
"123 0 obj\n" +
"<< /Type /Annot /Subtype /Widget /FT /Btn " +
"/AP << /N << /Checked 314 0 R /Off 271 0 R>>>> " +
"/V /Checked /AS /Checked /M (date) /MK << /R 180>>>>\nendobj\n"
);
annotationStorage.set(annotation.data.id, { value: false });
const data = await annotation.save(
partialEvaluator,
task,
annotationStorage
);
expect(data).toEqual(null);
});
it("should handle radio buttons with a field value", async function () {
const parentDict = new Dict();
parentDict.set("V", Name.get("1"));
@ -3485,6 +3582,67 @@ describe("annotation", function () {
);
});
it("should save rotated choice", async function () {
choiceWidgetDict.set("Opt", ["A", "B", "C"]);
choiceWidgetDict.set("V", "A");
const choiceWidgetRef = Ref.get(123, 0);
const xref = new XRefMock([
{ ref: choiceWidgetRef, data: choiceWidgetDict },
fontRefObj,
]);
partialEvaluator.xref = xref;
const task = new WorkerTask("test save");
const annotation = await AnnotationFactory.create(
xref,
choiceWidgetRef,
pdfManagerMock,
idFactoryMock
);
const annotationStorage = new Map();
annotationStorage.set(annotation.data.id, { value: "C", rotation: 270 });
const data = await annotation.save(
partialEvaluator,
task,
annotationStorage
);
expect(data.length).toEqual(2);
const [oldData, newData] = data;
expect(oldData.ref).toEqual(Ref.get(123, 0));
expect(newData.ref).toEqual(Ref.get(2, 0));
oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)");
expect(oldData.data).toEqual(
"123 0 obj\n" +
"<< /Type /Annot /Subtype /Widget /FT /Ch /DA (/Helv 5 Tf) /DR " +
"<< /Font << /Helv 314 0 R>>>> " +
"/Rect [0 0 32 10] /Opt [(A) (B) (C)] /V (C) " +
"/AP << /N 2 0 R>> /M (date) /MK << /R 270>>>>\nendobj\n"
);
expect(newData.data).toEqual(
[
"2 0 obj",
"<< /Length 170 /Subtype /Form /Resources << /Font << /Helv 314 0 R>>>> " +
"/BBox [0 0 32 10] /Matrix [0 -1 1 0 0 10]>> stream",
"/Tx BMC q",
"1 1 10 32 re W n",
"0.600006 0.756866 0.854904 rg",
"1 11.75 10 6.75 re f",
"BT",
"/Helv 5 Tf",
"1 0 0 1 0 32 Tm",
"2 -5.88 Td (A) Tj",
"0 -6.75 Td (B) Tj",
"0 -6.75 Td (C) Tj",
"ET Q EMC",
"endstream",
"endobj\n",
].join("\n")
);
});
it("should save choice", async function () {
choiceWidgetDict.set("Opt", ["A", "B", "C"]);
choiceWidgetDict.set("V", "A");

View file

@ -1333,6 +1333,7 @@ describe("api", function () {
page: 0,
strokeColor: null,
fillColor: null,
rotation: 0,
type: "text",
},
],
@ -1354,6 +1355,7 @@ describe("api", function () {
page: 0,
strokeColor: null,
fillColor: new Uint8ClampedArray([192, 192, 192]),
rotation: 0,
type: "button",
},
],