mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-08 01:10:08 +02:00
Merge pull request #18283 from nicolo-ribaudo/ignore-browser-min-font-size
Override the minimum font size when rendering the text layer
This commit is contained in:
commit
11cb3a8e11
2 changed files with 79 additions and 3 deletions
|
@ -83,6 +83,8 @@ class TextLayer {
|
||||||
|
|
||||||
static #canvasContexts = new Map();
|
static #canvasContexts = new Map();
|
||||||
|
|
||||||
|
static #minFontSize = null;
|
||||||
|
|
||||||
static #pendingTextLayers = new Set();
|
static #pendingTextLayers = new Set();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,6 +122,8 @@ class TextLayer {
|
||||||
this.#pageWidth = pageWidth;
|
this.#pageWidth = pageWidth;
|
||||||
this.#pageHeight = pageHeight;
|
this.#pageHeight = pageHeight;
|
||||||
|
|
||||||
|
TextLayer.#ensureMinFontSizeComputed();
|
||||||
|
|
||||||
setLayerDimensions(container, viewport);
|
setLayerDimensions(container, viewport);
|
||||||
|
|
||||||
// Always clean-up the temporary canvas once rendering is no longer pending.
|
// Always clean-up the temporary canvas once rendering is no longer pending.
|
||||||
|
@ -242,7 +246,7 @@ class TextLayer {
|
||||||
if (this.#disableProcessItems) {
|
if (this.#disableProcessItems) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#layoutTextParams.ctx ||= TextLayer.#getCtx(this.#lang);
|
this.#layoutTextParams.ctx ??= TextLayer.#getCtx(this.#lang);
|
||||||
|
|
||||||
const textDivs = this.#textDivs,
|
const textDivs = this.#textDivs,
|
||||||
textContentItemsStr = this.#textContentItemsStr;
|
textContentItemsStr = this.#textContentItemsStr;
|
||||||
|
@ -326,7 +330,11 @@ class TextLayer {
|
||||||
divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`;
|
divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`;
|
||||||
divStyle.top = `${scaleFactorStr}${top.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;
|
divStyle.fontFamily = fontFamily;
|
||||||
|
|
||||||
textDivProperties.fontSize = fontHeight;
|
textDivProperties.fontSize = fontHeight;
|
||||||
|
@ -388,7 +396,12 @@ class TextLayer {
|
||||||
#layout(params) {
|
#layout(params) {
|
||||||
const { div, properties, ctx, prevFontSize, prevFontFamily } = params;
|
const { div, properties, ctx, prevFontSize, prevFontFamily } = params;
|
||||||
const { style } = div;
|
const { style } = div;
|
||||||
|
|
||||||
let transform = "";
|
let transform = "";
|
||||||
|
if (TextLayer.#minFontSize > 1) {
|
||||||
|
transform = `scale(${1 / TextLayer.#minFontSize})`;
|
||||||
|
}
|
||||||
|
|
||||||
if (properties.canvasWidth !== 0 && properties.hasText) {
|
if (properties.canvasWidth !== 0 && properties.hasText) {
|
||||||
const { fontFamily } = style;
|
const { fontFamily } = style;
|
||||||
const { canvasWidth, fontSize } = properties;
|
const { canvasWidth, fontSize } = properties;
|
||||||
|
@ -403,7 +416,7 @@ class TextLayer {
|
||||||
const { width } = ctx.measureText(div.textContent);
|
const { width } = ctx.measureText(div.textContent);
|
||||||
|
|
||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
transform = `scaleX(${(canvasWidth * this.#scale) / width})`;
|
transform = `scaleX(${(canvasWidth * this.#scale) / width}) ${transform}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (properties.angle !== 0) {
|
if (properties.angle !== 0) {
|
||||||
|
@ -456,6 +469,26 @@ class TextLayer {
|
||||||
return canvasContext;
|
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) {
|
static #getAscent(fontFamily, lang) {
|
||||||
const cachedAscent = this.#ascentCache.get(fontFamily);
|
const cachedAscent = this.#ascentCache.get(fontFamily);
|
||||||
if (cachedAscent) {
|
if (cachedAscent) {
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue