Display widget signature
- 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.
This commit is contained in:
parent
6ddc297170
commit
5875ebb1ca
@ -15,5 +15,6 @@
|
|||||||
# Chrome notification bar messages and buttons
|
# Chrome notification bar messages and buttons
|
||||||
unsupported_feature=This PDF document might not be displayed correctly.
|
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_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=Open With Different Viewer
|
||||||
open_with_different_viewer.accessKey=o
|
open_with_different_viewer.accessKey=o
|
||||||
|
@ -126,6 +126,8 @@ class AnnotationFactory {
|
|||||||
return new ButtonWidgetAnnotation(parameters);
|
return new ButtonWidgetAnnotation(parameters);
|
||||||
case "Ch":
|
case "Ch":
|
||||||
return new ChoiceWidgetAnnotation(parameters);
|
return new ChoiceWidgetAnnotation(parameters);
|
||||||
|
case "Sig":
|
||||||
|
return new SignatureWidgetAnnotation(parameters);
|
||||||
}
|
}
|
||||||
warn(
|
warn(
|
||||||
`Unimplemented widget field type "${fieldType}", ` +
|
`Unimplemented widget field type "${fieldType}", ` +
|
||||||
@ -1151,15 +1153,6 @@ class WidgetAnnotation extends Annotation {
|
|||||||
|
|
||||||
data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
|
data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
|
||||||
data.hidden = this._hasFlag(data.annotationFlags, AnnotationFlag.HIDDEN);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1201,7 +1194,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
getOperatorList(evaluator, task, renderForms, annotationStorage) {
|
getOperatorList(evaluator, task, renderForms, annotationStorage) {
|
||||||
// Do not render form elements on the canvas when interactive forms are
|
// Do not render form elements on the canvas when interactive forms are
|
||||||
// enabled. The display layer is responsible for rendering them instead.
|
// enabled. The display layer is responsible for rendering them instead.
|
||||||
if (renderForms) {
|
if (renderForms && !(this instanceof SignatureWidgetAnnotation)) {
|
||||||
return Promise.resolve(new OperatorList());
|
return Promise.resolve(new OperatorList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1600,13 +1593,6 @@ class WidgetAnnotation extends Annotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFieldObject() {
|
getFieldObject() {
|
||||||
if (this.data.fieldType === "Sig") {
|
|
||||||
return {
|
|
||||||
id: this.data.id,
|
|
||||||
value: null,
|
|
||||||
type: "signature",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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 {
|
class TextAnnotation extends MarkupAnnotation {
|
||||||
constructor(parameters) {
|
constructor(parameters) {
|
||||||
const DEFAULT_ICON_SIZE = 22; // px
|
const DEFAULT_ICON_SIZE = 22; // px
|
||||||
|
@ -828,7 +828,12 @@ class PDFDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get formInfo() {
|
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;
|
const acroForm = this.catalog.acroForm;
|
||||||
if (!acroForm) {
|
if (!acroForm) {
|
||||||
return shadow(this, "formInfo", formInfo);
|
return shadow(this, "formInfo", formInfo);
|
||||||
@ -854,9 +859,11 @@ class PDFDocument {
|
|||||||
// the first bit of the `SigFlags` integer (see Table 219 in the
|
// the first bit of the `SigFlags` integer (see Table 219 in the
|
||||||
// specification).
|
// specification).
|
||||||
const sigFlags = acroForm.get("SigFlags");
|
const sigFlags = acroForm.get("SigFlags");
|
||||||
|
const hasSignatures = !!(sigFlags & 0x1);
|
||||||
const hasOnlyDocumentSignatures =
|
const hasOnlyDocumentSignatures =
|
||||||
!!(sigFlags & 0x1) && this._hasOnlyDocumentSignatures(fields);
|
hasSignatures && this._hasOnlyDocumentSignatures(fields);
|
||||||
formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures;
|
formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures;
|
||||||
|
formInfo.hasSignatures = hasSignatures;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex instanceof MissingDataException) {
|
if (ex instanceof MissingDataException) {
|
||||||
throw ex;
|
throw ex;
|
||||||
@ -894,6 +901,7 @@ class PDFDocument {
|
|||||||
IsAcroFormPresent: this.formInfo.hasAcroForm,
|
IsAcroFormPresent: this.formInfo.hasAcroForm,
|
||||||
IsXFAPresent: this.formInfo.hasXfa,
|
IsXFAPresent: this.formInfo.hasXfa,
|
||||||
IsCollectionPresent: !!this.catalog.collection,
|
IsCollectionPresent: !!this.catalog.collection,
|
||||||
|
IsSignaturesPresent: this.formInfo.hasSignatures,
|
||||||
};
|
};
|
||||||
|
|
||||||
let infoDict;
|
let infoDict;
|
||||||
|
@ -315,6 +315,7 @@ const UNSUPPORTED_FEATURES = {
|
|||||||
unknown: "unknown",
|
unknown: "unknown",
|
||||||
forms: "forms",
|
forms: "forms",
|
||||||
javaScript: "javaScript",
|
javaScript: "javaScript",
|
||||||
|
signatures: "signatures",
|
||||||
smask: "smask",
|
smask: "smask",
|
||||||
shadingPattern: "shadingPattern",
|
shadingPattern: "shadingPattern",
|
||||||
/** @deprecated unused */
|
/** @deprecated unused */
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -85,6 +85,7 @@
|
|||||||
!issue9458.pdf
|
!issue9458.pdf
|
||||||
!issue9655_reduced.pdf
|
!issue9655_reduced.pdf
|
||||||
!issue9915_reduced.pdf
|
!issue9915_reduced.pdf
|
||||||
|
!bug854315.pdf
|
||||||
!issue9940.pdf
|
!issue9940.pdf
|
||||||
!issue10388_reduced.pdf
|
!issue10388_reduced.pdf
|
||||||
!issue10438_reduced.pdf
|
!issue10438_reduced.pdf
|
||||||
|
BIN
test/pdfs/bug854315.pdf
Normal file
BIN
test/pdfs/bug854315.pdf
Normal file
Binary file not shown.
@ -389,6 +389,12 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
|
{ "id": "bug854315",
|
||||||
|
"file": "pdfs/bug854315.pdf",
|
||||||
|
"md5": "675754c07f71c068d2997905a5456f05",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "bug868745",
|
{ "id": "bug868745",
|
||||||
"file": "pdfs/bug868745.pdf",
|
"file": "pdfs/bug868745.pdf",
|
||||||
"md5": "86111ea5097dd7daffcdea891ad1b348",
|
"md5": "86111ea5097dd7daffcdea891ad1b348",
|
||||||
|
@ -1212,6 +1212,7 @@ describe("api", function () {
|
|||||||
expect(info.IsAcroFormPresent).toEqual(false);
|
expect(info.IsAcroFormPresent).toEqual(false);
|
||||||
expect(info.IsXFAPresent).toEqual(false);
|
expect(info.IsXFAPresent).toEqual(false);
|
||||||
expect(info.IsCollectionPresent).toEqual(false);
|
expect(info.IsCollectionPresent).toEqual(false);
|
||||||
|
expect(info.IsSignaturesPresent).toEqual(false);
|
||||||
|
|
||||||
expect(metadata instanceof Metadata).toEqual(true);
|
expect(metadata instanceof Metadata).toEqual(true);
|
||||||
expect(metadata.get("dc:title")).toEqual("Basic API Test");
|
expect(metadata.get("dc:title")).toEqual("Basic API Test");
|
||||||
@ -1254,6 +1255,7 @@ describe("api", function () {
|
|||||||
expect(info.IsAcroFormPresent).toEqual(false);
|
expect(info.IsAcroFormPresent).toEqual(false);
|
||||||
expect(info.IsXFAPresent).toEqual(false);
|
expect(info.IsXFAPresent).toEqual(false);
|
||||||
expect(info.IsCollectionPresent).toEqual(false);
|
expect(info.IsCollectionPresent).toEqual(false);
|
||||||
|
expect(info.IsSignaturesPresent).toEqual(false);
|
||||||
|
|
||||||
expect(metadata).toEqual(null);
|
expect(metadata).toEqual(null);
|
||||||
expect(contentDispositionFilename).toEqual(null);
|
expect(contentDispositionFilename).toEqual(null);
|
||||||
@ -1282,6 +1284,7 @@ describe("api", function () {
|
|||||||
expect(info.IsAcroFormPresent).toEqual(false);
|
expect(info.IsAcroFormPresent).toEqual(false);
|
||||||
expect(info.IsXFAPresent).toEqual(false);
|
expect(info.IsXFAPresent).toEqual(false);
|
||||||
expect(info.IsCollectionPresent).toEqual(false);
|
expect(info.IsCollectionPresent).toEqual(false);
|
||||||
|
expect(info.IsSignaturesPresent).toEqual(false);
|
||||||
|
|
||||||
expect(metadata).toEqual(null);
|
expect(metadata).toEqual(null);
|
||||||
expect(contentDispositionFilename).toEqual(null);
|
expect(contentDispositionFilename).toEqual(null);
|
||||||
|
@ -77,6 +77,7 @@ describe("document", function () {
|
|||||||
const pdfDocument = getDocument(null);
|
const pdfDocument = getDocument(null);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: false,
|
hasAcroForm: false,
|
||||||
|
hasSignatures: false,
|
||||||
hasXfa: false,
|
hasXfa: false,
|
||||||
hasFields: false,
|
hasFields: false,
|
||||||
});
|
});
|
||||||
@ -90,6 +91,7 @@ describe("document", function () {
|
|||||||
let pdfDocument = getDocument(acroForm);
|
let pdfDocument = getDocument(acroForm);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: false,
|
hasAcroForm: false,
|
||||||
|
hasSignatures: false,
|
||||||
hasXfa: false,
|
hasXfa: false,
|
||||||
hasFields: false,
|
hasFields: false,
|
||||||
});
|
});
|
||||||
@ -98,6 +100,7 @@ describe("document", function () {
|
|||||||
pdfDocument = getDocument(acroForm);
|
pdfDocument = getDocument(acroForm);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: false,
|
hasAcroForm: false,
|
||||||
|
hasSignatures: false,
|
||||||
hasXfa: true,
|
hasXfa: true,
|
||||||
hasFields: false,
|
hasFields: false,
|
||||||
});
|
});
|
||||||
@ -106,6 +109,7 @@ describe("document", function () {
|
|||||||
pdfDocument = getDocument(acroForm);
|
pdfDocument = getDocument(acroForm);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: false,
|
hasAcroForm: false,
|
||||||
|
hasSignatures: false,
|
||||||
hasXfa: false,
|
hasXfa: false,
|
||||||
hasFields: false,
|
hasFields: false,
|
||||||
});
|
});
|
||||||
@ -114,6 +118,7 @@ describe("document", function () {
|
|||||||
pdfDocument = getDocument(acroForm);
|
pdfDocument = getDocument(acroForm);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: false,
|
hasAcroForm: false,
|
||||||
|
hasSignatures: false,
|
||||||
hasXfa: true,
|
hasXfa: true,
|
||||||
hasFields: false,
|
hasFields: false,
|
||||||
});
|
});
|
||||||
@ -127,6 +132,7 @@ describe("document", function () {
|
|||||||
let pdfDocument = getDocument(acroForm);
|
let pdfDocument = getDocument(acroForm);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: false,
|
hasAcroForm: false,
|
||||||
|
hasSignatures: false,
|
||||||
hasXfa: false,
|
hasXfa: false,
|
||||||
hasFields: false,
|
hasFields: false,
|
||||||
});
|
});
|
||||||
@ -135,6 +141,7 @@ describe("document", function () {
|
|||||||
pdfDocument = getDocument(acroForm);
|
pdfDocument = getDocument(acroForm);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: true,
|
hasAcroForm: true,
|
||||||
|
hasSignatures: false,
|
||||||
hasXfa: false,
|
hasXfa: false,
|
||||||
hasFields: true,
|
hasFields: true,
|
||||||
});
|
});
|
||||||
@ -146,6 +153,7 @@ describe("document", function () {
|
|||||||
pdfDocument = getDocument(acroForm);
|
pdfDocument = getDocument(acroForm);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: true,
|
hasAcroForm: true,
|
||||||
|
hasSignatures: false,
|
||||||
hasXfa: false,
|
hasXfa: false,
|
||||||
hasFields: true,
|
hasFields: true,
|
||||||
});
|
});
|
||||||
@ -169,6 +177,7 @@ describe("document", function () {
|
|||||||
pdfDocument = getDocument(acroForm, xref);
|
pdfDocument = getDocument(acroForm, xref);
|
||||||
expect(pdfDocument.formInfo).toEqual({
|
expect(pdfDocument.formInfo).toEqual({
|
||||||
hasAcroForm: false,
|
hasAcroForm: false,
|
||||||
|
hasSignatures: true,
|
||||||
hasXfa: false,
|
hasXfa: false,
|
||||||
hasFields: true,
|
hasFields: true,
|
||||||
});
|
});
|
||||||
|
@ -1583,6 +1583,11 @@ const PDFViewerApplication = {
|
|||||||
this._delayedFallback(UNSUPPORTED_FEATURES.forms);
|
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.
|
// Telemetry labels must be C++ variable friendly.
|
||||||
let versionId = "other";
|
let versionId = "other";
|
||||||
if (KNOWN_VERSIONS.includes(info.PDFFormatVersion)) {
|
if (KNOWN_VERSIONS.includes(info.PDFFormatVersion)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user