Skip to content

Render not displayed annotations in using normal appearance when printing #12395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,8 @@ class WidgetAnnotation extends Annotation {
}

async save(evaluator, task, annotationStorage) {
if (this.data.fieldValue === annotationStorage[this.data.id]) {
const value = annotationStorage[this.data.id];
if (value === this.data.fieldValue || value === undefined) {
return null;
}

Expand All @@ -1156,7 +1157,6 @@ class WidgetAnnotation extends Annotation {
return null;
}

const value = annotationStorage[this.data.id];
const bbox = [
0,
0,
Expand Down Expand Up @@ -1222,7 +1222,13 @@ class WidgetAnnotation extends Annotation {
return null;
}
const value = annotationStorage[this.data.id];
if (value === undefined) {
// The annotation hasn't been rendered so use the appearance
return null;
}

if (value === "") {
// the field is empty: nothing to render
return "";
}

Expand Down Expand Up @@ -1695,7 +1701,16 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
}

if (annotationStorage) {
const value = annotationStorage[this.data.id] || false;
const value = annotationStorage[this.data.id];
if (value === undefined) {
return super.getOperatorList(
evaluator,
task,
renderForms,
annotationStorage
);
}

let appearance;
if (value) {
appearance = this.checkedAppearance;
Expand Down Expand Up @@ -1741,9 +1756,12 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
}

async _saveCheckbox(evaluator, task, annotationStorage) {
const defaultValue = this.data.fieldValue && this.data.fieldValue !== "Off";
const value = annotationStorage[this.data.id];
if (value === undefined) {
return null;
}

const defaultValue = this.data.fieldValue && this.data.fieldValue !== "Off";
if (defaultValue === value) {
return null;
}
Expand Down Expand Up @@ -1780,9 +1798,12 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
}

async _saveRadioButton(evaluator, task, annotationStorage) {
const defaultValue = this.data.fieldValue === this.data.buttonValue;
const value = annotationStorage[this.data.id];
if (value === undefined) {
return null;
}

const defaultValue = this.data.fieldValue === this.data.buttonValue;
if (defaultValue === value) {
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/display/annotation_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
// used and the full array of field values is stored.
storage.getOrCreateValue(
id,
this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : null
this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : undefined
);

const selectElement = document.createElement("select");
Expand Down
1 change: 1 addition & 0 deletions test/pdfs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
!bug766086.pdf
!bug793632.pdf
!bug1020858.pdf
!prefilled_f1040.pdf
!bug1050040.pdf
!bug1200096.pdf
!bug1068432.pdf
Expand Down
Binary file added test/pdfs/prefilled_f1040.pdf
Binary file not shown.
10 changes: 10 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3659,6 +3659,16 @@
"type": "eq",
"about": "CFF font that is drawn with clipping."
},
{ "id": "prefilled_f1040",
"file": "pdfs/prefilled_f1040.pdf",
"md5": "2335da66fb7c2c3b84971597f27785e2",
"rounds": 1,
"type": "eq",
"print": true,
"annotationStorage": {
"1605R": true
Copy link
Contributor

@timvandermeij timvandermeij Oct 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified that this patch itserlf fixes the reported issue, but I can't get this reference test working when checking out this patch and I also don't see how this is supposed to work. The prefilled file seems to only have prefilled content on page 1 and this page is always rendered, so there is an annotation layer and the content is simply in the annotation storage. Checkbox 1605R is also on the first page, so that will also work. I don't see any changes in the output before/after this patch, which confirms my suspicion that this most likely doesn't work as expected.

Did you try the suggestion from #12395 (comment)? You can run the reference tests locally before/after applying this patch to check if the test indeed works as expected.

Since I found that your steps in #12395 (comment) do work, perhaps this test can be made in the same way by providing a PDF with text prefilled on page 3 and then printing it?

}
},
{ "id": "clippath",
"file": "pdfs/clippath.pdf",
"md5": "7ab95c0f106dccd90d6569f241fe8771",
Expand Down
166 changes: 166 additions & 0 deletions test/unit/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,55 @@ describe("annotation", function () {
}, done.fail);
});

it("should render regular text for printing using normal appearance", function (done) {
const textWidgetRef = Ref.get(271, 0);

const appearanceStatesDict = new Dict();
const normalAppearanceDict = new Dict();

const normalAppearanceStream = new StringStream("0.1 0.2 0.3 rg");
normalAppearanceStream.dict = normalAppearanceDict;

appearanceStatesDict.set("N", normalAppearanceStream);
textWidgetDict.set("AP", appearanceStatesDict);

const xref = new XRefMock([
{ ref: textWidgetRef, data: textWidgetDict },
fontRefObj,
]);
const task = new WorkerTask("test print");
partialEvaluator.xref = xref;

AnnotationFactory.create(
xref,
textWidgetRef,
pdfManagerMock,
idFactoryMock
)
.then(annotation => {
const annotationStorage = {};
return annotation.getOperatorList(
partialEvaluator,
task,
false,
annotationStorage
);
})
.then(opList => {
expect(opList.argsArray.length).toEqual(3);
expect(opList.fnArray).toEqual([
OPS.beginAnnotation,
OPS.setFillRGBColor,
OPS.endAnnotation,
]);
expect(opList.argsArray[1]).toEqual(
new Uint8ClampedArray([26, 51, 76])
);
done();
})
.catch(done.fail);
});

it("should render auto-sized text for printing", function (done) {
textWidgetDict.set("DA", "/Helv 0 Tf");

Expand Down Expand Up @@ -2278,6 +2327,64 @@ describe("annotation", function () {
.catch(done.fail);
});

it("should render checkboxes for printing using normal appearance", function (done) {
const appearanceStatesDict = new Dict();
const normalAppearanceDict = new Dict();
const checkedAppearanceDict = new Dict();
const uncheckedAppearanceDict = new Dict();

const checkedStream = new StringStream("0.1 0.2 0.3 rg");
checkedStream.dict = checkedAppearanceDict;

const uncheckedStream = new StringStream("0.3 0.2 0.1 rg");
uncheckedStream.dict = uncheckedAppearanceDict;

checkedAppearanceDict.set("BBox", [0, 0, 8, 8]);
checkedAppearanceDict.set("FormType", 1);
checkedAppearanceDict.set("Matrix", [1, 0, 0, 1, 0, 0]);
normalAppearanceDict.set("Checked", checkedStream);
normalAppearanceDict.set("Off", uncheckedStream);
appearanceStatesDict.set("N", normalAppearanceDict);

buttonWidgetDict.set("AP", appearanceStatesDict);
buttonWidgetDict.set("AS", Name.get("Checked"));

const buttonWidgetRef = Ref.get(124, 0);
const xref = new XRefMock([
{ ref: buttonWidgetRef, data: buttonWidgetDict },
]);
const task = new WorkerTask("test print");

AnnotationFactory.create(
xref,
buttonWidgetRef,
pdfManagerMock,
idFactoryMock
)
.then(annotation => {
const annotationStorage = {};
return annotation.getOperatorList(
partialEvaluator,
task,
false,
annotationStorage
);
})
.then(opList => {
expect(opList.argsArray.length).toEqual(3);
expect(opList.fnArray).toEqual([
OPS.beginAnnotation,
OPS.setFillRGBColor,
OPS.endAnnotation,
]);
expect(opList.argsArray[1]).toEqual(
new Uint8ClampedArray([26, 51, 76])
);
done();
})
.catch(done.fail);
});

it("should save checkboxes", function (done) {
const appearanceStatesDict = new Dict();
const normalAppearanceDict = new Dict();
Expand Down Expand Up @@ -2513,6 +2620,65 @@ describe("annotation", function () {
}, done.fail);
});

it("should render radio buttons for printing using normal appearance", function (done) {
const appearanceStatesDict = new Dict();
const normalAppearanceDict = new Dict();
const checkedAppearanceDict = new Dict();
const uncheckedAppearanceDict = new Dict();

const checkedStream = new StringStream("0.1 0.2 0.3 rg");
checkedStream.dict = checkedAppearanceDict;

const uncheckedStream = new StringStream("0.3 0.2 0.1 rg");
uncheckedStream.dict = uncheckedAppearanceDict;

checkedAppearanceDict.set("BBox", [0, 0, 8, 8]);
checkedAppearanceDict.set("FormType", 1);
checkedAppearanceDict.set("Matrix", [1, 0, 0, 1, 0, 0]);
normalAppearanceDict.set("Checked", checkedStream);
normalAppearanceDict.set("Off", uncheckedStream);
appearanceStatesDict.set("N", normalAppearanceDict);

buttonWidgetDict.set("Ff", AnnotationFieldFlag.RADIO);
buttonWidgetDict.set("AP", appearanceStatesDict);
buttonWidgetDict.set("AS", Name.get("Off"));

const buttonWidgetRef = Ref.get(124, 0);
const xref = new XRefMock([
{ ref: buttonWidgetRef, data: buttonWidgetDict },
]);
const task = new WorkerTask("test print");

AnnotationFactory.create(
xref,
buttonWidgetRef,
pdfManagerMock,
idFactoryMock
)
.then(annotation => {
const annotationStorage = {};
return annotation.getOperatorList(
partialEvaluator,
task,
false,
annotationStorage
);
})
.then(opList => {
expect(opList.argsArray.length).toEqual(3);
expect(opList.fnArray).toEqual([
OPS.beginAnnotation,
OPS.setFillRGBColor,
OPS.endAnnotation,
]);
expect(opList.argsArray[1]).toEqual(
new Uint8ClampedArray([76, 51, 26])
);
done();
})
.catch(done.fail);
});

it("should save radio buttons", function (done) {
const appearanceStatesDict = new Dict();
const normalAppearanceDict = new Dict();
Expand Down