forked from akai/readest
This commit is contained in:
@@ -8,7 +8,7 @@ import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useCustomFontStore } from '@/store/customFontStore';
|
||||
import { FILE_SELECTION_PRESETS, useFileSelector } from '@/hooks/useFileSelector';
|
||||
import { mountCustomFont } from '@/styles/fonts';
|
||||
import { parseFontFamily } from '@/utils/font';
|
||||
import { parseFontName } from '@/utils/font';
|
||||
import { getFilename } from '@/utils/path';
|
||||
import { saveViewSettings } from '../../utils/viewSettingsHelper';
|
||||
|
||||
@@ -62,9 +62,11 @@ const CustomFonts: React.FC<CustomFontsProps> = ({ bookKey, onBack }) => {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
const fontFamily = parseFontFamily(await fontFile.arrayBuffer(), fontPath);
|
||||
const fontName = parseFontName(await fontFile.arrayBuffer(), fontPath);
|
||||
const customFont = addFont(fontPath, {
|
||||
name: fontFamily,
|
||||
name: fontName.name,
|
||||
family: fontName.family,
|
||||
style: fontName.style,
|
||||
});
|
||||
if (customFont && !customFont.error) {
|
||||
const loadedFont = await loadFont(envConfig, customFont.id);
|
||||
@@ -148,7 +150,7 @@ const CustomFonts: React.FC<CustomFontsProps> = ({ bookKey, onBack }) => {
|
||||
<div className='flex items-center justify-center'>
|
||||
<MdAdd className='text-primary/85 group-hover:text-primary h-6 w-6' />
|
||||
</div>
|
||||
<div className='text-primary/85 group-hover:text-primary font-medium'>
|
||||
<div className='text-primary/85 group-hover:text-primary line-clamp-1 font-medium'>
|
||||
{_('Import Font')}
|
||||
</div>
|
||||
</div>
|
||||
@@ -165,6 +167,7 @@ const CustomFonts: React.FC<CustomFontsProps> = ({ bookKey, onBack }) => {
|
||||
: 'border-base-200 bg-base-200 cursor-pointer',
|
||||
)}
|
||||
onClick={() => handleSelectFont(font.id)}
|
||||
title={font.name}
|
||||
>
|
||||
<div className='card-body flex items-center justify-center p-2'>
|
||||
<div
|
||||
@@ -174,7 +177,7 @@ const CustomFonts: React.FC<CustomFontsProps> = ({ bookKey, onBack }) => {
|
||||
}}
|
||||
className='text-base-content line-clamp-1'
|
||||
>
|
||||
{font.name}
|
||||
{font.family || font.name}
|
||||
</div>
|
||||
{isDeleteMode && (
|
||||
<button
|
||||
|
||||
@@ -8,7 +8,10 @@ interface FontStoreState {
|
||||
loading: boolean;
|
||||
|
||||
setFonts: (fonts: CustomFont[]) => void;
|
||||
addFont: (path: string, options?: { name?: string }) => CustomFont;
|
||||
addFont: (
|
||||
path: string,
|
||||
options?: { name?: string; family?: string; style?: string },
|
||||
) => CustomFont;
|
||||
removeFont: (id: string) => boolean;
|
||||
updateFont: (id: string, updates: Partial<CustomFont>) => boolean;
|
||||
getFont: (id: string) => CustomFont | undefined;
|
||||
@@ -45,20 +48,18 @@ export const useCustomFontStore = create<FontStoreState>((set, get) => ({
|
||||
const font = createCustomFont(path, options);
|
||||
const existingFont = get().fonts.find((f) => f.id === font.id);
|
||||
if (existingFont) {
|
||||
if (existingFont.deletedAt) {
|
||||
get().updateFont(font.id, {
|
||||
path: font.path,
|
||||
downloadedAt: Date.now(),
|
||||
deletedAt: undefined,
|
||||
loaded: false,
|
||||
blobUrl: undefined,
|
||||
error: undefined,
|
||||
});
|
||||
set((state) => ({
|
||||
fonts: [...state.fonts],
|
||||
}));
|
||||
return existingFont;
|
||||
}
|
||||
get().updateFont(font.id, {
|
||||
...font,
|
||||
path: font.path,
|
||||
downloadedAt: Date.now(),
|
||||
deletedAt: undefined,
|
||||
loaded: false,
|
||||
blobUrl: undefined,
|
||||
error: undefined,
|
||||
});
|
||||
set((state) => ({
|
||||
fonts: [...state.fonts],
|
||||
}));
|
||||
return existingFont;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,8 @@ export const mountAdditionalFonts = (document: Document, isCJK = false) => {
|
||||
export interface CustomFont {
|
||||
id: string;
|
||||
name: string;
|
||||
family?: string;
|
||||
style?: string;
|
||||
path: string;
|
||||
downloadedAt?: number;
|
||||
deletedAt?: number;
|
||||
@@ -189,10 +191,12 @@ export function createCustomFont(
|
||||
path: string,
|
||||
options?: {
|
||||
name?: string;
|
||||
family?: string;
|
||||
style?: string;
|
||||
},
|
||||
): CustomFont {
|
||||
const name = options?.name || getFontName(path);
|
||||
return { id: getFontId(name), name, path };
|
||||
return { id: getFontId(name), name, family: options?.family, style: options?.style, path };
|
||||
}
|
||||
|
||||
export const mountCustomFont = (document: Document, font: CustomFont) => {
|
||||
|
||||
@@ -75,7 +75,14 @@ function getLanguagePriority(platformID: number, languageID: number, userLanguag
|
||||
return priority;
|
||||
}
|
||||
|
||||
export const parseFontFamily = (fontData: ArrayBuffer, filename: string) => {
|
||||
type FontNameType = {
|
||||
name: string;
|
||||
platformID: number;
|
||||
languageID: number;
|
||||
priority: number;
|
||||
};
|
||||
|
||||
export const parseFontName = (fontData: ArrayBuffer, filename: string) => {
|
||||
const fallbackName = filename.replace(/\.[^/.]+$/, '');
|
||||
try {
|
||||
const dataView = new DataView(fontData);
|
||||
@@ -108,12 +115,8 @@ export const parseFontFamily = (fontData: ArrayBuffer, filename: string) => {
|
||||
const stringOffset = dataView.getUint16(nameTableOffset + 4, false);
|
||||
|
||||
const userLanguage = getUserLang();
|
||||
const fontNames: Array<{
|
||||
name: string;
|
||||
platformID: number;
|
||||
languageID: number;
|
||||
priority: number;
|
||||
}> = [];
|
||||
const fontFamilyNames: Array<FontNameType> = [];
|
||||
const fontStyleNames: Array<FontNameType> = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const recordOffset = nameTableOffset + 6 + i * 12;
|
||||
const platformID = dataView.getUint16(recordOffset, false);
|
||||
@@ -122,36 +125,54 @@ export const parseFontFamily = (fontData: ArrayBuffer, filename: string) => {
|
||||
const nameLength = dataView.getUint16(recordOffset + 8, false);
|
||||
const nameOffsetInTable = dataView.getUint16(recordOffset + 10, false);
|
||||
|
||||
if (nameID === 1) {
|
||||
// nameID 1 = Font Family name, nameID 2 = Font Subfamily name (style)
|
||||
if (nameID === 1 || nameID === 2) {
|
||||
const stringStart = nameTableOffset + stringOffset + nameOffsetInTable;
|
||||
let familyName = '';
|
||||
let fontName = '';
|
||||
|
||||
if (platformID === 0 || platformID === 3) {
|
||||
// Unicode/Microsoft platform
|
||||
familyName = parseUnicodeString(dataView, stringStart, nameLength);
|
||||
fontName = parseUnicodeString(dataView, stringStart, nameLength);
|
||||
} else if (platformID === 1) {
|
||||
// Macintosh platform
|
||||
familyName = parseMacintoshString(dataView, stringStart, nameLength);
|
||||
fontName = parseMacintoshString(dataView, stringStart, nameLength);
|
||||
}
|
||||
|
||||
if (familyName && familyName.trim()) {
|
||||
if (fontName && fontName.trim()) {
|
||||
const priority = getLanguagePriority(platformID, languageID, userLanguage);
|
||||
fontNames.push({
|
||||
name: familyName.trim(),
|
||||
const nameEntry = {
|
||||
name: fontName.trim(),
|
||||
platformID,
|
||||
languageID,
|
||||
priority,
|
||||
});
|
||||
};
|
||||
|
||||
if (nameID === 1) {
|
||||
fontFamilyNames.push(nameEntry);
|
||||
} else if (nameID === 2) {
|
||||
fontStyleNames.push(nameEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fontNames.length === 0) {
|
||||
if (fontFamilyNames.length === 0) {
|
||||
throw new Error('Font family name not found');
|
||||
}
|
||||
fontNames.sort((a, b) => b.priority - a.priority);
|
||||
return fontNames[0]!.name;
|
||||
fontFamilyNames.sort((a, b) => b.priority - a.priority);
|
||||
fontStyleNames.sort((a, b) => b.priority - a.priority);
|
||||
const familyName = fontFamilyNames[0]!.name;
|
||||
const styleName = fontStyleNames.length > 0 ? fontStyleNames[0]!.name : '';
|
||||
return {
|
||||
name: styleName ? `${familyName} ${styleName}` : familyName,
|
||||
family: familyName,
|
||||
style: styleName,
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn(`Failed to parse font: ${error}`);
|
||||
return fallbackName;
|
||||
return {
|
||||
name: fallbackName,
|
||||
family: fallbackName,
|
||||
style: '',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user