Skip to content

Commit

Permalink
Display widget signature
Browse files Browse the repository at this point in the history
  - but don't validate them for now;
  - Firefox will display a bar to warn that the signature validation is not supported (see https://bugzilla.mozilla.org/show_bug.cgi?id=854315)
  - almost all (all ?) pdf readers display signatures;
  - validation is done in edge but for now it's behind a pref.
  • Loading branch information
calixteman committed Apr 10, 2021
1 parent 6ddc297 commit 5875ebb
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 19 deletions.
1 change: 1 addition & 0 deletions l10n/en-US/chrome.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
# Chrome notification bar messages and buttons
unsupported_feature=This PDF document might not be displayed correctly.
unsupported_feature_forms=This PDF document contains forms. The filling of form fields is not supported.
unsupported_feature_signatures=This PDF document contains digital signatures. Validation of signatures is not supported.
open_with_different_viewer=Open With Different Viewer
open_with_different_viewer.accessKey=o
39 changes: 22 additions & 17 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class AnnotationFactory {
return new ButtonWidgetAnnotation(parameters);
case "Ch":
return new ChoiceWidgetAnnotation(parameters);
case "Sig":
return new SignatureWidgetAnnotation(parameters);
}
warn(
`Unimplemented widget field type "${fieldType}", ` +
Expand Down Expand Up @@ -1151,15 +1153,6 @@ class WidgetAnnotation extends Annotation {

data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
data.hidden = this._hasFlag(data.annotationFlags, AnnotationFlag.HIDDEN);

// Hide signatures because we cannot validate them, and unset the fieldValue
// since it's (most likely) a `Dict` which is non-serializable and will thus
// cause errors when sending annotations to the main-thread (issue 10347).
if (data.fieldType === "Sig") {
data.fieldValue = null;
this.setFlags(AnnotationFlag.HIDDEN);
data.hidden = true;
}
}

/**
Expand Down Expand Up @@ -1201,7 +1194,7 @@ class WidgetAnnotation extends Annotation {
getOperatorList(evaluator, task, renderForms, annotationStorage) {
// Do not render form elements on the canvas when interactive forms are
// enabled. The display layer is responsible for rendering them instead.
if (renderForms) {
if (renderForms && !(this instanceof SignatureWidgetAnnotation)) {
return Promise.resolve(new OperatorList());
}

Expand Down Expand Up @@ -1600,13 +1593,6 @@ class WidgetAnnotation extends Annotation {
}

getFieldObject() {
if (this.data.fieldType === "Sig") {
return {
id: this.data.id,
value: null,
type: "signature",
};
}
return null;
}
}
Expand Down Expand Up @@ -2203,6 +2189,25 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
}
}

class SignatureWidgetAnnotation extends WidgetAnnotation {
constructor(params) {
super(params);

// Unset the fieldValue since it's (most likely) a `Dict` which is
// non-serializable and will thus cause errors when sending annotations
// to the main-thread (issue 10347).
this.data.fieldValue = null;
}

getFieldObject() {
return {
id: this.data.id,
value: null,
type: "signature",
};
}
}

class TextAnnotation extends MarkupAnnotation {
constructor(parameters) {
const DEFAULT_ICON_SIZE = 22; // px
Expand Down
12 changes: 10 additions & 2 deletions src/core/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,12 @@ class PDFDocument {
}

get formInfo() {
const formInfo = { hasFields: false, hasAcroForm: false, hasXfa: false };
const formInfo = {
hasFields: false,
hasAcroForm: false,
hasXfa: false,
hasSignatures: false,
};
const acroForm = this.catalog.acroForm;
if (!acroForm) {
return shadow(this, "formInfo", formInfo);
Expand All @@ -854,9 +859,11 @@ class PDFDocument {
// the first bit of the `SigFlags` integer (see Table 219 in the
// specification).
const sigFlags = acroForm.get("SigFlags");
const hasSignatures = !!(sigFlags & 0x1);
const hasOnlyDocumentSignatures =
!!(sigFlags & 0x1) && this._hasOnlyDocumentSignatures(fields);
hasSignatures && this._hasOnlyDocumentSignatures(fields);
formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures;
formInfo.hasSignatures = hasSignatures;
} catch (ex) {
if (ex instanceof MissingDataException) {
throw ex;
Expand Down Expand Up @@ -894,6 +901,7 @@ class PDFDocument {
IsAcroFormPresent: this.formInfo.hasAcroForm,
IsXFAPresent: this.formInfo.hasXfa,
IsCollectionPresent: !!this.catalog.collection,
IsSignaturesPresent: this.formInfo.hasSignatures,
};

let infoDict;
Expand Down
1 change: 1 addition & 0 deletions src/shared/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ const UNSUPPORTED_FEATURES = {
unknown: "unknown",
forms: "forms",
javaScript: "javaScript",
signatures: "signatures",
smask: "smask",
shadingPattern: "shadingPattern",
/** @deprecated unused */
Expand Down
1 change: 1 addition & 0 deletions test/pdfs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
!issue9458.pdf
!issue9655_reduced.pdf
!issue9915_reduced.pdf
!bug854315.pdf
!issue9940.pdf
!issue10388_reduced.pdf
!issue10438_reduced.pdf
Expand Down
Binary file added test/pdfs/bug854315.pdf
Binary file not shown.
6 changes: 6 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,12 @@
"rounds": 1,
"type": "load"
},
{ "id": "bug854315",
"file": "pdfs/bug854315.pdf",
"md5": "675754c07f71c068d2997905a5456f05",
"rounds": 1,
"type": "eq"
},
{ "id": "bug868745",
"file": "pdfs/bug868745.pdf",
"md5": "86111ea5097dd7daffcdea891ad1b348",
Expand Down
3 changes: 3 additions & 0 deletions test/unit/api_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,7 @@ describe("api", function () {
expect(info.IsAcroFormPresent).toEqual(false);
expect(info.IsXFAPresent).toEqual(false);
expect(info.IsCollectionPresent).toEqual(false);
expect(info.IsSignaturesPresent).toEqual(false);

expect(metadata instanceof Metadata).toEqual(true);
expect(metadata.get("dc:title")).toEqual("Basic API Test");
Expand Down Expand Up @@ -1254,6 +1255,7 @@ describe("api", function () {
expect(info.IsAcroFormPresent).toEqual(false);
expect(info.IsXFAPresent).toEqual(false);
expect(info.IsCollectionPresent).toEqual(false);
expect(info.IsSignaturesPresent).toEqual(false);

expect(metadata).toEqual(null);
expect(contentDispositionFilename).toEqual(null);
Expand Down Expand Up @@ -1282,6 +1284,7 @@ describe("api", function () {
expect(info.IsAcroFormPresent).toEqual(false);
expect(info.IsXFAPresent).toEqual(false);
expect(info.IsCollectionPresent).toEqual(false);
expect(info.IsSignaturesPresent).toEqual(false);

expect(metadata).toEqual(null);
expect(contentDispositionFilename).toEqual(null);
Expand Down
9 changes: 9 additions & 0 deletions test/unit/document_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ describe("document", function () {
const pdfDocument = getDocument(null);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: false,
hasSignatures: false,
hasXfa: false,
hasFields: false,
});
Expand All @@ -90,6 +91,7 @@ describe("document", function () {
let pdfDocument = getDocument(acroForm);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: false,
hasSignatures: false,
hasXfa: false,
hasFields: false,
});
Expand All @@ -98,6 +100,7 @@ describe("document", function () {
pdfDocument = getDocument(acroForm);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: false,
hasSignatures: false,
hasXfa: true,
hasFields: false,
});
Expand All @@ -106,6 +109,7 @@ describe("document", function () {
pdfDocument = getDocument(acroForm);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: false,
hasSignatures: false,
hasXfa: false,
hasFields: false,
});
Expand All @@ -114,6 +118,7 @@ describe("document", function () {
pdfDocument = getDocument(acroForm);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: false,
hasSignatures: false,
hasXfa: true,
hasFields: false,
});
Expand All @@ -127,6 +132,7 @@ describe("document", function () {
let pdfDocument = getDocument(acroForm);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: false,
hasSignatures: false,
hasXfa: false,
hasFields: false,
});
Expand All @@ -135,6 +141,7 @@ describe("document", function () {
pdfDocument = getDocument(acroForm);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: true,
hasSignatures: false,
hasXfa: false,
hasFields: true,
});
Expand All @@ -146,6 +153,7 @@ describe("document", function () {
pdfDocument = getDocument(acroForm);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: true,
hasSignatures: false,
hasXfa: false,
hasFields: true,
});
Expand All @@ -169,6 +177,7 @@ describe("document", function () {
pdfDocument = getDocument(acroForm, xref);
expect(pdfDocument.formInfo).toEqual({
hasAcroForm: false,
hasSignatures: true,
hasXfa: false,
hasFields: true,
});
Expand Down
5 changes: 5 additions & 0 deletions web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,11 @@ const PDFViewerApplication = {
this._delayedFallback(UNSUPPORTED_FEATURES.forms);
}

if (info.IsSignaturesPresent) {
console.warn("Warning: Digital signatures validation is not supported");
this.fallback(UNSUPPORTED_FEATURES.signatures);
}

// Telemetry labels must be C++ variable friendly.
let versionId = "other";
if (KNOWN_VERSIONS.includes(info.PDFFormatVersion)) {
Expand Down

0 comments on commit 5875ebb

Please sign in to comment.