Skip to content

Commit

Permalink
Merge pull request #18089 from Snuffleupagus/textLayer-private
Browse files Browse the repository at this point in the history
Improve the `TextLayerRenderTask` implementation
  • Loading branch information
timvandermeij committed May 14, 2024
2 parents 40f9371 + 036fd11 commit c6d7ae2
Showing 1 changed file with 97 additions and 103 deletions.
200 changes: 97 additions & 103 deletions src/display/text_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,97 +162,6 @@ function getAscent(fontFamily, lang) {
return DEFAULT_FONT_ASCENT;
}

function appendText(task, geom, lang) {
// Initialize all used properties to keep the caches monomorphic.
const textDiv = document.createElement("span");
const textDivProperties = {
angle: 0,
canvasWidth: 0,
hasText: geom.str !== "",
hasEOL: geom.hasEOL,
fontSize: 0,
};
task._textDivs.push(textDiv);

const tx = Util.transform(task._transform, geom.transform);
let angle = Math.atan2(tx[1], tx[0]);
const style = task._styleCache[geom.fontName];
if (style.vertical) {
angle += Math.PI / 2;
}

const fontFamily =
(task._fontInspectorEnabled && style.fontSubstitution) || style.fontFamily;
const fontHeight = Math.hypot(tx[2], tx[3]);
const fontAscent = fontHeight * getAscent(fontFamily, lang);

let left, top;
if (angle === 0) {
left = tx[4];
top = tx[5] - fontAscent;
} else {
left = tx[4] + fontAscent * Math.sin(angle);
top = tx[5] - fontAscent * Math.cos(angle);
}

const scaleFactorStr = "calc(var(--scale-factor)*";
const divStyle = textDiv.style;
// Setting the style properties individually, rather than all at once,
// should be OK since the `textDiv` isn't appended to the document yet.
if (task._container === task._rootContainer) {
divStyle.left = `${((100 * left) / task._pageWidth).toFixed(2)}%`;
divStyle.top = `${((100 * top) / task._pageHeight).toFixed(2)}%`;
} else {
// We're in a marked content span, hence we can't use percents.
divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`;
divStyle.top = `${scaleFactorStr}${top.toFixed(2)}px)`;
}
divStyle.fontSize = `${scaleFactorStr}${fontHeight.toFixed(2)}px)`;
divStyle.fontFamily = fontFamily;

textDivProperties.fontSize = fontHeight;

// Keeps screen readers from pausing on every new text span.
textDiv.setAttribute("role", "presentation");

textDiv.textContent = geom.str;
// geom.dir may be 'ttb' for vertical texts.
textDiv.dir = geom.dir;

// `fontName` is only used by the FontInspector, and we only use `dataset`
// here to make the font name available in the debugger.
if (task._fontInspectorEnabled) {
textDiv.dataset.fontName =
style.fontSubstitutionLoadedName || geom.fontName;
}
if (angle !== 0) {
textDivProperties.angle = angle * (180 / Math.PI);
}
// We don't bother scaling single-char text divs, because it has very
// little effect on text highlighting. This makes scrolling on docs with
// lots of such divs a lot faster.
let shouldScaleText = false;
if (geom.str.length > 1) {
shouldScaleText = true;
} else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) {
const absScaleX = Math.abs(geom.transform[0]),
absScaleY = Math.abs(geom.transform[3]);
// When the horizontal/vertical scaling differs significantly, also scale
// even single-char text to improve highlighting (fixes issue11713.pdf).
if (
absScaleX !== absScaleY &&
Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5
) {
shouldScaleText = true;
}
}
if (shouldScaleText) {
textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width;
}
task._textDivProperties.set(textDiv, textDivProperties);
task._layoutText(textDiv);
}

function layout(params) {
const { div, scale, properties, ctx, prevFontSize, prevFontFamily } = params;
const { style } = div;
Expand Down Expand Up @@ -368,10 +277,7 @@ class TextLayerRenderTask {
this._capability.reject(abortEx);
}

/**
* @private
*/
_processItems(items, lang) {
#processItems(items, lang) {
if (!this._layoutTextParams.ctx) {
this._textDivProperties.set(this._rootContainer, { lang });
this._layoutTextParams.ctx = getCtx(lang);
Expand Down Expand Up @@ -407,17 +313,105 @@ class TextLayerRenderTask {
continue;
}
textContentItemsStr.push(item.str);
appendText(this, item, lang);
this.#appendText(item, lang);
}
}

/**
* @private
*/
_layoutText(textDiv) {
const textDivProperties = (this._layoutTextParams.properties =
this._textDivProperties.get(textDiv));
#appendText(geom, lang) {
// Initialize all used properties to keep the caches monomorphic.
const textDiv = document.createElement("span");
const textDivProperties = {
angle: 0,
canvasWidth: 0,
hasText: geom.str !== "",
hasEOL: geom.hasEOL,
fontSize: 0,
};
this._textDivs.push(textDiv);

const tx = Util.transform(this._transform, geom.transform);
let angle = Math.atan2(tx[1], tx[0]);
const style = this._styleCache[geom.fontName];
if (style.vertical) {
angle += Math.PI / 2;
}

const fontFamily =
(this._fontInspectorEnabled && style.fontSubstitution) ||
style.fontFamily;
const fontHeight = Math.hypot(tx[2], tx[3]);
const fontAscent = fontHeight * getAscent(fontFamily, lang);

let left, top;
if (angle === 0) {
left = tx[4];
top = tx[5] - fontAscent;
} else {
left = tx[4] + fontAscent * Math.sin(angle);
top = tx[5] - fontAscent * Math.cos(angle);
}

const scaleFactorStr = "calc(var(--scale-factor)*";
const divStyle = textDiv.style;
// Setting the style properties individually, rather than all at once,
// should be OK since the `textDiv` isn't appended to the document yet.
if (this._container === this._rootContainer) {
divStyle.left = `${((100 * left) / this._pageWidth).toFixed(2)}%`;
divStyle.top = `${((100 * top) / this._pageHeight).toFixed(2)}%`;
} else {
// We're in a marked content span, hence we can't use percents.
divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`;
divStyle.top = `${scaleFactorStr}${top.toFixed(2)}px)`;
}
divStyle.fontSize = `${scaleFactorStr}${fontHeight.toFixed(2)}px)`;
divStyle.fontFamily = fontFamily;

