Skip to content

Commit 6100ab4

Browse files
committed
Text widget annotations: implement comb support
1 parent c0e82db commit 6100ab4

File tree

7 files changed

+113
-4
lines changed

7 files changed

+113
-4
lines changed

src/core/annotation.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,11 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
715715
// Process field flags for the display layer.
716716
this.data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
717717
this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE);
718+
this.data.comb = this.hasFieldFlag(AnnotationFieldFlag.COMB) &&
719+
!this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) &&
720+
!this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) &&
721+
!this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) &&
722+
this.data.maxLen !== null;
718723
}
719724

720725
Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {

src/display/annotation_layer.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,14 @@ var TextWidgetAnnotationElement = (
461461
if (this.data.maxLen !== null) {
462462
element.maxLength = this.data.maxLen;
463463
}
464+
465+
if (this.data.comb) {
466+
var fieldWidth = this.data.rect[2] - this.data.rect[0];
467+
var combWidth = fieldWidth / this.data.maxLen;
468+
469+
element.classList.add('comb');
470+
element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)';
471+
}
464472
} else {
465473
element = document.createElement('div');
466474
element.textContent = this.data.fieldValue;

test/annotation_layer_test.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@
6767
border: 1px solid transparent;
6868
}
6969

70+
.annotationLayer .textWidgetAnnotation input.comb {
71+
font-family: monospace;
72+
padding-left: 2px;
73+
padding-right: 0;
74+
}
75+
7076
.annotationLayer .popupAnnotation {
7177
display: block !important;
7278
}

test/pdfs/annotation-text-widget.pdf

1.87 KB
Binary file not shown.

