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:
Tim van der Meij 2016-09-17 17:19:47 +02:00 committed by GitHub
commit f062695d62
13 changed files with 150 additions and 25 deletions

View File

@ -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;
});
}
});

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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,
});
}

View File

@ -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;

View File

@ -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;
}

View File

@ -250,4 +250,5 @@
!annotation-squiggly.pdf
!annotation-highlight.pdf
!annotation-fileattachment.pdf
!annotation-text-widget.pdf
!zero_descent.pdf

Binary file not shown.

View File

@ -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",

View File

@ -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);
});
});

View File

@ -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;
}

View File

@ -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);