Merge pull request #7633 from timvandermeij/interactive-forms-tx-flags
Text widget annotations: support read-only/multiline fields and improve testing
This commit is contained in:
commit
f062695d62
@ -33,6 +33,7 @@
|
||||
coreColorSpace, coreObj, coreEvaluator) {
|
||||
|
||||
var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType;
|
||||
var AnnotationFieldFlag = sharedUtil.AnnotationFieldFlag;
|
||||
var AnnotationFlag = sharedUtil.AnnotationFlag;
|
||||
var AnnotationType = sharedUtil.AnnotationType;
|
||||
var OPS = sharedUtil.OPS;
|
||||
@ -65,10 +66,14 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
|
||||
/**
|
||||
* @param {XRef} xref
|
||||
* @param {Object} ref
|
||||
* @param {string} uniquePrefix
|
||||
* @param {Object} idCounters
|
||||
* @param {boolean} renderInteractiveForms
|
||||
* @returns {Annotation}
|
||||
*/
|
||||
create: function AnnotationFactory_create(xref, ref,
|
||||
uniquePrefix, idCounters) {
|
||||
uniquePrefix, idCounters,
|
||||
renderInteractiveForms) {
|
||||
var dict = xref.fetchIfRef(ref);
|
||||
if (!isDict(dict)) {
|
||||
return;
|
||||
@ -87,6 +92,7 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
|
||||
ref: isRef(ref) ? ref : null,
|
||||
subtype: subtype,
|
||||
id: id,
|
||||
renderInteractiveForms: renderInteractiveForms,
|
||||
};
|
||||
|
||||
switch (subtype) {
|
||||
@ -621,9 +627,13 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
|
||||
data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
|
||||
var fieldType = Util.getInheritableProperty(dict, 'FT');
|
||||
data.fieldType = isName(fieldType) ? fieldType.name : null;
|
||||
data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;
|
||||
this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty;
|
||||
|
||||
data.fieldFlags = Util.getInheritableProperty(dict, 'Ff');
|
||||
if (!isInt(data.fieldFlags) || data.fieldFlags < 0) {
|
||||
data.fieldFlags = 0;
|
||||
}
|
||||
|
||||
// Hide signatures because we cannot validate them.
|
||||
if (data.fieldType === 'Sig') {
|
||||
this.setFlags(AnnotationFlag.HIDDEN);
|
||||
@ -662,7 +672,22 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
|
||||
data.fullName = fieldName.join('.');
|
||||
}
|
||||
|
||||
Util.inherit(WidgetAnnotation, Annotation, {});
|
||||
Util.inherit(WidgetAnnotation, Annotation, {
|
||||
/**
|
||||
* Check if a provided field flag is set.
|
||||
*
|
||||
* @public
|
||||
* @memberof WidgetAnnotation
|
||||
* @param {number} flag - Bit position, numbered from one instead of
|
||||
* zero, to check
|
||||
* @return {boolean}
|
||||
* @see {@link shared/util.js}
|
||||
*/
|
||||
hasFieldFlag: function WidgetAnnotation_hasFieldFlag(flag) {
|
||||
var mask = 1 << (flag - 1);
|
||||
return !!(this.data.fieldFlags & mask);
|
||||
},
|
||||
});
|
||||
|
||||
return WidgetAnnotation;
|
||||
})();
|
||||
@ -671,6 +696,8 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
|
||||
function TextWidgetAnnotation(params) {
|
||||
WidgetAnnotation.call(this, params);
|
||||
|
||||
this.renderInteractiveForms = params.renderInteractiveForms;
|
||||
|
||||
// Determine the alignment of text in the field.
|
||||
var alignment = Util.getInheritableProperty(params.dict, 'Q');
|
||||
if (!isInt(alignment) || alignment < 0 || alignment > 2) {
|
||||
@ -684,29 +711,38 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
|
||||
maximumLength = null;
|
||||
}
|
||||
this.data.maxLen = maximumLength;
|
||||
|
||||
// Process field flags for the display layer.
|
||||
this.data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
|
||||
this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE);
|
||||
}
|
||||
|
||||
Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
|
||||
getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator,
|
||||
task) {
|
||||
var operatorList = new OperatorList();
|
||||
|
||||
// Do not render form elements on the canvas when interactive forms are
|
||||
// enabled. The display layer is responsible for rendering them instead.
|
||||
if (this.renderInteractiveForms) {
|
||||
return Promise.resolve(operatorList);
|
||||
}
|
||||
|
||||
if (this.appearance) {
|
||||
return Annotation.prototype.getOperatorList.call(this, evaluator, task);
|
||||
}
|
||||
|
||||
var opList = new OperatorList();
|
||||
var data = this.data;
|
||||
|
||||
// Even if there is an appearance stream, ignore it. This is the
|
||||
// behaviour used by Adobe Reader.
|
||||
if (!data.defaultAppearance) {
|
||||
return Promise.resolve(opList);
|
||||
if (!this.data.defaultAppearance) {
|
||||
return Promise.resolve(operatorList);
|
||||
}
|
||||
|
||||
var stream = new Stream(stringToBytes(data.defaultAppearance));
|
||||
return evaluator.getOperatorList(stream, task,
|
||||
this.fieldResources, opList).
|
||||
var stream = new Stream(stringToBytes(this.data.defaultAppearance));
|
||||
return evaluator.getOperatorList(stream, task, this.fieldResources,
|
||||
operatorList).
|
||||
then(function () {
|
||||
return opList;
|
||||
return operatorList;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -205,7 +205,8 @@ var Page = (function PageClosure() {
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getOperatorList: function Page_getOperatorList(handler, task, intent) {
|
||||
getOperatorList: function Page_getOperatorList(handler, task, intent,
|
||||
renderInteractiveForms) {
|
||||
var self = this;
|
||||
|
||||
var pdfManager = this.pdfManager;
|
||||
@ -245,6 +246,8 @@ var Page = (function PageClosure() {
|
||||
});
|
||||
});
|
||||
|
||||
this.renderInteractiveForms = renderInteractiveForms;
|
||||
|
||||
var annotationsPromise = pdfManager.ensure(this, 'annotations');
|
||||
return Promise.all([pageListPromise, annotationsPromise]).then(
|
||||
function(datas) {
|
||||
@ -328,7 +331,8 @@ var Page = (function PageClosure() {
|
||||
var annotationRef = annotationRefs[i];
|
||||
var annotation = annotationFactory.create(this.xref, annotationRef,
|
||||
this.uniquePrefix,
|
||||
this.idCounters);
|
||||
this.idCounters,
|
||||
this.renderInteractiveForms);
|
||||
if (annotation) {
|
||||
annotations.push(annotation);
|
||||
}
|
||||
|
@ -839,7 +839,8 @@ var WorkerMessageHandler = {
|
||||
var pageNum = pageIndex + 1;
|
||||
var start = Date.now();
|
||||
// Pre compile the pdf page and fetch the fonts/images.
|
||||
page.getOperatorList(handler, task, data.intent).then(
|
||||
page.getOperatorList(handler, task, data.intent,
|
||||
data.renderInteractiveForms).then(
|
||||
function(operatorList) {
|
||||
finishWorkerTask(task);
|
||||
|
||||
|
@ -446,9 +446,15 @@ var TextWidgetAnnotationElement = (
|
||||
|
||||
var element = null;
|
||||
if (this.renderInteractiveForms) {
|
||||
element = document.createElement('input');
|
||||
element.type = 'text';
|
||||
if (this.data.multiLine) {
|
||||
element = document.createElement('textarea');
|
||||
} else {
|
||||
element = document.createElement('input');
|
||||
element.type = 'text';
|
||||
}
|
||||
|
||||
element.value = this.data.fieldValue;
|
||||
element.disabled = this.data.readOnly;
|
||||
|
||||
if (this.data.maxLen !== null) {
|
||||
element.maxLength = this.data.maxLen;
|
||||
|
@ -656,6 +656,9 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
|
||||
* calling of PDFPage.getViewport method.
|
||||
* @property {string} intent - Rendering intent, can be 'display' or 'print'
|
||||
* (default value is 'display').
|
||||
* @property {boolean} renderInteractiveForms - (optional) Whether or not
|
||||
* interactive form elements are rendered in the display
|
||||
* layer. If so, we do not render them on canvas as well.
|
||||
* @property {Array} transform - (optional) Additional transform, applied
|
||||
* just before viewport transform.
|
||||
* @property {Object} imageLayer - (optional) An object that has beginLayout,
|
||||
@ -764,6 +767,8 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
|
||||
this.pendingCleanup = false;
|
||||
|
||||
var renderingIntent = (params.intent === 'print' ? 'print' : 'display');
|
||||
var renderInteractiveForms = (params.renderInteractiveForms === true ?
|
||||
true : /* Default */ false);
|
||||
|
||||
if (!this.intentStates[renderingIntent]) {
|
||||
this.intentStates[renderingIntent] = Object.create(null);
|
||||
@ -784,7 +789,8 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
|
||||
this.stats.time('Page Request');
|
||||
this.transport.messageHandler.send('RenderPageRequest', {
|
||||
pageIndex: this.pageNumber - 1,
|
||||
intent: renderingIntent
|
||||
intent: renderingIntent,
|
||||
renderInteractiveForms: renderInteractiveForms,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,28 @@ var AnnotationFlag = {
|
||||
LOCKEDCONTENTS: 0x200
|
||||
};
|
||||
|
||||
var AnnotationFieldFlag = {
|
||||
READONLY: 1,
|
||||
REQUIRED: 2,
|
||||
NOEXPORT: 3,
|
||||
MULTILINE: 13,
|
||||
PASSWORD: 14,
|
||||
NOTOGGLETOOFF: 15,
|
||||
RADIO: 16,
|
||||
PUSHBUTTON: 17,
|
||||
COMBO: 18,
|
||||
EDIT: 19,
|
||||
SORT: 20,
|
||||
FILESELECT: 21,
|
||||
MULTISELECT: 22,
|
||||
DONOTSPELLCHECK: 23,
|
||||
DONOTSCROLL: 24,
|
||||
COMB: 25,
|
||||
RICHTEXT: 26,
|
||||
RADIOSINUNISON: 26,
|
||||
COMMITONSELCHANGE: 27,
|
||||
};
|
||||
|
||||
var AnnotationBorderStyleType = {
|
||||
SOLID: 1,
|
||||
DASHED: 2,
|
||||
@ -2364,6 +2386,7 @@ exports.OPS = OPS;
|
||||
exports.VERBOSITY_LEVELS = VERBOSITY_LEVELS;
|
||||
exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES;
|
||||
exports.AnnotationBorderStyleType = AnnotationBorderStyleType;
|
||||
exports.AnnotationFieldFlag = AnnotationFieldFlag;
|
||||
exports.AnnotationFlag = AnnotationFlag;
|
||||
exports.AnnotationType = AnnotationType;
|
||||
exports.FontType = FontType;
|
||||
|
@ -43,7 +43,8 @@
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input {
|
||||
.annotationLayer .textWidgetAnnotation input,
|
||||
.annotationLayer .textWidgetAnnotation textarea {
|
||||
background-color: rgba(0, 54, 255, 0.13);
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
@ -54,6 +55,18 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.annotationLayer .textWidgetAnnotation textarea {
|
||||
font: message-box;
|
||||
font-size: 9px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input[disabled],
|
||||
.annotationLayer .textWidgetAnnotation textarea[disabled] {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.annotationLayer .popupAnnotation {
|
||||
display: block !important;
|
||||
}
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -250,4 +250,5 @@
|
||||
!annotation-squiggly.pdf
|
||||
!annotation-highlight.pdf
|
||||
!annotation-fileattachment.pdf
|
||||
!annotation-text-widget.pdf
|
||||
!zero_descent.pdf
|
||||
|
BIN
test/pdfs/annotation-text-widget.pdf
Normal file
BIN
test/pdfs/annotation-text-widget.pdf
Normal file
Binary file not shown.
@ -3151,6 +3151,13 @@
|
||||
"type": "eq",
|
||||
"annotations": true
|
||||
},
|
||||
{ "id": "annotation-text-widget-forms",
|
||||
"file": "pdfs/annotation-text-widget.pdf",
|
||||
"md5": "cc9672539ad5b837152a9c6961e5f106",
|
||||
"rounds": 1,
|
||||
"type": "eq",
|
||||
"forms": true
|
||||
},
|
||||
{ "id": "issue6108",
|
||||
"file": "pdfs/issue6108.pdf",
|
||||
"md5": "8961cb55149495989a80bf0487e0f076",
|
||||
|
@ -469,7 +469,8 @@ describe('Annotation layer', function() {
|
||||
textWidgetDict = null;
|
||||
});
|
||||
|
||||
it('should handle unknown text alignment and maximum length', function() {
|
||||
it('should handle unknown text alignment, maximum length and flags',
|
||||
function() {
|
||||
var textWidgetRef = new Ref(124, 0);
|
||||
var xref = new XRefMock([
|
||||
{ ref: textWidgetRef, data: textWidgetDict, }
|
||||
@ -478,11 +479,15 @@ describe('Annotation layer', function() {
|
||||
var textWidgetAnnotation = annotationFactory.create(xref, textWidgetRef);
|
||||
expect(textWidgetAnnotation.data.textAlignment).toEqual(null);
|
||||
expect(textWidgetAnnotation.data.maxLen).toEqual(null);
|
||||
expect(textWidgetAnnotation.data.readOnly).toEqual(false);
|
||||
expect(textWidgetAnnotation.data.multiLine).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not set invalid text alignment and maximum length', function() {
|
||||
it('should not set invalid text alignment, maximum length and flags',
|
||||
function() {
|
||||
textWidgetDict.set('Q', 'center');
|
||||
textWidgetDict.set('MaxLen', 'five');
|
||||
textWidgetDict.set('Ff', 'readonly');
|
||||
|
||||
var textWidgetRef = new Ref(43, 0);
|
||||
var xref = new XRefMock([
|
||||
@ -492,11 +497,15 @@ describe('Annotation layer', function() {
|
||||
var textWidgetAnnotation = annotationFactory.create(xref, textWidgetRef);
|
||||
expect(textWidgetAnnotation.data.textAlignment).toEqual(null);
|
||||
expect(textWidgetAnnotation.data.maxLen).toEqual(null);
|
||||
expect(textWidgetAnnotation.data.readOnly).toEqual(false);
|
||||
expect(textWidgetAnnotation.data.multiLine).toEqual(false);
|
||||
});
|
||||
|
||||
it('should set valid text alignment and maximum length', function() {
|
||||
it('should set valid text alignment, maximum length and flags',
|
||||
function() {
|
||||
textWidgetDict.set('Q', 1);
|
||||
textWidgetDict.set('MaxLen', 20);
|
||||
textWidgetDict.set('Ff', 4097);
|
||||
|
||||
var textWidgetRef = new Ref(84, 0);
|
||||
var xref = new XRefMock([
|
||||
@ -506,6 +515,8 @@ describe('Annotation layer', function() {
|
||||
var textWidgetAnnotation = annotationFactory.create(xref, textWidgetRef);
|
||||
expect(textWidgetAnnotation.data.textAlignment).toEqual(1);
|
||||
expect(textWidgetAnnotation.data.maxLen).toEqual(20);
|
||||
expect(textWidgetAnnotation.data.readOnly).toEqual(true);
|
||||
expect(textWidgetAnnotation.data.multiLine).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -41,7 +41,8 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input {
|
||||
.annotationLayer .textWidgetAnnotation input,
|
||||
.annotationLayer .textWidgetAnnotation textarea {
|
||||
background-color: rgba(0, 54, 255, 0.13);
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
@ -52,11 +53,26 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input:hover {
|
||||
.annotationLayer .textWidgetAnnotation textarea {
|
||||
font: message-box;
|
||||
font-size: 9px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input[disabled],
|
||||
.annotationLayer .textWidgetAnnotation textarea[disabled] {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input:hover,
|
||||
.annotationLayer .textWidgetAnnotation textarea:hover {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
.annotationLayer .textWidgetAnnotation input:focus {
|
||||
.annotationLayer .textWidgetAnnotation input:focus,
|
||||
.annotationLayer .textWidgetAnnotation textarea:focus {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
@ -498,6 +498,7 @@ var PDFPageView = (function PDFPageViewClosure() {
|
||||
canvasContext: ctx,
|
||||
transform: transform,
|
||||
viewport: this.viewport,
|
||||
renderInteractiveForms: pdfjsLib.PDFJS.renderInteractiveForms,
|
||||
// intent: 'default', // === 'display'
|
||||
};
|
||||
var renderTask = this.renderTask = this.pdfPage.render(renderContext);
|
||||
|
Loading…
Reference in New Issue
Block a user