Merge pull request #21413 from calixteman/improve_comb

Improve rendering of comb text fields
This commit is contained in:
calixteman 2026-06-09 23:10:49 +02:00 committed by GitHub
commit a13f2aa793
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 66 additions and 29 deletions

View File

@ -2559,7 +2559,6 @@ class WidgetAnnotation extends Annotation {
fontSize,
totalWidth,
totalHeight,
defaultHPadding,
defaultVPadding,
descent,
lineHeight,
@ -2924,7 +2923,6 @@ class TextWidgetAnnotation extends WidgetAnnotation {
fontSize,
width,
height,
hPadding,
vPadding,
descent,
lineHeight,
@ -2936,24 +2934,34 @@ class TextWidgetAnnotation extends WidgetAnnotation {
// Empty or it has a trailing whitespace.
const colors = this.getBorderAndBackgroundAppearances(annotationStorage);
const buf = [];
const positions = font.getCharPositions(text);
for (const [start, end] of positions) {
buf.push(`(${escapeString(text.substring(start, end))}) Tj`);
}
const cells = font.getCharPositions(text).map(([start, end]) => {
const glyph = text.substring(start, end);
return { glyph, width: this._getTextWidth(glyph, font) * fontSize };
});
if (isRTL) {
buf.reverse();
cells.reverse();
}
const textWidth = combWidth * positions.length;
let hShift = hPadding;
const textWidth = combWidth * cells.length;
let hShift = 0;
if (alignment === 1) {
hShift += Math.floor((width - textWidth) / (2 * combWidth)) * combWidth;
} else if (alignment === 2) {
hShift += width - textWidth;
}
const renderedComb = buf.join(` ${numberToString(combWidth)} 0 Td `);
const buf = [];
let previousWidth = 0;
for (let i = 0, ii = cells.length; i < ii; i++) {
const { glyph, width: glyphWidth } = cells[i];
const shift =
i === 0
? (combWidth - glyphWidth) / 2
: combWidth + (previousWidth - glyphWidth) / 2;
buf.push(`${numberToString(shift)} 0 Td (${escapeString(glyph)}) Tj`);
previousWidth = glyphWidth;
}
const renderedComb = buf.join(" ");
return (
`/Tx BMC q ${colors}BT ` +
defaultAppearance +

View File

@ -1450,7 +1450,7 @@ class WidgetAnnotationElement extends AnnotationElement {
style.color = Util.makeHexColor(...fontColor);
if (this.data.textAlignment !== null) {
if (this.data.textAlignment !== null && !this.data.comb) {
style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
}
}
@ -1879,7 +1879,30 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
const combWidth = fieldWidth / maxLen;
element.classList.add("comb");
element.style.letterSpacing = `calc(${combWidth}px * var(--total-scale-factor) - 1ch)`;
element.style.setProperty(
"--comb-width",
`calc(${combWidth}px * var(--total-scale-factor))`
);
const alignment = this.data.textAlignment;
if (alignment === 1 || alignment === 2) {
const setCombOffset = () => {
const free = maxLen - element.value.length;
element.style.setProperty(
"--comb-offset",
`${alignment === 1 ? free >> 1 : free}`
);
};
setCombOffset();
for (const evt of [
"input",
"blur",
"resetform",
"updatefromsandbox",
]) {
element.addEventListener(evt, setCombOffset);
}
}
}
} else {
element = document.createElement("div");

View File

@ -2051,11 +2051,11 @@ describe("annotation", function () {
annotationStorage
);
expect(appearance).toEqual(
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 2 3.07 Tm" +
" (a) Tj 8 0 Td (a) Tj 8 0 Td (\\() Tj" +
" 8 0 Td (a) Tj 8 0 Td (a) Tj" +
" 8 0 Td (\\)) Tj 8 0 Td (a) Tj" +
" 8 0 Td (\\\\) Tj ET Q EMC"
"/Tx BMC q BT /Helv 5 Tf 1 0 0 1 0 3.07 Tm" +
" 2.61 0 Td (a) Tj 8 0 Td (a) Tj 8.56 0 Td (\\() Tj" +
" 7.44 0 Td (a) Tj 8 0 Td (a) Tj" +
" 8.56 0 Td (\\)) Tj 7.44 0 Td (a) Tj" +
" 8.7 0 Td (\\\\) Tj ET Q EMC"
);
});
@ -2092,8 +2092,8 @@ describe("annotation", function () {
annotationStorage
);
expect(appearance).toEqual(
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 2 3.07 Tm" +
" (\x30\x53) Tj 8 0 Td (\x30\x93) Tj 8 0 Td (\x30\x6b) Tj" +
"/Tx BMC q BT /Goth 5 Tf 1 0 0 1 0 3.07 Tm" +
" 1.5 0 Td (\x30\x53) Tj 8 0 Td (\x30\x93) Tj 8 0 Td (\x30\x6b) Tj" +
" 8 0 Td (\x30\x61) Tj 8 0 Td (\x30\x6f) Tj" +
" 8 0 Td (\x4e\x16) Tj 8 0 Td (\x75\x4c) Tj" +
" 8 0 Td (\x30\x6e) Tj ET Q EMC"

View File

@ -292,19 +292,25 @@
}
.textWidgetAnnotation input.comb {
--comb-width: 0px;
--comb-letter-spacing: calc(var(--comb-width) - 1ch);
font-family: monospace;
padding-left: 2px;
padding-right: 0;
letter-spacing: var(--comb-letter-spacing);
overflow-x: hidden;
padding: 0;
text-indent: calc(
var(--comb-offset, 0) * var(--comb-width) + var(--comb-letter-spacing) / 2
);
}
.textWidgetAnnotation input.comb:focus {
/*
* Letter spacing is placed on the right side of each character. Hence, the
* letter spacing of the last character may be placed outside the visible
* area, causing horizontal scrolling. We avoid this by extending the width
* when the element has focus and revert this when it loses focus.
*/
width: 103%;
background-image: repeating-linear-gradient(
to right,
transparent 0 calc(var(--comb-width) - 1px),
var(--input-focus-border-color) calc(var(--comb-width) - 1px)
var(--comb-width)
);
}
.buttonWidgetAnnotation:is(.checkBox, .radioButton) input {