Skip to content

Commit

Permalink
Merge pull request #18283 from nicolo-ribaudo/ignore-browser-min-font…
Browse files Browse the repository at this point in the history
…-size

Override the minimum font size when rendering the text layer
  • Loading branch information
timvandermeij committed Jun 25, 2024
2 parents 5885874 + 5b29e93 commit 11cb3a8
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 3 deletions.
39 changes: 36 additions & 3 deletions src/display/text_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class TextLayer {

static #canvasContexts = new Map();

static #minFontSize = null;

static #pendingTextLayers = new Set();

/**
Expand Down Expand Up @@ -120,6 +122,8 @@ class TextLayer {
this.#pageWidth = pageWidth;
this.#pageHeight = pageHeight;

TextLayer.#ensureMinFontSizeComputed();

setLayerDimensions(container, viewport);

// Always clean-up the temporary canvas once rendering is no longer pending.
Expand Down Expand Up @@ -242,7 +246,7 @@ class TextLayer {
if (this.#disableProcessItems) {
return;
}
this.#layoutTextParams.ctx ||= TextLayer.#getCtx(this.#lang);
this.#layoutTextParams.ctx ??= TextLayer.#getCtx(this.#lang);

const textDivs = this.#textDivs,
textContentItemsStr = this.#textContentItemsStr;
Expand Down Expand Up @@ -326,7 +330,11 @@ class TextLayer {
divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`;
divStyle.top = `${scaleFactorStr}${top.toFixed(2)}px)`;
}
divStyle.fontSize = `${scaleFactorStr}${fontHeight.toFixed(2)}px)`;
// We multiply the font size by #minFontSize, and then #layout will
// scale the element by 1/#minFontSize. This allows us to effectively
// ignore the minimum font size enforced by the browser, so that the text
// layer <span>s can always match the size of the text in the canvas.
divStyle.fontSize = `${scaleFactorStr}${(TextLayer.#minFontSize * fontHeight).toFixed(2)}px)`;
divStyle.fontFamily = fontFamily;

textDivProperties.fontSize = fontHeight;
Expand Down Expand Up @@ -388,7 +396,12 @@ class TextLayer {
#layout(params) {
const { div, properties, ctx, prevFontSize, prevFontFamily } = params;
const { style } = div;

let transform = "";
if (TextLayer.#minFontSize > 1) {
transform = `scale(${1 / TextLayer.#minFontSize})`;
}

if (properties.canvasWidth !== 0 && properties.hasText) {
const { fontFamily } = style;
const { canvasWidth, fontSize } = properties;
Expand All @@ -403,7 +416,7 @@ class TextLayer {
const { width } = ctx.measureText(div.textContent);

if (width > 0) {
transform = `scaleX(${(canvasWidth * this.#scale) / width})`;
transform = `scaleX(${(canvasWidth * this.#scale) / width}) ${transform}`;
}
}
if (properties.angle !== 0) {
Expand Down Expand Up @@ -456,6 +469,26 @@ class TextLayer {
return canvasContext;
}

/**
* Compute the minimum font size enforced by the browser.
*/
static #ensureMinFontSizeComputed() {
if (this.#minFontSize !== null) {
return;
}
const div = document.createElement("div");
div.style.opacity = 0;
div.style.lineHeight = 1;
div.style.fontSize = "1px";
div.textContent = "X";
document.body.append(div);
// In `display:block` elements contain a single line of text,
// the height matches the line height (which, when set to 1,
// matches the actual font size).
this.#minFontSize = div.getBoundingClientRect().height;
div.remove();
}

static #getAscent(fontFamily, lang) {
const cachedAscent = this.#ascentCache.get(fontFamily);
if (cachedAscent) {
Expand Down
43 changes: 43 additions & 0 deletions test/integration/text_layer_spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,47 @@ describe("Text layer", () => {
});
});
});

describe("when the browser enforces a minimum font size", () => {
let browser;
let page;

beforeAll(async () => {
// Only testing in Firefox because, while Chrome has a setting similar to
// font.minimum-size.x-western, it is not exposed through its API.
browser = await startBrowser({
browserName: "firefox",
startUrl: "",
extraPrefsFirefox: { "font.minimum-size.x-western": 40 },
});
page = await browser.newPage();
await page.goto(
`${global.integrationBaseUrl}?file=/test/pdfs/tracemonkey.pdf#zoom=100`
);
await page.bringToFront();
await page.waitForSelector(
`.page[data-page-number = "1"] .endOfContent`,
{ timeout: 0 }
);
});

afterAll(async () => {
await closeSinglePage(page);
await browser.close();
});

it("renders spans with the right size", async () => {
const rect = await getSpanRectFromText(
page,
1,
"Dynamic languages such as JavaScript are more difficult to com-"
);

// The difference between `a` and `b`, as a percentage of the lower one
const getPercentDiff = (a, b) => Math.max(a, b) / Math.min(a, b) - 1;

expect(getPercentDiff(rect.width, 315)).toBeLessThan(0.03);
expect(getPercentDiff(rect.height, 12)).toBeLessThan(0.03);
});
});
});

0 comments on commit 11cb3a8

Please sign in to comment.