Skip to content

Commit

Permalink
Add support for checkboxes printing
Browse files Browse the repository at this point in the history
  • Loading branch information
calixteman committed Jul 24, 2020
1 parent 584902d commit abb2825
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 15 deletions.
67 changes: 55 additions & 12 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,21 @@ class AnnotationFactory {
* instance.
*/
static create(xref, ref, pdfManager, idFactory) {
return pdfManager.ensure(this, "_create", [
xref,
ref,
pdfManager,
idFactory,
]);
return pdfManager.ensureDoc("acroForm").then(acroForm => {
return pdfManager.ensure(this, "_create", [
xref,
ref,
pdfManager,
idFactory,
acroForm,
]);
});
}

/**
* @private
*/
static _create(xref, ref, pdfManager, idFactory) {
static _create(xref, ref, pdfManager, idFactory, acroForm) {
const dict = xref.fetchIfRef(ref);
if (!isDict(dict)) {
return undefined;
Expand All @@ -78,6 +81,7 @@ class AnnotationFactory {
subtype,
id,
pdfManager,
acroForm: acroForm instanceof Dict ? acroForm : Dict.empty,
};

switch (subtype) {
Expand Down Expand Up @@ -471,6 +475,7 @@ class Annotation {
*/
setAppearance(dict) {
this.appearance = null;
this.checkedAppearance = null;

const appearanceStates = dict.get("AP");
if (!isDict(appearanceStates)) {
Expand Down Expand Up @@ -514,8 +519,9 @@ class Annotation {
return Promise.resolve(new OperatorList());
}

const appearance = this.appearance;
const data = this.data;
const appearanceDict = this.appearance.dict;
const appearanceDict = appearance.dict;
const resourcesPromise = this.loadResources([
"ExtGState",
"ColorSpace",
Expand All @@ -533,14 +539,14 @@ class Annotation {
opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
return evaluator
.getOperatorList({
stream: this.appearance,
stream: appearance,
task,
resources,
operatorList: opList,
})
.then(() => {
opList.addOp(OPS.endAnnotation, []);
this.appearance.reset();
appearance.reset();
return opList;
});
});
Expand Down Expand Up @@ -795,11 +801,16 @@ class WidgetAnnotation extends Annotation {
getArray: true,
});
data.alternativeText = stringToPDFString(dict.get("TU") || "");
data.defaultAppearance = getInheritableProperty({ dict, key: "DA" }) || "";
data.defaultAppearance =
getInheritableProperty({ dict, key: "DA" }) ||
params.acroForm.get("DA") ||
"";
const fieldType = getInheritableProperty({ dict, key: "FT" });
data.fieldType = isName(fieldType) ? fieldType.name : null;
this.fieldResources =
getInheritableProperty({ dict, key: "DR" }) || Dict.empty;
getInheritableProperty({ dict, key: "DR" }) ||
params.acroForm.get("DR") ||
Dict.empty;

data.fieldFlags = getInheritableProperty({ dict, key: "Ff" });
if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) {
Expand Down Expand Up @@ -980,6 +991,31 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
}
}

getOperatorList(evaluator, task, renderForms, annotationStorage) {
if (annotationStorage) {
const value = annotationStorage[this.data.id] || false;
if (value && this.checkedAppearance) {
const savedAppearance = this.appearance;
this.appearance = this.checkedAppearance;
const operatorList = super.getOperatorList(
evaluator,
task,
renderForms,
annotationStorage
);
this.appearance = savedAppearance;
return operatorList;
}
return Promise.resolve(new OperatorList());
}
return super.getOperatorList(
evaluator,
task,
renderForms,
annotationStorage
);
}

_processCheckBox(params) {
if (isName(this.data.fieldValue)) {
this.data.fieldValue = this.data.fieldValue.name;
Expand All @@ -1003,6 +1039,13 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {

this.data.exportValue =
exportValues[0] === "Off" ? exportValues[1] : exportValues[0];

const normalAppearanceState = customAppearance.get("N");
if (!isDict(normalAppearanceState)) {
return;
}

this.checkedAppearance = normalAppearanceState.get(this.data.exportValue);
}

_processRadioButton(params) {
Expand Down
16 changes: 14 additions & 2 deletions src/display/annotation_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -542,16 +542,28 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
* @returns {HTMLSectionElement}
*/
render() {
const storage = this.annotationStorage;
const data = this.data;
const id = data.id;
const value = storage.getOrCreateValue(
id,
data.fieldValue && data.fieldValue !== "Off"
);

this.container.className = "buttonWidgetAnnotation checkBox";

const element = document.createElement("input");
element.disabled = this.data.readOnly;
element.disabled = data.readOnly;
element.type = "checkbox";
element.name = this.data.fieldName;
if (this.data.fieldValue && this.data.fieldValue !== "Off") {
if (value) {
element.setAttribute("checked", true);
}

element.addEventListener("change", function (event) {
storage.setValue(id, event.target.checked);
});

this.container.appendChild(element);
return this.container;
}
Expand Down
81 changes: 80 additions & 1 deletion test/unit/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ import {
import { createIdFactory, XRefMock } from "./test_utils.js";
import { Dict, Name, Ref } from "../../src/core/primitives.js";
import { Lexer, Parser } from "../../src/core/parser.js";
import { PartialEvaluator } from "../../src/core/evaluator.js";
import { StringStream } from "../../src/core/stream.js";
import { WorkerTask } from "../../src/core/worker.js";

describe("annotation", function () {
class PDFManagerMock {
constructor(params) {
this.docBaseUrl = params.docBaseUrl || null;
this.pdfDocument = {
acroForm: new Dict(),
};
}

ensure(obj, prop, args) {
Expand All @@ -49,21 +54,41 @@ describe("annotation", function () {
}
});
}

ensureDoc(prop, args) {
return this.ensure(this.pdfDocument, prop, args);
}
}

let pdfManagerMock, idFactoryMock;
function HandlerMock() {
this.inputs = [];
}
HandlerMock.prototype = {
send(name, data) {
this.inputs.push({ name, data });
},
};

let pdfManagerMock, idFactoryMock, partialEvaluator;

beforeAll(function (done) {
pdfManagerMock = new PDFManagerMock({
docBaseUrl: null,
});
idFactoryMock = createIdFactory(/* pageIndex = */ 0);
partialEvaluator = new PartialEvaluator({
xref: new XRefMock(),
handler: new HandlerMock(),
pageIndex: 0,
idFactory: createIdFactory(/* pageIndex = */ 0),
});
done();
});

afterAll(function () {
pdfManagerMock = null;
idFactoryMock = null;
partialEvaluator = null;
});

describe("AnnotationFactory", function () {
Expand Down Expand Up @@ -1659,6 +1684,60 @@ describe("annotation", function () {
done();
}, done.fail);
});

it("should get rendered annotation for printing", function (done) {
buttonWidgetDict.set("V", Name.get("1"));

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

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

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

buttonWidgetDict.set("AP", appearanceStatesDict);

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 id = annotation.data.id;
const annotationStorage = {};
annotationStorage[id] = true;
return annotation.getOperatorList(
partialEvaluator,
task,
false,
annotationStorage
);
}, done.fail)
.then(opList => {
expect(opList.argsArray.length).toEqual(3);
expect(opList.argsArray[1]).toEqual(
new Uint8ClampedArray([26, 51, 76])
);
done();
}, done.fail);
});
});

describe("ChoiceWidgetAnnotation", function () {
Expand Down

0 comments on commit abb2825

Please sign in to comment.