From 12dd4ac8f406422a14b516747aa1182de2ad3bb8 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Thu, 19 May 2022 15:12:28 +0200 Subject: [PATCH] Set the text fields font size based on their height - right now we're using the font size from the pdf itself but we use an other font in the annotation layer. So this size doesn't really make sense and leads to bad rendering (see pdf in #14928); - use a sans-serif font for the fields containing text (fix issue #14736); - remove useless padding in text-based fields (fix issue #14301); - text fields allow/disallow scrolling bars (see bit 24 in Ff entry), so use this value to hide/show scrollbars in annotation layer. --- src/core/annotation.js | 10 +-- src/display/annotation_layer.js | 103 ++++++++++++++++++++----------- src/shared/util.js | 5 ++ test/driver.js | 4 +- test/pdfs/issue14301.pdf.link | 2 + test/test_manifest.json | 21 +++++++ web/annotation_layer_builder.css | 5 +- 7 files changed, 104 insertions(+), 46 deletions(-) create mode 100644 test/pdfs/issue14301.pdf.link diff --git a/src/core/annotation.js b/src/core/annotation.js index b0bbf8e1f97860..bcad7497361b5b 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -24,6 +24,7 @@ import { escapeString, getModificationDate, isAscii, + LINE_FACTOR, OPS, RenderingIntentFlag, shadow, @@ -55,11 +56,6 @@ import { StringStream } from "./stream.js"; import { writeDict } from "./writer.js"; import { XFAFactory } from "./xfa/factory.js"; -// Represent the percentage of the height of a single-line field over -// the font size. -// Acrobat seems to use this value. -const LINE_FACTOR = 1.35; - class AnnotationFactory { /** * Create an `Annotation` object of the correct type for the given reference @@ -1921,6 +1917,7 @@ class TextWidgetAnnotation extends WidgetAnnotation { !this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) && !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) && this.data.maxLen !== null; + this.data.doNotScroll = this.hasFieldFlag(AnnotationFieldFlag.DONOTSCROLL); } _getCombAppearance(defaultAppearance, font, text, width, hPadding, vPadding) { @@ -2788,6 +2785,9 @@ class LinkAnnotation extends Annotation { this.data.quadPoints = quadPoints; } + // The color entry for a link annotation is the color of the border. + this.data.borderColor = this.data.borderColor || this.data.color; + Catalog.parseDestDictionary({ destDict: params.dict, resultObj: this.data, diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 16c0bed488eec5..66fd47d16405e2 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -22,6 +22,7 @@ import { AnnotationBorderStyleType, AnnotationType, assert, + LINE_FACTOR, shadow, unreachable, Util, @@ -37,6 +38,7 @@ import { ColorConverters } from "../shared/scripting_utils.js"; import { XfaLayer } from "./xfa_layer.js"; const DEFAULT_TAB_INDEX = 1000; +const DEFAULT_FONT_SIZE = 9; const GetElementsByNameSet = new WeakSet(); function getRectDims(rect) { @@ -271,12 +273,12 @@ class AnnotationElement { break; } - const borderColor = data.borderColor || data.color || null; + const borderColor = data.borderColor || null; if (borderColor) { container.style.borderColor = Util.makeHexColor( - data.color[0] | 0, - data.color[1] | 0, - data.color[2] | 0 + borderColor[0] | 0, + borderColor[1] | 0, + borderColor[2] | 0 ); } else { // Transparent (invisible) border, so do not draw it at all. @@ -897,6 +899,53 @@ class WidgetAnnotationElement extends AnnotationElement { ? "transparent" : Util.makeHexColor(color[0], color[1], color[2]); } + + /** + * Apply text styles to the text in the element. + * + * @private + * @param {HTMLDivElement} element + * @memberof TextWidgetAnnotationElement + */ + _setTextStyle(element) { + const TEXT_ALIGNMENT = ["left", "center", "right"]; + const { fontColor } = this.data.defaultAppearanceData; + const fontSize = + this.data.defaultAppearanceData.fontSize || DEFAULT_FONT_SIZE; + + const style = element.style; + + // TODO: If the font-size is zero, calculate it based on the height and + // width of the element. + // Not setting `style.fontSize` will use the default font-size for now. + + // We don't use the font, as specified in the PDF document, for the + // element. Hence using the original `fontSize` could look bad, which is why + // it's instead based on the field height. + // If the height is "big" then it could lead to a too big font size + // so in this case use the one we've in the pdf (hence the min). + if (this.data.multiLine) { + const height = Math.abs(this.data.rect[3] - this.data.rect[1]); + const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1; + const lineHeight = height / numberOfLines; + style.fontSize = `${Math.min( + fontSize, + Math.round(lineHeight / LINE_FACTOR) + )}px`; + } else { + const height = Math.abs(this.data.rect[3] - this.data.rect[1]); + style.fontSize = `${Math.min( + fontSize, + Math.round(height / LINE_FACTOR) + )}px`; + } + + style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); + + if (this.data.textAlignment !== null) { + style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; + } + } } class TextWidgetAnnotationElement extends WidgetAnnotationElement { @@ -944,10 +993,16 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { if (this.data.multiLine) { element = document.createElement("textarea"); element.textContent = textContent; + if (this.data.doNotScroll) { + element.style.overflowY = "hidden"; + } } else { element = document.createElement("input"); element.type = "text"; element.setAttribute("value", textContent); + if (this.data.doNotScroll) { + element.style.overflowX = "hidden"; + } } GetElementsByNameSet.add(element); element.disabled = this.data.readOnly; @@ -1177,32 +1232,6 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { this.container.appendChild(element); return this.container; } - - /** - * Apply text styles to the text in the element. - * - * @private - * @param {HTMLDivElement} element - * @memberof TextWidgetAnnotationElement - */ - _setTextStyle(element) { - const TEXT_ALIGNMENT = ["left", "center", "right"]; - const { fontSize, fontColor } = this.data.defaultAppearanceData; - const style = element.style; - - // TODO: If the font-size is zero, calculate it based on the height and - // width of the element. - // Not setting `style.fontSize` will use the default font-size for now. - if (fontSize) { - style.fontSize = `${fontSize}px`; - } - - style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); - - if (this.data.textAlignment !== null) { - style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; - } - } } class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { @@ -1413,10 +1442,8 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { value: this.data.fieldValue, }); - let { fontSize } = this.data.defaultAppearanceData; - if (!fontSize) { - fontSize = 9; - } + const fontSize = + this.data.defaultAppearanceData.fontSize || DEFAULT_FONT_SIZE; const fontSizeStyle = `calc(${fontSize}px * var(--zoom-factor))`; const selectElement = document.createElement("select"); @@ -1426,8 +1453,6 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { selectElement.setAttribute("id", id); selectElement.tabIndex = DEFAULT_TAB_INDEX; - selectElement.style.fontSize = `${fontSize}px`; - if (!this.data.combo) { // List boxes have a size and (optionally) multiple selection. selectElement.size = this.data.options.length; @@ -1606,6 +1631,12 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { }); } + if (this.data.combo) { + this._setTextStyle(selectElement); + } else { + // Just use the default font size... + // it's a bit hard to guess what is a good size. + } this._setBackgroundColor(selectElement); this._setDefaultPropertiesFromJS(selectElement); diff --git a/src/shared/util.js b/src/shared/util.js index 5e5529e4150e0d..665b59d391539f 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -18,6 +18,10 @@ import "./compatibility.js"; const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; +// Represent the percentage of the height of a single-line field over +// the font size. Acrobat seems to use this value. +const LINE_FACTOR = 1.35; + /** * Refer to the `WorkerTransport.getRenderingIntent`-method in the API, to see * how these flags are being used: @@ -1162,6 +1166,7 @@ export { isArrayBuffer, isArrayEqual, isAscii, + LINE_FACTOR, MissingPDFException, objectFromMap, objectSize, diff --git a/test/driver.js b/test/driver.js index 87b9f50004a0bb..bd943b0cb21950 100644 --- a/test/driver.js +++ b/test/driver.js @@ -751,7 +751,9 @@ class Driver { transform, }; if (renderForms) { - renderContext.annotationMode = AnnotationMode.ENABLE_FORMS; + renderContext.annotationMode = task.annotationStorage + ? AnnotationMode.ENABLE_STORAGE + : AnnotationMode.ENABLE_FORMS; } else if (renderPrint) { if (task.annotationStorage) { renderContext.annotationMode = AnnotationMode.ENABLE_STORAGE; diff --git a/test/pdfs/issue14301.pdf.link b/test/pdfs/issue14301.pdf.link new file mode 100644 index 00000000000000..63b5eeeb23e168 --- /dev/null +++ b/test/pdfs/issue14301.pdf.link @@ -0,0 +1,2 @@ +https://github.com/mozilla/pdf.js/files/7594316/formulairecerfa90nov00-1.pdf + diff --git a/test/test_manifest.json b/test/test_manifest.json index 8fd040b75813c0..f88ba2255631e7 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -6507,5 +6507,26 @@ "rounds": 1, "link": true, "type": "other" + }, + { "id": "issue14301", + "file": "pdfs/issue14301.pdf", + "md5": "9973936dcf8dd41daa62c04a4eb621f0", + "rounds": 1, + "link": true, + "firstPage": 2, + "lastPage": 2, + "type": "eq", + "forms": true, + "annotationStorage": { + "1832R": { + "value": "3" + }, + "1827R": { + "value": "2" + }, + "1808R": { + "value": "1" + } + } } ] diff --git a/web/annotation_layer_builder.css b/web/annotation_layer_builder.css index f0aaec32e8c8b5..4ab85caf3e4f06 100644 --- a/web/annotation_layer_builder.css +++ b/web/annotation_layer_builder.css @@ -59,10 +59,9 @@ background-image: var(--annotation-unfocused-field-background); border: 1px solid transparent; box-sizing: border-box; - font-size: 9px; + font: 9px sans-serif; height: 100%; margin: 0; - padding: 0 3px; vertical-align: top; width: 100%; } @@ -76,8 +75,6 @@ } .annotationLayer .textWidgetAnnotation textarea { - font: message-box; - font-size: 9px; resize: none; }