textDivProperties.fontSize = fontHeight;

// Keeps screen readers from pausing on every new text span.
textDiv.setAttribute("role", "presentation");

textDiv.textContent = geom.str;
// geom.dir may be 'ttb' for vertical texts.
textDiv.dir = geom.dir;

// `fontName` is only used by the FontInspector, and we only use `dataset`
// here to make the font name available in the debugger.
if (this._fontInspectorEnabled) {
textDiv.dataset.fontName =
style.fontSubstitutionLoadedName || geom.fontName;
}
if (angle !== 0) {
textDivProperties.angle = angle * (180 / Math.PI);
}
// We don't bother scaling single-char text divs, because it has very
// little effect on text highlighting. This makes scrolling on docs with
// lots of such divs a lot faster.
let shouldScaleText = false;
if (geom.str.length > 1) {
shouldScaleText = true;
} else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) {
const absScaleX = Math.abs(geom.transform[0]),
absScaleY = Math.abs(geom.transform[3]);
// When the horizontal/vertical scaling differs significantly, also scale
// even single-char text to improve highlighting (fixes issue11713.pdf).
if (
absScaleX !== absScaleY &&
Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5
) {
shouldScaleText = true;
}
}
if (shouldScaleText) {
textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width;
}
this._textDivProperties.set(textDiv, textDivProperties);
this.#layoutText(textDiv, textDivProperties);
}

#layoutText(textDiv, textDivProperties) {
this._layoutTextParams.div = textDiv;
this._layoutTextParams.properties = textDivProperties;
layout(this._layoutTextParams);

if (textDivProperties.hasText) {
Expand All @@ -444,7 +438,7 @@ class TextLayerRenderTask {
}

Object.assign(styleCache, value.styles);
this._processItems(value.items, value.lang);
this.#processItems(value.items, value.lang);
pump();
}, this._capability.reject);
};
Expand Down

0 comments on commit c6d7ae2

Please sign in to comment.