refactor: separate elements logic into a standalone package (#9285)

This commit is contained in:
Marcel Mraz
2025-03-26 15:24:59 +01:00
committed by GitHub
parent a18f059188
commit 432a46ef9e
372 changed files with 3463 additions and 2463 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
import { LOCAL_FONT_PROTOCOL } from "../FontMetadata";
import { LOCAL_FONT_PROTOCOL } from "@excalidraw/common";
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
export const EmojiFontFaces: ExcalidrawFontFaceDescriptor[] = [
@@ -1,7 +1,6 @@
import { subsetWoff2GlyphsByCodepoints } from "../subset/subset-main";
import { promiseTry } from "../utils";
import { promiseTry, LOCAL_FONT_PROTOCOL } from "@excalidraw/common";
import { LOCAL_FONT_PROTOCOL } from "./FontMetadata";
import { subsetWoff2GlyphsByCodepoints } from "../subset/subset-main";
type DataURL = string;
-151
View File
@@ -1,151 +0,0 @@
import {
FreedrawIcon,
FontFamilyNormalIcon,
FontFamilyHeadingIcon,
FontFamilyCodeIcon,
} from "../components/icons";
import { FONT_FAMILY, FONT_FAMILY_FALLBACKS } from "../constants";
import type { JSX } from "react";
/**
* Encapsulates font metrics with additional font metadata.
* */
export interface FontMetadata {
/** for head & hhea metrics read the woff2 with https://fontdrop.info/ */
metrics: {
/** head.unitsPerEm metric */
unitsPerEm: 1000 | 1024 | 2048;
/** hhea.ascender metric */
ascender: number;
/** hhea.descender metric */
descender: number;
/** harcoded unitless line-height, https://github.com/excalidraw/excalidraw/pull/6360#issuecomment-1477635971 */
lineHeight: number;
};
/** element to be displayed as an icon */
icon?: JSX.Element;
/** flag to indicate a deprecated font */
deprecated?: true;
/** flag to indicate a server-side only font */
serverSide?: true;
/** flag to indiccate a local-only font */
local?: true;
/** flag to indicate a fallback font */
fallback?: true;
}
export const FONT_METADATA: Record<number, FontMetadata> = {
[FONT_FAMILY.Excalifont]: {
metrics: {
unitsPerEm: 1000,
ascender: 886,
descender: -374,
lineHeight: 1.25,
},
icon: FreedrawIcon,
},
[FONT_FAMILY.Nunito]: {
metrics: {
unitsPerEm: 1000,
ascender: 1011,
descender: -353,
lineHeight: 1.35,
},
icon: FontFamilyNormalIcon,
},
[FONT_FAMILY["Lilita One"]]: {
metrics: {
unitsPerEm: 1000,
ascender: 923,
descender: -220,
lineHeight: 1.15,
},
icon: FontFamilyHeadingIcon,
},
[FONT_FAMILY["Comic Shanns"]]: {
metrics: {
unitsPerEm: 1000,
ascender: 750,
descender: -250,
lineHeight: 1.25,
},
icon: FontFamilyCodeIcon,
},
[FONT_FAMILY.Virgil]: {
metrics: {
unitsPerEm: 1000,
ascender: 886,
descender: -374,
lineHeight: 1.25,
},
icon: FreedrawIcon,
deprecated: true,
},
[FONT_FAMILY.Helvetica]: {
metrics: {
unitsPerEm: 2048,
ascender: 1577,
descender: -471,
lineHeight: 1.15,
},
icon: FontFamilyNormalIcon,
deprecated: true,
local: true,
},
[FONT_FAMILY.Cascadia]: {
metrics: {
unitsPerEm: 2048,
ascender: 1900,
descender: -480,
lineHeight: 1.2,
},
icon: FontFamilyCodeIcon,
deprecated: true,
},
[FONT_FAMILY["Liberation Sans"]]: {
metrics: {
unitsPerEm: 2048,
ascender: 1854,
descender: -434,
lineHeight: 1.15,
},
serverSide: true,
},
[FONT_FAMILY_FALLBACKS.Xiaolai]: {
metrics: {
unitsPerEm: 1000,
ascender: 880,
descender: -144,
lineHeight: 1.15,
},
fallback: true,
},
[FONT_FAMILY_FALLBACKS["Segoe UI Emoji"]]: {
metrics: {
// reusing Excalifont metrics
unitsPerEm: 1000,
ascender: 886,
descender: -374,
lineHeight: 1.25,
},
local: true,
fallback: true,
},
};
/** Unicode ranges defined by google fonts */
export const GOOGLE_FONTS_RANGES = {
LATIN:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD",
LATIN_EXT:
"U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF",
CYRILIC_EXT:
"U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F",
CYRILIC: "U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116",
VIETNAMESE:
"U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB",
};
/** local protocol to skip the local font from registering or inlining */
export const LOCAL_FONT_PROTOCOL = "local:";
+23 -45
View File
@@ -4,20 +4,35 @@ import {
CJK_HAND_DRAWN_FALLBACK_FONT,
WINDOWS_EMOJI_FALLBACK_FONT,
getFontFamilyFallbacks,
} from "../constants";
import { isTextElement } from "../element";
import { getContainerElement } from "../element/textElement";
import { charWidth } from "../element/textMeasurements";
import { containsCJK } from "../element/textWrapping";
import { ShapeCache } from "../scene/ShapeCache";
import { getFontString, PromisePool, promiseTry } from "../utils";
} from "@excalidraw/common";
import { getContainerElement } from "@excalidraw/element/textElement";
import { charWidth } from "@excalidraw/element/textMeasurements";
import { containsCJK } from "@excalidraw/element/textWrapping";
import {
FONT_METADATA,
type FontMetadata,
getFontString,
PromisePool,
promiseTry,
} from "@excalidraw/common";
import { ShapeCache } from "@excalidraw/element/ShapeCache";
import { isTextElement } from "@excalidraw/element/typeChecks";
import type {
ExcalidrawElement,
ExcalidrawTextElement,
} from "@excalidraw/element/types";
import type { ValueOf } from "@excalidraw/common/utility-types";
import { CascadiaFontFaces } from "./Cascadia";
import { ComicShannsFontFaces } from "./ComicShanns";
import { EmojiFontFaces } from "./Emoji";
import { ExcalidrawFontFace } from "./ExcalidrawFontFace";
import { ExcalifontFontFaces } from "./Excalifont";
import { FONT_METADATA, type FontMetadata } from "./FontMetadata";
import { HelveticaFontFaces } from "./Helvetica";
import { LiberationFontFaces } from "./Liberation";
import { LilitaFontFaces } from "./Lilita";
@@ -25,13 +40,7 @@ import { NunitoFontFaces } from "./Nunito";
import { VirgilFontFaces } from "./Virgil";
import { XiaolaiFontFaces } from "./Xiaolai";
import type {
ExcalidrawElement,
ExcalidrawTextElement,
FontFamilyValues,
} from "../element/types";
import type Scene from "../scene/Scene";
import type { ValueOf } from "../utility-types";
export class Fonts {
// it's ok to track fonts across multiple instances only once, so let's use
@@ -454,37 +463,6 @@ export class Fonts {
}
}
/**
* Calculates vertical offset for a text with alphabetic baseline.
*/
export const getVerticalOffset = (
fontFamily: ExcalidrawTextElement["fontFamily"],
fontSize: ExcalidrawTextElement["fontSize"],
lineHeightPx: number,
) => {
const { unitsPerEm, ascender, descender } =
Fonts.registered.get(fontFamily)?.metadata.metrics ||
FONT_METADATA[FONT_FAMILY.Virgil].metrics;
const fontSizeEm = fontSize / unitsPerEm;
const lineGap =
(lineHeightPx - fontSizeEm * ascender + fontSizeEm * descender) / 2;
const verticalOffset = fontSizeEm * ascender + lineGap;
return verticalOffset;
};
/**
* Gets line height forr a selected family.
*/
export const getLineHeight = (fontFamily: FontFamilyValues) => {
const { lineHeight } =
Fonts.registered.get(fontFamily)?.metadata.metrics ||
FONT_METADATA[FONT_FAMILY.Excalifont].metrics;
return lineHeight as ExcalidrawTextElement["lineHeight"];
};
export interface ExcalidrawFontFaceDescriptor {
uri: string;
descriptors?: FontFaceDescriptors;
+2 -1
View File
@@ -1,4 +1,5 @@
import { LOCAL_FONT_PROTOCOL } from "../FontMetadata";
import { LOCAL_FONT_PROTOCOL } from "@excalidraw/common";
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
export const HelveticaFontFaces: ExcalidrawFontFaceDescriptor[] = [
+2 -1
View File
@@ -1,4 +1,5 @@
import { GOOGLE_FONTS_RANGES } from "../FontMetadata";
import { GOOGLE_FONTS_RANGES } from "@excalidraw/common";
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
import LilitaLatinExt from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2";
+2 -1
View File
@@ -1,4 +1,5 @@
import { GOOGLE_FONTS_RANGES } from "../FontMetadata";
import { GOOGLE_FONTS_RANGES } from "@excalidraw/common";
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
import Cyrilic from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2";