Merge pull request #12896 from calixteman/text_layer
Modifiy the way to compute baseline to have a better match between canvas and text layer
This commit is contained in:
commit
c79fd71457
@ -54,6 +54,9 @@ import {
|
||||
*/
|
||||
const renderTextLayer = (function renderTextLayerClosure() {
|
||||
const MAX_TEXT_DIVS_TO_RENDER = 100000;
|
||||
const DEFAULT_FONT_SIZE = 30;
|
||||
const DEFAULT_FONT_ASCENT = 0.8;
|
||||
const ascentCache = new Map();
|
||||
|
||||
const NonWhitespaceRegexp = /\S/;
|
||||
|
||||
@ -61,7 +64,70 @@ const renderTextLayer = (function renderTextLayerClosure() {
|
||||
return !NonWhitespaceRegexp.test(str);
|
||||
}
|
||||
|
||||
function appendText(task, geom, styles) {
|
||||
function getAscent(fontFamily, ctx) {
|
||||
const cachedAscent = ascentCache.get(fontFamily);
|
||||
if (cachedAscent) {
|
||||
return cachedAscent;
|
||||
}
|
||||
|
||||
ctx.save();
|
||||
ctx.font = `${DEFAULT_FONT_SIZE}px ${fontFamily}`;
|
||||
const metrics = ctx.measureText("");
|
||||
|
||||
// Both properties aren't available by default in Firefox.
|
||||
let ascent = metrics.fontBoundingBoxAscent;
|
||||
let descent = Math.abs(metrics.fontBoundingBoxDescent);
|
||||
if (ascent) {
|
||||
ctx.restore();
|
||||
const ratio = ascent / (ascent + descent);
|
||||
ascentCache.set(fontFamily, ratio);
|
||||
return ratio;
|
||||
}
|
||||
|
||||
// Try basic heuristic to guess ascent/descent.
|
||||
// Draw a g with baseline at 0,0 and then get the line
|
||||
// number where a pixel has non-null red component (starting
|
||||
// from bottom).
|
||||
ctx.strokeStyle = "red";
|
||||
ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE);
|
||||
ctx.strokeText("g", 0, 0);
|
||||
let pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE)
|
||||
.data;
|
||||
descent = 0;
|
||||
for (let i = pixels.length - 1 - 3; i >= 0; i -= 4) {
|
||||
if (pixels[i] > 0) {
|
||||
descent = Math.ceil(i / 4 / DEFAULT_FONT_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw an A with baseline at 0,DEFAULT_FONT_SIZE and then get the line
|
||||
// number where a pixel has non-null red component (starting
|
||||
// from top).
|
||||
ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE);
|
||||
ctx.strokeText("A", 0, DEFAULT_FONT_SIZE);
|
||||
pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data;
|
||||
ascent = 0;
|
||||
for (let i = 0, ii = pixels.length; i < ii; i += 4) {
|
||||
if (pixels[i] > 0) {
|
||||
ascent = DEFAULT_FONT_SIZE - Math.floor(i / 4 / DEFAULT_FONT_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
|
||||
if (ascent) {
|
||||
const ratio = ascent / (ascent + descent);
|
||||
ascentCache.set(fontFamily, ratio);
|
||||
return ratio;
|
||||
}
|
||||
|
||||
ascentCache.set(fontFamily, DEFAULT_FONT_ASCENT);
|
||||
return DEFAULT_FONT_ASCENT;
|
||||
}
|
||||
|
||||
function appendText(task, geom, styles, ctx) {
|
||||
// Initialize all used properties to keep the caches monomorphic.
|
||||
const textDiv = document.createElement("span");
|
||||
const textDivProperties = {
|
||||
@ -90,12 +156,7 @@ const renderTextLayer = (function renderTextLayerClosure() {
|
||||
angle += Math.PI / 2;
|
||||
}
|
||||
const fontHeight = Math.hypot(tx[2], tx[3]);
|
||||
let fontAscent = fontHeight;
|
||||
if (style.ascent) {
|
||||
fontAscent = style.ascent * fontAscent;
|
||||
} else if (style.descent) {
|
||||
fontAscent = (1 + style.descent) * fontAscent;
|
||||
}
|
||||
const fontAscent = fontHeight * getAscent(style.fontFamily, ctx);
|
||||
|
||||
let left, top;
|
||||
if (angle === 0) {
|
||||
@ -578,7 +639,7 @@ const renderTextLayer = (function renderTextLayerClosure() {
|
||||
_processItems(items, styleCache) {
|
||||
for (let i = 0, len = items.length; i < len; i++) {
|
||||
this._textContentItemsStr.push(items[i].str);
|
||||
appendText(this, items[i], styleCache);
|
||||
appendText(this, items[i], styleCache, this._layoutTextCtx);
|
||||
}
|
||||
},
|
||||
|
||||
@ -628,6 +689,8 @@ const renderTextLayer = (function renderTextLayerClosure() {
|
||||
|
||||
// The temporary canvas is used to measure text length in the DOM.
|
||||
const canvas = this._document.createElement("canvas");
|
||||
canvas.height = canvas.width = DEFAULT_FONT_SIZE;
|
||||
|
||||
if (
|
||||
typeof PDFJSDev === "undefined" ||
|
||||
PDFJSDev.test("MOZCENTRAL || GENERIC")
|
||||
|
Loading…
x
Reference in New Issue
Block a user