我想推迟网站上的字体加载,灵感来自于 Smashing Magazine 的延迟字体加载逻辑。
主要部分是将字体转换为 base64 并准备 CSS 文件。到目前为止我的步骤:
Open Sans Bold 的 CSS 片段:
@font-face {
font-family: 'Open Sans';
src: url(data:application/x-font-woff;charset=utf-8;base64,<base64_encoded>) format('woff');
font-weight: 700;
font-style: normal;
}
问题是,转换后的字体看起来有很大不同。看看 Open Sans Bold:
特别注意口音太离谱并且字母绝对可怕a
。其他字体系列和变体看起来也非常明显不同(大小和形状扭曲等)。
如何正确地将 TTF 文件从 Google Web Fonts(或其他来源)编码为 Base64 格式,并以结果与原始文件相同的方式使用它?
或者,如果您对直接从 GWF 渲染字体感到满意,您可以直接获取该文件并自行进行 Base64 编码。在 OS X 或 Linux 中,在 Terminal/shell 中使用内置的 base64 命令:
$ base64 -i myfont.ttf -o fontbase64.txt
@font-face {
font-family: 'myfont';
src: url(data:font/truetype;charset=utf-8;base64,<<copied base64 string>>) format('truetype');
font-weight: normal;
font-style: normal;
}
(请注意,您可能需要对各种@font-face信息进行一些调整以匹配您的特定字体数据;这只是一个示例模板)
function base64convert (files) {
console.clear()
const reader = new FileReader()
reader.onload = (e) => {
console.log(e.target.result)
}
reader.readAsDataURL(files[0])
}
<input type="file" onchange="base64convert(this.files)">
然后复制输出并将其粘贴到您的 CSS 中:
@font-face {
font-family: 'myfont';
src: url("<<copied base64 string>>");
}
专用字体转换器可能会删除功能或更改数据
解析并重建你的字体文件。
此过程还可能会因优化设置(例如影响字体渲染的hinting数据)而引入更改。
2023:许多转换器(例如 fontsquirrel 和 transfonter)不支持可变字体
使用这些工具获取 Base64 数据 URL 时,您在生成数据 URL 时可能会丢失可变字体功能(设计轴相关数据被剥离)。
这并不意味着,您根本不应该使用这些转换器 - 它们大多工作良好,如果
Ilyich所示:您可以使用任何base64编码器。 (例如 browserlings 转换器。
这是基于这些步骤的另一个 JS 帮助器示例:
inputUrl.addEventListener("input", async(e) => {
let url = e.currentTarget.value;
let css = await getdataUrlsFromCss(url)
// output and download button
fontCss.value = css;
btnDownload.href = URL.createObjectURL(new Blob([css]));
});
// init
inputUrl.dispatchEvent(new Event("input"));
async function getdataUrlsFromCss(url) {
// fetch external css
let css = await (await fetch(url)).text();
// find external urls in css via regex
let urls = css.match(/https:\/\/[^)]+/g);
for (let i = 0; i < urls.length; i++) {
let url = urls[i];
// fetch font file
let blob = await (await await fetch(url)).blob();
// create base64 string
let base64 = await blobToBase64(blob);
//replace urls with data url
css = css.replaceAll(url, base64);
}
return css;
}
/**
* fetched blob to base64
*/
function blobToBase64(blob) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
}
body {
font-family: sans-serif
}
legend {
font-weight: bold;
}
fieldset {
margin-bottom: 1em;
}
fieldset input,
fieldset textarea {
border: none
}
input {
width: 100%;
display: block;
margin-bottom: 1em;
}
textarea {
width: 100%;
min-height: 20em;
}
.btn-default {
text-decoration: none;
border: 1px solid #000;
background: #ccc;
color: #000;
font-weight: bold;
padding: 0.3em;
}
<h1>Fonts to base64</h1>
<fieldset>
<legend>Enter CSS Url</legend>
<input type="text" id="inputUrl" value="https://fonts.googleapis.com/css2?family=Open+Sans:ital@0;1&display=swap">
</fieldset>
<fieldset>
<legend>New Css</legend>
<textarea id="fontCss"></textarea>
<p><a class="btn-default" id="btnDownload" href="#" download="fontface.css">Download css</a></p>
</fieldset>
用于测试:
const { readdirSync, mkdir, existsSync, readFileSync, writeFileSync } = require("fs")
const { resolve } = require("path")
const woffDirPath=resolve(".", "public", "assets", "fonts", "Typold", "woff")
const files = readdirSync(woffDirPath)
const base64Path = resolve(".", "public", "assets", "fonts", "Typold", "base64")
if (!existsSync(base64Path)) mkdir(base64Path, (err) => {
console.log("Error on dir creattion", err);
});
for (let file of files) {
if (file.includes(".woff")) {
const fileData = readFileSync(resolve(woffDirPath, file), { encoding: "base64" })
writeFileSync(resolve(base64Path, file.replace(".woff", ".txt")), fileData)
}
}
console.log("done");
为什么它很棒:
快速入门:
代码:
#!/bin/bash
# Initialize a variable to hold all @font-face codes, used when choosing the clipboard
all_font_face_codes=""
FONT_DIR="."
OUTPUT_DIR="./css"
SUPPORTED_EXTENSIONS=("ttf" "woff" "woff2")
usage() {
echo "Usage: $0 -i path/to/fonts -o path/to/output/css"
exit 1
}
# Parse command-line arguments
while getopts "i:o:h" opt; do
case ${opt} in
i)
FONT_DIR="${OPTARG}"
;;
o)
OUTPUT_DIR="${OPTARG}"
;;
h|*)
usage
;;
esac
done
# Prompt user for action: create CSS files or copy to clipboard
options=("Create CSS files" "Copy the @font-face code to the clipboard")
select opt in "${options[@]}"; do
case $opt in
"Create CSS files")
user_choice=1
break
;;
"Copy the @font-face code to the clipboard")
user_choice=2
break
;;
*)
echo "Invalid choice. Please enter 1 or 2."
;;
esac
done
mkdir -p $OUTPUT_DIR
# Function to generate @font-face code
generate_font_face_code() {
local font=$1
local font_name=$(basename "$font" | cut -d. -f1)
local base64_string=$(base64 < "$font")
local font_format
case $font in
*.ttf)
font_format='truetype'
;;
*.woff)
font_format='woff'
;;
*.woff2)
font_format='woff2'
;;
esac
echo "@font-face {
font-family: '$font_name';
/* Remember to add font-weight, font-style, etc. if needed */
src: url(data:application/$font_format;charset=utf-8;base64,$base64_string) format('$font_format');
}"
}
# Function to copy to clipboard
copy_to_clipboard() {
local data=$1
case "$OSTYPE" in
"linux-gnu"*)
echo "$data" | xclip -selection clipboard
;;
"darwin"*)
echo "$data" | pbcopy
;;
*)
echo "Clipboard support is not available on your OS."
exit 1
;;
esac
}
# Loop through each font file in the directory
for ext in "${SUPPORTED_EXTENSIONS[@]}"; do
for font in "$FONT_DIR"/*."$ext"; do
[ -f "$font" ] || continue
font_face_code=$(generate_font_face_code "$font")
case $user_choice in
1)
# User chose to create CSS files
echo "$font_face_code" >> "$OUTPUT_DIR/$(basename "$font" ."$ext").css"
echo "Generated CSS for $(basename "$font" ."$ext")"
;;
2)
# User chose to copy to clipboard
all_font_face_codes+="$font_face_code"$'\n'
echo "Generated CSS source for $(basename "$font" ."$ext")"
;;
esac
done
done
# Output message based on user choice
if [ $user_choice -eq 1 ]; then
echo "All fonts processed."
echo "CSS files generated in $OUTPUT_DIR"
elif [ $user_choice -eq 2 ]; then
copy_to_clipboard "$all_font_face_codes"
echo "All @font-face codes copied to clipboard."
fi
使用示例
$ ./font_encoder.sh # [-h -i -o]
尝试一下,看看你的字体是否平滑地变换,编码愉快! 🚀