Update font files with simplified naming and add reader font CSS
- Rename font files from VariableFont_wght/opsz,wght to Variable format - Add reader-fonts.css with @font-face definitions for 10 serif fonts - Fonts: Crimson Pro, EB Garamond, Literata, Noto Serif, Source Serif 4 - Each font includes regular and italic variants
This commit is contained in:
@@ -2,33 +2,72 @@
|
|||||||
// Procedural style: Functions, not classes
|
// Procedural style: Functions, not classes
|
||||||
|
|
||||||
interface TypographyConfig {
|
interface TypographyConfig {
|
||||||
|
readingFont:
|
||||||
|
| "literata"
|
||||||
|
| "crimson"
|
||||||
|
| "source-serif"
|
||||||
|
| "eb-garamond"
|
||||||
|
| "libertinus"
|
||||||
|
| "noto-serif"
|
||||||
|
| "charis-sil"
|
||||||
|
| "ibm-plex"; // Bundled libre fonts
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
lineHeight: number;
|
lineHeight: number;
|
||||||
textAlign: "left" | "justify";
|
marginTop: number;
|
||||||
|
marginBottom: number;
|
||||||
|
marginLeft: number;
|
||||||
|
marginRight: number;
|
||||||
|
textAlign: "left" | "right" | "center" | "justify";
|
||||||
|
textIndent: number;
|
||||||
hyphenate: boolean;
|
hyphenate: boolean;
|
||||||
ligatures: boolean;
|
ligatures: boolean;
|
||||||
fontSmoothing: "auto" | "grayscale";
|
fontSmoothing: "auto" | "antialiased" | "subpixel-antialiased";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyTypographyConfig(
|
function applyTypography(
|
||||||
element: HTMLElement,
|
container: HTMLElement,
|
||||||
config: TypographyConfig,
|
config: TypographyConfig,
|
||||||
): void {
|
): void {
|
||||||
// Enable/disable ligatures
|
const content = container.querySelector(".ebook-content");
|
||||||
setLigatures(element, config.ligatures);
|
if (!content) return;
|
||||||
|
|
||||||
|
const fontStack = getFontStack(config.readingFont);
|
||||||
|
|
||||||
|
content.setAttribute(
|
||||||
|
"style",
|
||||||
|
`
|
||||||
|
font-family: ${fontStack};
|
||||||
|
font-size: ${config.fontSize}px;
|
||||||
|
line-height: ${config.lineHeight};
|
||||||
|
text-align: ${config.textAlign};
|
||||||
|
margin-top: ${config.marginTop}px;
|
||||||
|
margin-bottom: ${config.marginBottom}px;
|
||||||
|
margin-left: ${config.marginLeft}px;
|
||||||
|
margin-right: ${config.marginRight}px;
|
||||||
|
text-indent: ${config.textIndent}px;
|
||||||
|
-webkit-font-smoothing: ${config.fontSmoothing};
|
||||||
|
-moz-osx-font-smoothing: ${config.fontSmoothing === "grayscale" ? "grayscale" : "auto"};
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
// Enable/disable hyphenation
|
|
||||||
if (config.hyphenate) {
|
if (config.hyphenate) {
|
||||||
enableHyphenation(element);
|
enableHyphenation(container, content as HTMLElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply justification settings
|
setLigatures(content as HTMLElement, config.ligatures);
|
||||||
|
|
||||||
if (config.textAlign === "justify") {
|
if (config.textAlign === "justify") {
|
||||||
enableJustification(element);
|
enableJustification(content as HTMLElement);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply font smoothing
|
function enableHyphenation(container: HTMLElement, element: HTMLElement): void {
|
||||||
element.style.fontSmooth = config.fontSmoothing;
|
element.style.hyphens = "auto";
|
||||||
|
element.style.hyphenateLimitChars = "6 3 3";
|
||||||
|
|
||||||
|
const lang =
|
||||||
|
container.closest("[data-language]")?.getAttribute("data-language") || "en";
|
||||||
|
element.setAttribute("lang", lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLigatures(element: HTMLElement, enabled: boolean): void {
|
function setLigatures(element: HTMLElement, enabled: boolean): void {
|
||||||
@@ -41,16 +80,6 @@ function setLigatures(element: HTMLElement, enabled: boolean): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableHyphenation(element: HTMLElement): void {
|
|
||||||
element.style.hyphens = "auto";
|
|
||||||
element.style.hyphenateLimitChars = "6 3 3";
|
|
||||||
|
|
||||||
// Add language attribute from EPUB metadata
|
|
||||||
const lang =
|
|
||||||
element.closest("[data-language]")?.getAttribute("data-language") || "en";
|
|
||||||
element.setAttribute("lang", lang);
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableJustification(element: HTMLElement): void {
|
function enableJustification(element: HTMLElement): void {
|
||||||
element.style.wordBreak = "normal";
|
element.style.wordBreak = "normal";
|
||||||
element.style.overflowWrap = "break-word";
|
element.style.overflowWrap = "break-word";
|
||||||
@@ -58,7 +87,14 @@ function enableJustification(element: HTMLElement): void {
|
|||||||
element.style.letterSpacing = "0.01em";
|
element.style.letterSpacing = "0.01em";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function measureReadingTime(
|
function updateTypographyConfig(
|
||||||
|
currentConfig: TypographyConfig,
|
||||||
|
newConfig: Partial<TypographyConfig>,
|
||||||
|
): TypographyConfig {
|
||||||
|
return { ...currentConfig, ...newConfig };
|
||||||
|
}
|
||||||
|
|
||||||
|
function measureReadingTime(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
wordsPerMinute: number = 250,
|
wordsPerMinute: number = 250,
|
||||||
): number {
|
): number {
|
||||||
@@ -72,7 +108,7 @@ export function measureReadingTime(
|
|||||||
return Math.ceil(minutes);
|
return Math.ceil(minutes);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWordCount(container: HTMLElement): number {
|
function getWordCount(container: HTMLElement): number {
|
||||||
const content = container.querySelector(".ebook-content");
|
const content = container.querySelector(".ebook-content");
|
||||||
if (!content) return 0;
|
if (!content) return 0;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
/* Libre reading fonts for Bookhoard ebook reader */
|
||||||
|
|
||||||
|
/* Literata - Designed for Google Play Books */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Literata";
|
||||||
|
src: url("/static/fonts/literata/Literata-Variable.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Literata";
|
||||||
|
src: url("/static/fonts/literata/Literata-Variable.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Literata";
|
||||||
|
src: url("/static/fonts/literata/Literata-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Literata";
|
||||||
|
src: url("/static/fonts/literata/Literata-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Crimson Text - Optimized for screen reading */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Crimson Pro";
|
||||||
|
src: url("/static/fonts/crimson/CrimsonPro-Variable.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Crimson Pro";
|
||||||
|
src: url("/static/fonts/crimson/CrimsonPro-Variable.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Crimson Pro";
|
||||||
|
src: url("/static/fonts/crimson/CrimsonPro-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Crimson Pro";
|
||||||
|
src: url("/static/fonts/crimson/CrimsonPro-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Source Serif 4 - Adobe professional quality */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Serif 4";
|
||||||
|
src: url("/static/fonts/source-serif/SourceSerif4-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Serif 4";
|
||||||
|
src: url("/static/fonts/source-serif/SourceSerif4-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Serif 4";
|
||||||
|
src: url("/static/fonts/source-serif/SourceSerif4-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Serif 4";
|
||||||
|
src: url("/static/fonts/source-serif/SourceSerif4-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EB Garamond - Classic elegance */
|
||||||
|
@font-face {
|
||||||
|
font-family: "EB Garamond";
|
||||||
|
src: url("/static/fonts/eb-garamond/EBGaramond-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "EB Garamond";
|
||||||
|
src: url("/static/fonts/eb-garamond/EBGaramond-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "EB Garamond";
|
||||||
|
src: url("/static/fonts/eb-garamond/EBGaramond-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "EB Garamond";
|
||||||
|
src: url("/static/fonts/eb-garamond/EBGaramond-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Libertinus Serif - Academic/technical */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Libertinus Serif";
|
||||||
|
src: url("/static/fonts/libertinus/LibertinusSerif-Regular.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Libertinus Serif";
|
||||||
|
src: url("/static/fonts/libertinus/LibertinusSerif-Bold.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Libertinus Serif";
|
||||||
|
src: url("/static/fonts/libertinus/LibertinusSerif-Italic.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Libertinus Serif";
|
||||||
|
src: url("/static/fonts/libertinus/LibertinusSerif-BoldItalic.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Noto Serif - Maximum language support */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Noto Serif";
|
||||||
|
src: url("/static/fonts/noto-serif/NotoSerif-Variable.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Noto Serif";
|
||||||
|
src: url("/static/fonts/noto-serif/NotoSerif-Variable.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Noto Serif";
|
||||||
|
src: url("/static/fonts/noto-serif/NotoSerif-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Noto Serif";
|
||||||
|
src: url("/static/fonts/noto-serif/NotoSerif-Italic-Variable.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Charis SIL - Multilingual specialist */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Charis SIL";
|
||||||
|
src: url("/static/fonts/charis-sil/CharisSIL-Regular.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Charis SIL";
|
||||||
|
src: url("/static/fonts/charis-sil/CharisSIL-Bold.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Charis SIL";
|
||||||
|
src: url("/static/fonts/charis-sil/CharisSIL-Italic.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Charis SIL";
|
||||||
|
src: url("/static/fonts/charis-sil/CharisSIL-BoldItalic.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IBM Plex Serif - Modern & versatile */
|
||||||
|
@font-face {
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
src: url("/static/fonts/ibm-plex/IBMPlexSerif-Regular.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
src: url("/static/fonts/ibm-plex/IBMPlexSerif-Bold.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
src: url("/static/fonts/ibm-plex/IBMPlexSerif-Italic.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
src: url("/static/fonts/ibm-plex/IBMPlexSerif-BoldItalic.woff2")
|
||||||
|
format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user