mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-18 17:48:52 +08:00
180 lines
4.7 KiB
TypeScript
180 lines
4.7 KiB
TypeScript
import { logger } from '@/core/common/logger';
|
|
|
|
export interface IFontConfig {
|
|
/** 字体类型 */
|
|
readonly family: string;
|
|
/** 字体大小的值 */
|
|
readonly size: number;
|
|
/** 字体大小单位,推荐使用 px */
|
|
readonly sizeUnit: string;
|
|
/** 字体粗细,范围 0-1000 */
|
|
readonly weight: number;
|
|
/** 是否斜体 */
|
|
readonly italic: boolean;
|
|
}
|
|
|
|
export const enum FontWeight {
|
|
Light = 300,
|
|
Normal = 400,
|
|
Bold = 700
|
|
}
|
|
|
|
type _FontStretch =
|
|
| 'ultra-condensed'
|
|
| 'extra-condensed'
|
|
| 'condensed'
|
|
| 'semi-condensed'
|
|
| 'normal'
|
|
| 'semi-expanded'
|
|
| 'expanded'
|
|
| 'extra-expanded'
|
|
| 'ultra-expanded';
|
|
|
|
type _FontVariant = 'normal' | 'small-caps';
|
|
|
|
export class Font implements IFontConfig {
|
|
static defaultFamily: string = 'Verdana';
|
|
static defaultSize: number = 16;
|
|
static defaultSizeUnit: string = 'px';
|
|
static defaultWeight: number = 400;
|
|
static defaultItalic: boolean = false;
|
|
|
|
private readonly fallbacks: Font[] = [];
|
|
|
|
private fontString: string = '';
|
|
|
|
constructor(
|
|
public readonly family: string = Font.defaultFamily,
|
|
public readonly size: number = Font.defaultSize,
|
|
public readonly sizeUnit: string = Font.defaultSizeUnit,
|
|
public readonly weight: number = Font.defaultWeight,
|
|
public readonly italic: boolean = Font.defaultItalic
|
|
) {
|
|
this.fontString = this.getFont();
|
|
}
|
|
|
|
/**
|
|
* 添加补充字体,若当前字体不可用,那么会使用补充字体,补充字体也可以添加补充字体,但是请避免递归添加
|
|
* @param fallback 补充字体
|
|
*/
|
|
addFallback(...fallback: Font[]) {
|
|
this.fallbacks.push(...fallback);
|
|
this.fontString = this.getFont();
|
|
}
|
|
|
|
private build() {
|
|
return `${
|
|
this.italic ? 'italic ' : ''
|
|
} ${this.weight} ${this.size}${this.sizeUnit} ${this.family}`;
|
|
}
|
|
|
|
private getFallbackFont(used: Set<Font>) {
|
|
let font = '';
|
|
this.fallbacks.forEach(v => {
|
|
if (used.has(v)) {
|
|
logger.warn(62, this.build());
|
|
return;
|
|
}
|
|
used.add(v);
|
|
font += `, ${v.getFallbackFont(used)}`;
|
|
});
|
|
return font;
|
|
}
|
|
|
|
private getFont() {
|
|
if (this.fallbacks.length === 0) {
|
|
return this.build();
|
|
} else {
|
|
const usedFont = new Set<Font>();
|
|
return this.build() + this.getFallbackFont(usedFont);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取字体的 CSS 字符串
|
|
*/
|
|
string() {
|
|
return this.fontString;
|
|
}
|
|
|
|
private static parseOne(str: string) {
|
|
if (!str) return new Font();
|
|
let italic = this.defaultItalic;
|
|
let weight = this.defaultWeight;
|
|
let size = this.defaultSize;
|
|
let unit = this.defaultSizeUnit;
|
|
let family = this.defaultFamily;
|
|
const tokens = str.split(/\s+/);
|
|
tokens.forEach(v => {
|
|
// font-italic
|
|
if (v === 'italic') {
|
|
italic = true;
|
|
return;
|
|
}
|
|
|
|
// font-weight
|
|
const num = Number(v);
|
|
if (!isNaN(num)) {
|
|
weight = num;
|
|
return;
|
|
}
|
|
|
|
// font-size
|
|
const parse = parseFloat(v);
|
|
if (!isNaN(parse)) {
|
|
size = parse;
|
|
unit = v.slice(parse.toString().length);
|
|
return;
|
|
}
|
|
});
|
|
family = tokens.at(-1) ?? 'Verdana';
|
|
return new Font(family, size, unit, weight, italic);
|
|
}
|
|
|
|
/**
|
|
* 从 CSS 字体字符串解析出 Font 实例,不支持的属性将被忽略
|
|
*/
|
|
static parse(str: string) {
|
|
const fonts = str.split(',');
|
|
const main = this.parseOne(fonts[0]);
|
|
for (let i = 1; i < fonts.length; i++) {
|
|
main.addFallback(this.parseOne(fonts[i]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置默认字体
|
|
*/
|
|
static setDefaults(font: Font) {
|
|
this.defaultFamily = font.family;
|
|
this.defaultItalic = font.italic;
|
|
this.defaultSize = font.size;
|
|
this.defaultSizeUnit = font.sizeUnit;
|
|
this.defaultWeight = font.weight;
|
|
}
|
|
|
|
/**
|
|
* 获取默认字体
|
|
*/
|
|
static defaults() {
|
|
return new Font();
|
|
}
|
|
|
|
/**
|
|
* 复制一个字体,同时修改字体的一部分属性
|
|
* @param font 要复制的字体
|
|
*/
|
|
static clone(
|
|
font: Font,
|
|
{
|
|
family = font.family,
|
|
size = font.size,
|
|
sizeUnit = font.sizeUnit,
|
|
weight = font.weight,
|
|
italic = font.italic
|
|
}: Partial<IFontConfig>
|
|
) {
|
|
return new Font(family, size, sizeUnit, weight, italic);
|
|
}
|
|
}
|