test/test_manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3197,14 +3197,14 @@
31973197
},
31983198
{ "id": "annotation-text-widget-annotations",
31993199
"file": "pdfs/annotation-text-widget.pdf",
3200-
"md5": "cc9672539ad5b837152a9c6961e5f106",
3200+
"md5": "b7b8923a12998fca8603fae53f73f19b",
32013201
"rounds": 1,
32023202
"type": "eq",
32033203
"annotations": true
32043204
},
32053205
{ "id": "annotation-text-widget-forms",
32063206
"file": "pdfs/annotation-text-widget.pdf",
3207-
"md5": "cc9672539ad5b837152a9c6961e5f106",
3207+
"md5": "b7b8923a12998fca8603fae53f73f19b",
32083208
"rounds": 1,
32093209
"type": "eq",
32103210
"forms": true

test/unit/annotation_layer_spec.js

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* globals expect, it, describe, Dict, Name, Annotation, AnnotationBorderStyle,
22
AnnotationBorderStyleType, AnnotationType, AnnotationFlag, PDFJS,
33
beforeEach, afterEach, stringToBytes, AnnotationFactory, Ref, isRef,
4-
beforeAll, afterAll */
4+
beforeAll, afterAll, AnnotationFieldFlag */
55

66
'use strict';
77

@@ -481,6 +481,7 @@ describe('Annotation layer', function() {
481481
expect(textWidgetAnnotation.data.maxLen).toEqual(null);
482482
expect(textWidgetAnnotation.data.readOnly).toEqual(false);
483483
expect(textWidgetAnnotation.data.multiLine).toEqual(false);
484+
expect(textWidgetAnnotation.data.comb).toEqual(false);
484485
});
485486

486487
it('should not set invalid text alignment, maximum length and flags',
@@ -499,13 +500,18 @@ describe('Annotation layer', function() {
499500
expect(textWidgetAnnotation.data.maxLen).toEqual(null);
500501
expect(textWidgetAnnotation.data.readOnly).toEqual(false);
501502
expect(textWidgetAnnotation.data.multiLine).toEqual(false);
503+
expect(textWidgetAnnotation.data.comb).toEqual(false);
502504
});
503505

504506
it('should set valid text alignment, maximum length and flags',
505507
function() {
508+
var flags = 0;
509+
flags |= 1 << (AnnotationFieldFlag.READONLY - 1);
510+
flags |= 1 << (AnnotationFieldFlag.MULTILINE - 1);
511+
506512
textWidgetDict.set('Q', 1);
507513
textWidgetDict.set('MaxLen', 20);
508-
textWidgetDict.set('Ff', 4097);
514+
textWidgetDict.set('Ff', flags);
509515

510516
var textWidgetRef = new Ref(84, 0);
511517
var xref = new XRefMock([
@@ -518,6 +524,74 @@ describe('Annotation layer', function() {
518524
expect(textWidgetAnnotation.data.readOnly).toEqual(true);
519525
expect(textWidgetAnnotation.data.multiLine).toEqual(true);
520526
});
527+
528+
it('should reject comb fields without a maximum length', function() {
529+
var flags = 0;
530+
flags |= 1 << (AnnotationFieldFlag.COMB - 1);
531+
532+
textWidgetDict.set('Ff', flags);
533+
534+
var textWidgetRef = new Ref(46, 0);
535+
var xref = new XRefMock([
536+
{ ref: textWidgetRef, data: textWidgetDict, }
537+
]);
538+
539+
var textWidgetAnnotation = annotationFactory.create(xref, textWidgetRef);
540+
expect(textWidgetAnnotation.data.comb).toEqual(false);
541+
});
542+
543+
it('should accept comb fields with a maximum length', function() {
544+
var flags = 0;
545+
flags |= 1 << (AnnotationFieldFlag.COMB - 1);
546+
547+
textWidgetDict.set('MaxLen', 20);
548+
textWidgetDict.set('Ff', flags);
549+
550+
var textWidgetRef = new Ref(46, 0);
551+
var xref = new XRefMock([
552+
{ ref: textWidgetRef, data: textWidgetDict, }
553+
]);
554+
555+
var textWidgetAnnotation = annotationFactory.create(xref, textWidgetRef);
556+
expect(textWidgetAnnotation.data.comb).toEqual(true);
557+
});
558+
559+
it('should only accept comb fields when the flags are valid', function() {
560+
var invalidFieldFlags = [
561+
AnnotationFieldFlag.MULTILINE,
562+
AnnotationFieldFlag.PASSWORD,
563+
AnnotationFieldFlag.FILESELECT
564+
];
565+
566+
// The field may not use combs until all invalid flags are unset.
567+
for (var i = 0, ii = invalidFieldFlags.length; i <= ii; i++) {
568+
var flags = 0;
569+
flags |= 1 << (AnnotationFieldFlag.COMB - 1);
570+
571+
for (var j = 0, jj = invalidFieldFlags.length; j < jj; j++) {
572+
flags |= 1 << (invalidFieldFlags[j] - 1);
573+
}
574+
575+
textWidgetDict.set('MaxLen', 20);
576+
textWidgetDict.set('Ff', flags);
577+
578+
var textWidgetRef = new Ref(93, 0);
579+
var xref = new XRefMock([
580+
{ ref: textWidgetRef, data: textWidgetDict, }
581+
]);
582+
583+
var textWidgetAnnotation = annotationFactory.create(xref,
584+
textWidgetRef);
585+
586+
var valid = (invalidFieldFlags.length === 0);
587+
expect(textWidgetAnnotation.data.comb).toEqual(valid);
588+
589+
// Remove the last invalid flag for the next iteration.
590+
if (!valid) {
591+
invalidFieldFlags.splice(-1, 1);
592+
}
593+
}
594+
});
521595
});
522596

523597
describe('FileAttachmentAnnotation', function() {

web/annotation_layer_builder.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@
7777
border: 1px solid transparent;
7878
}
7979

80+
.annotationLayer .textWidgetAnnotation input.comb {
81+
font-family: monospace;
82+
padding-left: 2px;
83+
padding-right: 0;
84+
}
85+
86+
.annotationLayer .textWidgetAnnotation input.comb:focus {
87+
/*
88+
* Letter spacing is placed on the right side of each character. Hence, the
89+
* letter spacing of the last character may be placed outside the visible
90+
* area, causing horizontal scrolling. We avoid this by extending the width
91+
* when the element has focus and revert this when it loses focus.
92+
*/
93+
width: 115%;
94+
}
95+
8096
.annotationLayer .popupWrapper {
8197
position: absolute;
8298
width: 20em;

0 commit comments

Comments
 (0)