Public
Edited
Jul 14, 2023
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
spanBasedSize = spanBasedEstimator(testText, fontSize)
Insert cell
canvasBasedEstimator = newTextSizeEstimator(
buildCanvasBasedCharSizeEstimator(estimatorsConfig)
)
Insert cell
spanBasedEstimator = newTextSizeEstimator(
await buildSpanBasedCharSizeEstimator(estimatorsConfig)
)
Insert cell
canvasBasedSize = canvasBasedEstimator(testText, fontSize)
Insert cell
Insert cell
function newTextSizeEstimator(measureCharWidth, baseTextSize) {
baseTextSize = baseTextSize || measureCharWidth.size;
const table = buildTextWidthEstimatorTable(measureCharWidth);
const estimate = newTableBasedCharWidthEstimator(table);
return _newTextSizeEstimator(estimate, baseTextSize);

function _newTextSizeEstimator(measureCharWidth, baseTextSize) {
const index = {};
return (str, size = baseTextSize) => {
let width = str.split("").reduce((w, ch) => {
let width = index[ch];
if (!width) {
width = index[ch] = measureCharWidth(ch);
}
return width + w;
}, 0);
if (size !== baseTextSize) {
width *= size / baseTextSize;
}
return [width, size];
};
}
}
Insert cell
function newTableBasedCharWidthEstimator(table) {
let count = 0;
let sum = 0;
for( let i =0; i < table.length; i++) {
if (table[i] > 0) {
sum += table[i];
count++;
}
}
const avg = count > 0 ? sum / count : 0;
return (ch) => {
const code = ch.charCodeAt(0);
return (code < table.length) ? table[code] : avg;
}
}
Insert cell
// See https://jrgraphix.net/r/Unicode/
function buildTextWidthEstimatorTable(measureCharWidth, count = 0x20cf) {
const table = new Float32Array(count);
for (let i = 0; i < table.length; i++) {
const ch = String.fromCharCode(i);
table[i] = measureCharWidth(ch);
}
return table;
}
Insert cell
function buildCanvasBasedCharSizeEstimator({
fontSize = "16px",
fontFamily = "Arial",
fontWeight = "normal"
}) {
const canvas = document.createElement("canvas");
const size = parseFontSize(fontSize);
canvas.width = size * 10;
canvas.height = size * 2;
const fontSpec = [fontWeight, fontSize, fontFamily];
const ctx = canvas.getContext("2d");
ctx.font = fontSpec.join(" ");

const fontHeight = parseFontSize(fontSize);
const index = {};
function measureCharWidth(ch) {
return (index[ch] = index[ch] || ctx.measureText(ch).width);
}
return Object.assign(measureCharWidth, {
size,
// Do nothing. This method is added to be compatible with span-based chars estimators
cleanup: () => {}
});
}
Insert cell
async function buildSpanBasedCharSizeEstimator({
fontSize = "10px",
fontFamily = "Arial",
fontWeight = "normal"
} = {}) {
const span = document.createElement("span");
Object.assign(span.style, {
display: "block",
whiteSpace: "pre",
position: "fixed",
top: -1000,
left: -1000,
opacity: 0,
padding: "0",
marging: "0",
fontSize,
fontFamily,
fontWeight
});
document.body.appendChild(span);

const index = {};
function measureCharWidth(ch) {
let width = index[ch];
if (!width) {
span.innerText = ch;
width = span.getBoundingClientRect().width;
// width = span.clientWidth;
index[ch] = width;
}
return width;
}
await new Promise((y) => requestAnimationFrame(y));
let cleanup = () => {
cleanup = () => {};
span.parentElement && span.parentElement.removeChild(span);
};
return Object.assign(measureCharWidth, {
size: parseFontSize(fontSize),
cleanup: () => cleanup()
});
}
Insert cell
Insert cell
function getFontParams(el = document.body) {
const fontWeight = getCssStyle(el, "font-weight") || "normal";
const fontSize = getCssStyle(el, "font-size") || "16px";
const fontFamily = getCssStyle(el, "font-family") || "Times New Roman";
return {
fontSize,
fontFamily,
fontWeight
};
}
Insert cell
function getCssStyle(element, prop) {
return window.getComputedStyle(element, null).getPropertyValue(prop);
}
Insert cell
function parseFontSize(fontSize, defaultFontSize = 16) {
let size = 16,
unit = "px";
("" + fontSize).replace(
/([\d\.]+)(.*)$/,
(_, s, u) => ((size = s), (unit = u), "")
);
size = parseFloat(size);
if (unit === "em" || unit === "rem") {
size *= defaultFontSize;
}
return size;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more