Как воссоздать/получить эти данные, отображаемые в этом пользовательском интерфейсе?
В основном это восходящий элемент, высота заглавной буквы, высота по оси x и т. д. Материал справа можно получить из глифа Юникода или получить из данных Юникода, но левый материал выглядит как данные глифа, специфичные для шрифта.
Вопросы:
advanceWidth
, leftSideBearing
, xMin
, xMax
, yMin
и yMax
, но это не похоже на это изображение.... Есть ли там есть все, что есть на этом изображении?Любая помощь будет оценена по достоинству.
🤔 А знаете ли вы, что...
С JavaScript можно создавать интерактивные формы и проверять введенные пользователем данные.
Свойства, которые вы ищете, хранятся глобально, а не в данных глифов (например, в таблице os2).
let fontSrc =
"https://fonts.gstatic.com/s/robotoflex/v9/NaPccZLOBv5T3oB7Cb4i0zu6RME.woff2";
getVerticalMetrics(fontSrc);
async function getVerticalMetrics(fontSrc) {
let font = await loadFont(fontSrc);
let fontFamily = font.tables.name.fontFamily.en;
// collect data records
let {unitsPerEm, ascender, descender} = font
//get x-height from os2 table
let xHeight = font.tables.os2.sxHeight
let capHeight = font.tables.os2.sCapHeight
//optional: get x height from glyph data
let xHeight_x = font.charToGlyph('x').yMax
let capHeight_H = font.charToGlyph('H').yMax
let data = {
fontFamily: fontFamily,
xHeight: xHeight,
capHeight: capHeight,
ascender: ascender,
descender: descender,
unitsPerEm: unitsPerEm
}
pre.textContent= JSON.stringify(data, null, ' ');
// render example
let fontSize = 100;
let scale = fontSize / unitsPerEm;
let lineHeight = (ascender + Math.abs(descender) ) * scale;
let ratAsc = ascender / unitsPerEm;
let yOffset = fontSize * ratAsc;
let path = font.getPath('Hxg', 0, yOffset, fontSize)
let pathData = path.toPathData(1)
preview.setAttribute('d', pathData)
let yBaseline = ascender*scale
let yXHeight = yBaseline-xHeight*scale
let ypathCapHeight = yBaseline - capHeight*scale
pathBaseline.setAttribute('y1',yBaseline );
pathBaseline.setAttribute('y2',yBaseline );
pathXheight.setAttribute('y1',yXHeight );
pathXheight.setAttribute('y2',yXHeight );
pathCapHeight.setAttribute('y1',ypathCapHeight );
pathCapHeight.setAttribute('y2',ypathCapHeight );
svg.setAttribute('viewBox', [0, 0, 200, lineHeight])
}
/**
* opentype.js helper
* Based on @yne's comment
* https://github.com/opentypejs/opentype.js/issues/183#issuecomment-1147228025
* will decompress woff2 files
*/
async function loadFont(src, options = {}) {
let buffer = {};
let font = {};
let ext = 'woff2';
let url;
// 1. is file
if (src instanceof Object) {
// get file extension to skip woff2 decompression
let filename = src.name.split(".");
ext = filename[filename.length - 1];
buffer = await src.arrayBuffer();
}
// 2. is base64 data URI
else if (/^data/.test(src)) {
// is base64
let data = src.split(";");
ext = data[0].split("/")[1];
// create buffer from blob
let srcBlob = await (await fetch(src)).blob();
buffer = await srcBlob.arrayBuffer();
}
// 3. is url
else {
// if google font css - retrieve font src
if (/googleapis.com/.test(src)) {
ext = 'woff2';
src = await getGoogleFontUrl(src, options);
}
// might be subset - no extension
let hasExt = (src.includes('.woff2') || src.includes('.woff') || src.includes('.ttf') || src.includes('.otf')) ? true : false;
url = src.split(".");
ext = hasExt ? url[url.length - 1] : 'woff2';
let fetchedSrc = await fetch(src);
buffer = await fetchedSrc.arrayBuffer();
}
// decompress woff2
if (ext === "woff2") {
buffer = Uint8Array.from(Module.decompress(buffer)).buffer;
}
// parse font
font = opentype.parse(buffer);
return font;
}
svg{
border: 1px solid #ccc;
}
<!-- neeeded for woff2 fonts/brotli decompression -->
<script src = "https://unpkg.com/[email protected]/build/decompress_binding.js"></script>
<script src='https://cdn.jsdelivr.net/npm/opentype.js@latest/dist/opentype.min.js'></script>
<div id = "sample">
<svg id = "svg" viewBox = "0 0 200 100">
<path id = "preview" />
<line x1 = "0" y1 = "0" x2 = "100%" y2 = "" id = "pathBaseline" stroke = "red" fill = "none" />
<line x1 = "0" y1 = "0" x2 = "100%" y2 = "" id = "pathXheight" stroke = "green" fill = "none" />
<line x1 = "0" y1 = "0" x2 = "100%" y2 = "" id = "pathCapHeight" stroke = "#ccc" fill = "none" />
</svg>
</div>
<code>
<pre id = "pre"></pre>
</code>
Шрифты используют классическое декартово координатное пространство (ось Y идет снизу вверх), тогда как svg имеет вертикально перевернутую ось Y (сверху вниз).
Поэтому вам необходимо преобразовать значения для рендеринга SVG или Canvas.
Имейте в виду, что все единицы измерения относятся к UPM шрифта (значения единиц на em).
Поэтому вам может потребоваться вычислить относительное значение желаемого размера шрифта для рендеринга. Например, открытый тип .otf будет возвращать значения на основе 1000 UPM, тогда как версия truetype .ttf возвращает значения (обычно) на основе соотношения 2048 UPM.
Формат шрифта имеет значение только для распаковки перед анализом:
Для woff2 требуется сложный скрипт декомпрессии Brotli, который значительно увеличивает общее время загрузки и обработки.
При загрузке woff или truetype вы можете использовать уже включенные функции deflate.
Шрифты .svg устарели. Однако, если у вас также есть SVG-версия вашего текущего шрифта, вы также можете загрузить ее через fetch()
и написать свой собственный анализатор шрифтов SVG.
let font = new DOMParser().parseFromString(svgFont, 'text/html').querySelector('font')
let fontface = font.querySelector('font-face')
let data = [...fontface.attributes].map( item=>{let obj = {}; obj[item.name]=item.value; return obj})
console.info(data)
<script>
let svgFont = `<svg xmlns = "http://www.w3.org/2000/svg" xmlns:xlink = "http://www.w3.org/1999/xlink" version = "1.1">
<metadata>
Created by FontForge 20201107 at Mon Jun 22 12:22:04 2020
By Jimmy Wärting
Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
</metadata>
<defs>
<font id = "Poppins-Medium" horiz-adv-x = "678" >
<font-face
font-family = "Poppins Medium"
font-weight = "500"
font-stretch = "normal"
units-per-em = "1000"
panose-1 = "0 0 6 0 0 0 0 0 0 0"
ascent = "800"
descent = "-200"
x-height = "551"
cap-height = "695"
bbox = "-28 -272 1048 969"
underline-thickness = "50"
underline-position = "-100"
unicode-range = "U+000D-2215"
/>
<missing-glyph horiz-adv-x = "500"
d = "M0 700h500v-700h-500v700zM420 650h-340l170 -255zM50 95l170 255l-170 255v-510zM450 95v510l-170 -255zM420 50l-170 255l-170 -255h340z" />
<glyph glyph-name = ".notdef" horiz-adv-x = "500"
d = "M0 700h500v-700h-500v700zM420 650h-340l170 -255zM50 95l170 255l-170 255v-510zM450 95v510l-170 -255zM420 50l-170 255l-170 -255h340z" />
<glyph glyph-name = "NULL" horiz-adv-x = "0"
/>
<glyph glyph-name = "NULL" horiz-adv-x = "0"
/>
<glyph glyph-name = "CR" unicode = "
" horiz-adv-x = "260"
/>
<glyph glyph-name = "space" unicode = " " horiz-adv-x = "260"
/>
<glyph glyph-name = "exclam" unicode = "!" horiz-adv-x = "321"
d = "M218 695l-13 -485h-95l-13 485h121zM109 14q-21 21 -21 52t21 52t52 21q30 0 51 -21t21 -52t-21 -52t-51 -21q-31 0 -52 21z" />
<glyph glyph-name = "quotedbl" unicode = """ horiz-adv-x = "323"
d = "M137 797l-12 -205h-78l-13 205h103zM288 797l-12 -205h-78l-13 205h103z" />
<glyph glyph-name = "numbersign" unicode = "#" horiz-adv-x = "872"
d = "M658 458l-37 -173h132v-100h-153l-40 -185h-109l40 185h-196l-40 -185h-109l40 185h-155v100h176l37 173h-154v100h175l39 182h109l-39 -182h196l39 182h109l-39 -182h133v-100h-154zM549 458h-196l-37 -173h196z" />
</font>
</defs></svg>`
</script>
lib-font.js может быть альтернативой, если вам не нужны функции рендеринга, например, для преобразования контуров глифов в svg.
lib-font.js обычно предоставляет наиболее полную информацию о таблице данных — особенно данные переменных шрифтов (например, информация об осях) часто не полностью реализуются другими анализаторами.
// retrieve font data after all required assets are loaded (e.g for decompression)
let fontSrc =
"https://fonts.gstatic.com/s/robotoflex/v9/NaPccZLOBv5T3oB7Cb4i0zu6RME.woff2";
window.addEventListener("DOMContentLoaded", (e) => {
getVerticalMetrics(fontSrc);
});
async function getVerticalMetrics(fontSrc) {
let font = new Font("fontname", {
skipStyleSheet: true
});
font.src = fontSrc;
font.onload = (evt) => {
let font = evt.detail.font;
let tables = font.opentype.tables;
let os2 = tables["OS/2"];
let ascender = os2.sTypoAscender;
let descender = os2.sTypoDescender;
let xHeight = os2.sxHeight;
let capHeight = os2.sCapHeight;
//console.info(font);
//console.info(os2);
let data = {
xHeight: xHeight,
capHeight: capHeight,
ascender: ascender,
descender: descender,
unitsPerEm: tables.head.unitsPerEm
};
pre.textContent = JSON.stringify(data, null, " ");
};
}
<!-- add brotli decompression needed for woff2 -->
<script src = "https://cdn.jsdelivr.net/npm/[email protected]/lib/unbrotli.js"></script>
<script src = "https://cdn.jsdelivr.net/npm/[email protected]/lib-font.browser.js" type = "module"></script>
<code>
<pre id = "pre"></pre>
</code>