snapshot of https://github.com/excalidraw/excalidraw.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Tests / test (push) Failing after 15s
Some checks failed
Tests / test (push) Failing after 15s
This commit is contained in:
BIN
packages/excalidraw/fonts/Assistant/Assistant-Bold.woff2
Normal file
BIN
packages/excalidraw/fonts/Assistant/Assistant-Bold.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Assistant/Assistant-Medium.woff2
Normal file
BIN
packages/excalidraw/fonts/Assistant/Assistant-Medium.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Assistant/Assistant-Regular.woff2
Normal file
BIN
packages/excalidraw/fonts/Assistant/Assistant-Regular.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Assistant/Assistant-SemiBold.woff2
Normal file
BIN
packages/excalidraw/fonts/Assistant/Assistant-SemiBold.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Cascadia/CascadiaCode-Regular.woff2
Normal file
BIN
packages/excalidraw/fonts/Cascadia/CascadiaCode-Regular.woff2
Normal file
Binary file not shown.
9
packages/excalidraw/fonts/Cascadia/index.ts
Normal file
9
packages/excalidraw/fonts/Cascadia/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
import CascadiaCodeRegular from "./CascadiaCode-Regular.woff2";
|
||||
|
||||
export const CascadiaFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: CascadiaCodeRegular,
|
||||
},
|
||||
];
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12221
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular.sfd
Normal file
12221
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular.sfd
Normal file
File diff suppressed because it is too large
Load Diff
96
packages/excalidraw/fonts/ComicShanns/index.ts
Normal file
96
packages/excalidraw/fonts/ComicShanns/index.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
// The following file content was generated with https://chinese-font.netlify.app/online-split,
|
||||
// but has been manully rewritten from `@font-face` rules into TS while leveraging FontFace API.
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
import _0 from "./ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2";
|
||||
import _3 from "./ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2";
|
||||
import _2 from "./ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2";
|
||||
import _1 from "./ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2";
|
||||
|
||||
/* Generated By cn-font-split@5.2.2 https://www.npmjs.com/package/cn-font-split
|
||||
CreateTime: Thu, 17 Oct 2024 09:57:51 GMT;
|
||||
Origin File Name Table:
|
||||
copyright: MIT License
|
||||
|
||||
Copyright (c) 2018 Shannon Miwa
|
||||
Copyright (c) 2023 Jesus Gonzalez
|
||||
Copyright (c) 2023 Rodrigo Batista de Moraes
|
||||
Copyright (c) 2024 Fini Jastrow
|
||||
Copyright (c) 2024 Kyle Beechly
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
fontFamily: Comic Shanns Mono-Regular
|
||||
fontSubfamily: Regular
|
||||
uniqueID: FontForge 2.0 : Comic Shanns Mono Regular : 17-10-2024
|
||||
fullName: Comic Shanns Mono Regular
|
||||
version: 1.3.0
|
||||
postScriptName: ComicShannsMono-Regular
|
||||
license: MIT License
|
||||
|
||||
Copyright (c) 2018 Shannon Miwa
|
||||
Copyright (c) 2023 Jesus Gonzalez
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
preferredFamily: Comic Shanns Mono
|
||||
*/
|
||||
|
||||
export const ComicShannsFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: _0,
|
||||
descriptors: {
|
||||
unicodeRange:
|
||||
"U+20-7e,U+a1-a6,U+a8,U+ab-ac,U+af-b1,U+b4,U+b8,U+bb-bc,U+bf-cf,U+d1-d7,U+d9-de,U+e0-ef,U+f1-f7,U+f9-ff,U+131,U+152-153,U+2c6,U+2da,U+2dc,U+2013-2014,U+2018-201a,U+201c-201d,U+2020-2022,U+2026,U+2039-203a,U+2044,U+20ac,U+2191,U+2193,U+2212",
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: _1,
|
||||
descriptors: {
|
||||
unicodeRange:
|
||||
"U+100-10f,U+112-125,U+128-130,U+134-137,U+139-13c,U+141-148,U+14c-151,U+154-161,U+164-165,U+168-17f,U+1bf,U+1f7,U+218-21b,U+237,U+1e80-1e85,U+1ef2-1ef3,U+a75b",
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: _2,
|
||||
descriptors: {
|
||||
unicodeRange:
|
||||
"U+2c7,U+2d8-2d9,U+2db,U+2dd,U+315,U+2190,U+2192,U+2200,U+2203-2204,U+2264-2265,U+f6c3",
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: _3,
|
||||
descriptors: { unicodeRange: "U+3bb" },
|
||||
},
|
||||
];
|
||||
9
packages/excalidraw/fonts/Emoji/index.ts
Normal file
9
packages/excalidraw/fonts/Emoji/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { LOCAL_FONT_PROTOCOL } from "@excalidraw/common";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const EmojiFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: LOCAL_FONT_PROTOCOL,
|
||||
},
|
||||
];
|
||||
209
packages/excalidraw/fonts/ExcalidrawFontFace.ts
Normal file
209
packages/excalidraw/fonts/ExcalidrawFontFace.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { promiseTry, LOCAL_FONT_PROTOCOL } from "@excalidraw/common";
|
||||
|
||||
import { subsetWoff2GlyphsByCodepoints } from "../subset/subset-main";
|
||||
|
||||
type DataURL = string;
|
||||
|
||||
export class ExcalidrawFontFace {
|
||||
public readonly urls: URL[] | DataURL[];
|
||||
public readonly fontFace: FontFace;
|
||||
|
||||
private static readonly ASSETS_FALLBACK_URL = `https://esm.sh/${
|
||||
import.meta.env.PKG_NAME
|
||||
? `${import.meta.env.PKG_NAME}@${import.meta.env.PKG_VERSION}` // is provided during package build
|
||||
: "@excalidraw/excalidraw" // fallback to the latest package version (i.e. for app)
|
||||
}/dist/prod/`;
|
||||
|
||||
constructor(family: string, uri: string, descriptors?: FontFaceDescriptors) {
|
||||
this.urls = ExcalidrawFontFace.createUrls(uri);
|
||||
|
||||
const sources = this.urls
|
||||
.map((url) => `url(${url}) ${ExcalidrawFontFace.getFormat(url)}`)
|
||||
.join(", ");
|
||||
|
||||
this.fontFace = new FontFace(family, sources, {
|
||||
display: "swap",
|
||||
style: "normal",
|
||||
weight: "400",
|
||||
...descriptors,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates CSS `@font-face` definition with the (subsetted) font source as a data url for the characters within the unicode range.
|
||||
*
|
||||
* Retrieves `undefined` otherwise.
|
||||
*/
|
||||
public toCSS(characters: string): Promise<string> | undefined {
|
||||
// quick exit in case the characters are not within this font face's unicode range
|
||||
if (!this.getUnicodeRangeRegex().test(characters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const codepoints = Array.from(characters).map(
|
||||
(char) => char.codePointAt(0)!,
|
||||
);
|
||||
|
||||
return this.getContent(codepoints).then(
|
||||
(content) =>
|
||||
`@font-face { font-family: ${this.fontFace.family}; src: url(${content}); }`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to fetch woff2 content, based on the registered urls (from first to last, treated as fallbacks).
|
||||
*
|
||||
* @returns base64 with subsetted glyphs based on the passed codepoint, last defined url otherwise
|
||||
*/
|
||||
public async getContent(codePoints: Array<number>): Promise<string> {
|
||||
let i = 0;
|
||||
const errorMessages = [];
|
||||
|
||||
while (i < this.urls.length) {
|
||||
const url = this.urls[i];
|
||||
|
||||
try {
|
||||
const arrayBuffer = await this.fetchFont(url);
|
||||
const base64 = await subsetWoff2GlyphsByCodepoints(
|
||||
arrayBuffer,
|
||||
codePoints,
|
||||
);
|
||||
|
||||
return base64;
|
||||
} catch (e) {
|
||||
errorMessages.push(`"${url.toString()}" returned error "${e}"`);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
console.error(
|
||||
`Failed to fetch font family "${this.fontFace.family}"`,
|
||||
JSON.stringify(errorMessages, undefined, 2),
|
||||
);
|
||||
|
||||
// in case of issues, at least return the last url as a content
|
||||
// defaults to unpkg for bundled fonts (so that we don't have to host them forever) and http url for others
|
||||
return this.urls.length ? this.urls[this.urls.length - 1].toString() : "";
|
||||
}
|
||||
|
||||
public fetchFont(url: URL | DataURL): Promise<ArrayBuffer> {
|
||||
return promiseTry(async () => {
|
||||
const response = await fetch(url, {
|
||||
// always prefer cache (even stale), otherwise it always triggers an unnecessary validation request
|
||||
// which we don't need as we are controlling freshness of the fonts with the stable hash suffix in the url
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Request/cache
|
||||
cache: "force-cache",
|
||||
headers: {
|
||||
Accept: "font/woff2",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const urlString = url instanceof URL ? url.toString() : "dataurl";
|
||||
throw new Error(
|
||||
`Failed to fetch "${urlString}": ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return arrayBuffer;
|
||||
});
|
||||
}
|
||||
|
||||
private getUnicodeRangeRegex() {
|
||||
// using \u{h} or \u{hhhhh} to match any number of hex digits,
|
||||
// otherwise we would get an "Invalid Unicode escape" error
|
||||
// e.g. U+0-1007F -> \u{0}-\u{1007F}
|
||||
const unicodeRangeRegex = this.fontFace.unicodeRange
|
||||
.split(/,\s*/)
|
||||
.map((range) => {
|
||||
const [start, end] = range.replace("U+", "").split("-");
|
||||
if (end) {
|
||||
return `\\u{${start}}-\\u{${end}}`;
|
||||
}
|
||||
|
||||
return `\\u{${start}}`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
return new RegExp(`[${unicodeRangeRegex}]`, "u");
|
||||
}
|
||||
|
||||
private static createUrls(uri: string): URL[] | DataURL[] {
|
||||
if (uri.startsWith("data")) {
|
||||
// don't create the URL instance, as parsing the huge dataurl string is expensive
|
||||
return [uri];
|
||||
}
|
||||
|
||||
if (uri.startsWith(LOCAL_FONT_PROTOCOL)) {
|
||||
// no url for local fonts
|
||||
return [];
|
||||
}
|
||||
|
||||
if (uri.startsWith("http")) {
|
||||
// one url for http imports or data url
|
||||
return [new URL(uri)];
|
||||
}
|
||||
|
||||
// absolute assets paths, which are found in tests and excalidraw-app build, won't work with base url, so we are stripping initial slash away
|
||||
const assetUrl: string = uri.replace(/^\/+/, "");
|
||||
const urls: URL[] = [];
|
||||
|
||||
if (typeof window.EXCALIDRAW_ASSET_PATH === "string") {
|
||||
const normalizedBaseUrl = this.normalizeBaseUrl(
|
||||
window.EXCALIDRAW_ASSET_PATH,
|
||||
);
|
||||
|
||||
urls.push(new URL(assetUrl, normalizedBaseUrl));
|
||||
} else if (Array.isArray(window.EXCALIDRAW_ASSET_PATH)) {
|
||||
window.EXCALIDRAW_ASSET_PATH.forEach((path) => {
|
||||
const normalizedBaseUrl = this.normalizeBaseUrl(path);
|
||||
urls.push(new URL(assetUrl, normalizedBaseUrl));
|
||||
});
|
||||
}
|
||||
|
||||
// fallback url for bundled fonts
|
||||
urls.push(new URL(assetUrl, ExcalidrawFontFace.ASSETS_FALLBACK_URL));
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
private static getFormat(url: URL | DataURL) {
|
||||
if (!(url instanceof URL)) {
|
||||
// format is irrelevant for data url
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
const parts = new URL(url).pathname.split(".");
|
||||
|
||||
if (parts.length === 1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `format('${parts.pop()}')`;
|
||||
} catch (error) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static normalizeBaseUrl(baseUrl: string) {
|
||||
let result = baseUrl;
|
||||
|
||||
// in case user passed a root-relative url (~absolute path),
|
||||
// like "/" or "/some/path", or relative (starts with "./"),
|
||||
// prepend it with `location.origin`
|
||||
if (/^\.?\//.test(result)) {
|
||||
result = new URL(
|
||||
result.replace(/^\.?\/+/, ""),
|
||||
window?.location?.origin,
|
||||
).toString();
|
||||
}
|
||||
|
||||
// ensure there is a trailing slash, otherwise url won't be correctly concatenated
|
||||
result = `${result.replace(/\/+$/, "")}/`;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
160
packages/excalidraw/fonts/Excalifont/index.ts
Normal file
160
packages/excalidraw/fonts/Excalifont/index.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
import _5 from "./Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2";
|
||||
import _4 from "./Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2";
|
||||
import _3 from "./Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2";
|
||||
import _6 from "./Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2";
|
||||
import _0 from "./Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2";
|
||||
import _2 from "./Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2";
|
||||
import _1 from "./Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2";
|
||||
|
||||
/* Generated By cn-font-split@5.2.2 https://www.npmjs.com/package/cn-font-split
|
||||
CreateTime: Mon, 14 Oct 2024 18:59:19 GMT;
|
||||
Origin File Name Table:
|
||||
copyright: Copyright (c) 2024 by Excalidraw. All rights reserved.
|
||||
fontFamily: Excalifont
|
||||
fontSubfamily: Regular
|
||||
uniqueID: 1.000;DSGN;Excalifont
|
||||
fullName: Excalifont Regular
|
||||
version: Version 1.000;Glyphs 3.2 (3227)
|
||||
postScriptName: Excalifont-Regular
|
||||
trademark: Excalifont is a trademark of Excalidraw.
|
||||
manufacturer: Your Own Font Foundry (Virgil); Ján Filípek / DizajnDesign (Excalifont, modifications)
|
||||
designer: Your Own Font Foundry (Virgil); Ján Filípek / DizajnDesign (Excalifont, modifications)
|
||||
manufacturerURL: https://dizajndesign.sk
|
||||
designerURL: https://dizajndesign.sk
|
||||
license: This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
licenseURL: http://scripts.sil.org/OFL
|
||||
preferredFamily: Excalifont
|
||||
preferredSubfamily: Regular
|
||||
*/
|
||||
|
||||
export const ExcalifontFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: _0,
|
||||
descriptors: {
|
||||
unicodeRange:
|
||||
"U+20-7e,U+a0-a3,U+a5-a6,U+a8-ab,U+ad-b1,U+b4,U+b6-b8,U+ba-ff,U+131,U+152-153,U+2bc,U+2c6,U+2da,U+2dc,U+304,U+308,U+2013-2014,U+2018-201a,U+201c-201e,U+2020,U+2022,U+2024-2026,U+2030,U+2039-203a,U+20ac,U+2122,U+2212",
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: _1,
|
||||
descriptors: {
|
||||
unicodeRange:
|
||||
"U+100-130,U+132-137,U+139-149,U+14c-151,U+154-17e,U+192,U+1fc-1ff,U+218-21b,U+237,U+1e80-1e85,U+1ef2-1ef3,U+2113",
|
||||
},
|
||||
},
|
||||
{ uri: _2, descriptors: { unicodeRange: "U+400-45f,U+490-491,U+2116" } },
|
||||
{
|
||||
uri: _3,
|
||||
descriptors: {
|
||||
unicodeRange:
|
||||
"U+37e,U+384-38a,U+38c,U+38e-393,U+395-3a1,U+3a3-3a8,U+3aa-3cf,U+3d7",
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: _4,
|
||||
descriptors: {
|
||||
unicodeRange:
|
||||
"U+2c7,U+2d8-2d9,U+2db,U+2dd,U+302,U+306-307,U+30a-30c,U+326-328,U+212e,U+2211,U+fb01-fb02",
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: _5,
|
||||
descriptors: {
|
||||
unicodeRange:
|
||||
"U+462-463,U+472-475,U+4d8-4d9,U+4e2-4e3,U+4e6-4e9,U+4ee-4ef",
|
||||
},
|
||||
},
|
||||
{ uri: _6, descriptors: { unicodeRange: "U+300-301,U+303" } },
|
||||
];
|
||||
483
packages/excalidraw/fonts/Fonts.ts
Normal file
483
packages/excalidraw/fonts/Fonts.ts
Normal file
@@ -0,0 +1,483 @@
|
||||
import {
|
||||
FONT_FAMILY,
|
||||
FONT_FAMILY_FALLBACKS,
|
||||
CJK_HAND_DRAWN_FALLBACK_FONT,
|
||||
WINDOWS_EMOJI_FALLBACK_FONT,
|
||||
getFontFamilyFallbacks,
|
||||
FONT_SIZES,
|
||||
} from "@excalidraw/common";
|
||||
import { getContainerElement } from "@excalidraw/element";
|
||||
import { charWidth } from "@excalidraw/element";
|
||||
import { containsCJK } from "@excalidraw/element";
|
||||
|
||||
import {
|
||||
FONT_METADATA,
|
||||
type FontMetadata,
|
||||
getFontString,
|
||||
PromisePool,
|
||||
promiseTry,
|
||||
} from "@excalidraw/common";
|
||||
|
||||
import { ShapeCache } from "@excalidraw/element";
|
||||
|
||||
import { isTextElement } from "@excalidraw/element";
|
||||
|
||||
import type {
|
||||
ExcalidrawElement,
|
||||
ExcalidrawTextElement,
|
||||
} from "@excalidraw/element/types";
|
||||
|
||||
import type { ValueOf } from "@excalidraw/common/utility-types";
|
||||
|
||||
import type { Scene } from "@excalidraw/element";
|
||||
|
||||
import { CascadiaFontFaces } from "./Cascadia";
|
||||
import { ComicShannsFontFaces } from "./ComicShanns";
|
||||
import { EmojiFontFaces } from "./Emoji";
|
||||
import { ExcalidrawFontFace } from "./ExcalidrawFontFace";
|
||||
import { ExcalifontFontFaces } from "./Excalifont";
|
||||
import { HelveticaFontFaces } from "./Helvetica";
|
||||
import { LiberationFontFaces } from "./Liberation";
|
||||
import { LilitaFontFaces } from "./Lilita";
|
||||
import { NunitoFontFaces } from "./Nunito";
|
||||
import { VirgilFontFaces } from "./Virgil";
|
||||
import { XiaolaiFontFaces } from "./Xiaolai";
|
||||
|
||||
export class Fonts {
|
||||
// it's ok to track fonts across multiple instances only once, so let's use
|
||||
// a static member to reduce memory footprint
|
||||
public static readonly loadedFontsCache = new Set<string>();
|
||||
|
||||
private static _registered:
|
||||
| Map<
|
||||
number,
|
||||
{
|
||||
metadata: FontMetadata;
|
||||
fontFaces: ExcalidrawFontFace[];
|
||||
}
|
||||
>
|
||||
| undefined;
|
||||
|
||||
private static _initialized: boolean = false;
|
||||
|
||||
public static get registered() {
|
||||
// lazy load the font registration
|
||||
if (!Fonts._registered) {
|
||||
Fonts._registered = Fonts.init();
|
||||
} else if (!Fonts._initialized) {
|
||||
// case when host app register fonts before they are lazy loaded
|
||||
// don't override whatever has been previously registered
|
||||
Fonts._registered = new Map([
|
||||
...Fonts.init().entries(),
|
||||
...Fonts._registered.entries(),
|
||||
]);
|
||||
}
|
||||
|
||||
return Fonts._registered;
|
||||
}
|
||||
|
||||
public get registered() {
|
||||
return Fonts.registered;
|
||||
}
|
||||
|
||||
private readonly scene: Scene;
|
||||
private readonly ownerDocument: Document;
|
||||
|
||||
constructor(scene: Scene, ownerDocument: Document = document) {
|
||||
this.scene = scene;
|
||||
this.ownerDocument = ownerDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the font families for the given scene.
|
||||
*/
|
||||
public getSceneFamilies = () => {
|
||||
return Fonts.getUniqueFamilies(this.scene.getNonDeletedElements());
|
||||
};
|
||||
|
||||
/**
|
||||
* if we load a (new) font, it's likely that text elements using it have
|
||||
* already been rendered using a fallback font. Thus, we want invalidate
|
||||
* their shapes and rerender. See #637.
|
||||
*
|
||||
* Invalidates text elements and rerenders scene, provided that at least one
|
||||
* of the supplied fontFaces has not already been processed.
|
||||
*/
|
||||
public onLoaded = (fontFaces: readonly FontFace[]): void => {
|
||||
// bail if all fonts with have been processed. We're checking just a
|
||||
// subset of the font properties (though it should be enough), so it
|
||||
// can technically bail on a false positive.
|
||||
let shouldBail = true;
|
||||
|
||||
for (const fontFace of fontFaces) {
|
||||
const sig = `${fontFace.family}-${fontFace.style}-${fontFace.weight}-${fontFace.unicodeRange}`;
|
||||
|
||||
// make sure to update our cache with all the loaded font faces
|
||||
if (!Fonts.loadedFontsCache.has(sig)) {
|
||||
Fonts.loadedFontsCache.add(sig);
|
||||
shouldBail = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBail) {
|
||||
return;
|
||||
}
|
||||
|
||||
let didUpdate = false;
|
||||
|
||||
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||
|
||||
for (const element of this.scene.getNonDeletedElements()) {
|
||||
if (isTextElement(element)) {
|
||||
didUpdate = true;
|
||||
ShapeCache.delete(element);
|
||||
|
||||
// clear the width cache, so that we don't perform subsequent wrapping based on the stale fallback font metrics
|
||||
charWidth.clearCache(getFontString(element));
|
||||
|
||||
const container = getContainerElement(element, elementsMap);
|
||||
if (container) {
|
||||
ShapeCache.delete(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (didUpdate) {
|
||||
this.scene.triggerUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load font faces for a given scene and trigger scene update.
|
||||
*/
|
||||
public loadSceneFonts = async (): Promise<FontFace[]> => {
|
||||
const sceneFamilies = this.getSceneFamilies();
|
||||
const charsPerFamily = Fonts.getCharsPerFamily(
|
||||
this.scene.getNonDeletedElements(),
|
||||
);
|
||||
|
||||
return Fonts.loadFontFaces(
|
||||
sceneFamilies,
|
||||
charsPerFamily,
|
||||
this.ownerDocument,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load font faces for passed elements - use when the scene is unavailable (i.e. export).
|
||||
*/
|
||||
public static loadElementsFonts = async (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
ownerDocument: Document = document,
|
||||
): Promise<FontFace[]> => {
|
||||
const fontFamilies = Fonts.getUniqueFamilies(elements);
|
||||
const charsPerFamily = Fonts.getCharsPerFamily(elements);
|
||||
|
||||
return Fonts.loadFontFaces(fontFamilies, charsPerFamily, ownerDocument);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate CSS @font-face declarations for the given elements.
|
||||
*/
|
||||
public static async generateFontFaceDeclarations(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
) {
|
||||
const families = Fonts.getUniqueFamilies(elements);
|
||||
const charsPerFamily = Fonts.getCharsPerFamily(elements);
|
||||
|
||||
// for simplicity, assuming we have just one family with the CJK handdrawn fallback
|
||||
const familyWithCJK = families.find((x) =>
|
||||
getFontFamilyFallbacks(x).includes(CJK_HAND_DRAWN_FALLBACK_FONT),
|
||||
);
|
||||
|
||||
if (familyWithCJK) {
|
||||
const characters = Fonts.getCharacters(charsPerFamily, familyWithCJK);
|
||||
|
||||
if (containsCJK(characters)) {
|
||||
const family = FONT_FAMILY_FALLBACKS[CJK_HAND_DRAWN_FALLBACK_FONT];
|
||||
|
||||
// adding the same characters to the CJK handrawn family
|
||||
charsPerFamily[family] = new Set(characters);
|
||||
|
||||
// the order between the families and fallbacks is important, as fallbacks need to be defined first and in the reversed order
|
||||
// so that they get overriden with the later defined font faces, i.e. in case they share some codepoints
|
||||
families.unshift(FONT_FAMILY_FALLBACKS[CJK_HAND_DRAWN_FALLBACK_FONT]);
|
||||
}
|
||||
}
|
||||
|
||||
// don't trigger hundreds of concurrent requests (each performing fetch, creating a worker, etc.),
|
||||
// instead go three requests at a time, in a controlled manner, without completely blocking the main thread
|
||||
// and avoiding potential issues such as rate limits
|
||||
const iterator = Fonts.fontFacesStylesGenerator(families, charsPerFamily);
|
||||
const concurrency = 3;
|
||||
const fontFaces = await new PromisePool(iterator, concurrency).all();
|
||||
|
||||
// dedup just in case (i.e. could be the same font faces with 0 glyphs)
|
||||
return Array.from(new Set(fontFaces));
|
||||
}
|
||||
|
||||
private static async loadFontFaces(
|
||||
fontFamilies: Array<ExcalidrawTextElement["fontFamily"]>,
|
||||
charsPerFamily: Record<number, Set<string>>,
|
||||
ownerDocument: Document,
|
||||
) {
|
||||
// add all registered font faces into the `document.fonts` (if not added already)
|
||||
for (const { fontFaces, metadata } of Fonts.registered.values()) {
|
||||
// skip registering font faces for local fonts (i.e. Helvetica)
|
||||
if (metadata.local) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { fontFace } of fontFaces) {
|
||||
if (!ownerDocument.fonts.has(fontFace)) {
|
||||
ownerDocument.fonts.add(fontFace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loading 10 font faces at a time, in a controlled manner
|
||||
const iterator = Fonts.fontFacesLoader(
|
||||
fontFamilies,
|
||||
charsPerFamily,
|
||||
ownerDocument,
|
||||
);
|
||||
const concurrency = 10;
|
||||
const fontFaces = await new PromisePool(iterator, concurrency).all();
|
||||
return fontFaces.flat().filter(Boolean);
|
||||
}
|
||||
|
||||
private static *fontFacesLoader(
|
||||
fontFamilies: Array<ExcalidrawTextElement["fontFamily"]>,
|
||||
charsPerFamily: Record<number, Set<string>>,
|
||||
ownerDocument: Document,
|
||||
): Generator<Promise<void | readonly [number, FontFace[]]>> {
|
||||
for (const [index, fontFamily] of fontFamilies.entries()) {
|
||||
const font = getFontString({
|
||||
fontFamily,
|
||||
fontSize: FONT_SIZES.sm,
|
||||
});
|
||||
|
||||
// WARN: without "text" param it does not have to mean that all font faces are loaded as it could be just one irrelevant font face!
|
||||
// instead, we are always checking chars used in the family, so that no required font faces remain unloaded
|
||||
const text = Fonts.getCharacters(charsPerFamily, fontFamily);
|
||||
|
||||
if (!ownerDocument.fonts.check(font, text)) {
|
||||
yield promiseTry(async () => {
|
||||
try {
|
||||
// WARN: browser prioritizes loading only font faces with unicode ranges for characters which are present in the document (html & canvas), other font faces could stay unloaded
|
||||
// we might want to retry here, i.e. in case CDN is down, but so far I didn't experience any issues - maybe it handles retry-like logic under the hood
|
||||
const fontFaces = await ownerDocument.fonts.load(font, text);
|
||||
|
||||
return [index, fontFaces];
|
||||
} catch (e) {
|
||||
// don't let it all fail if just one font fails to load
|
||||
console.error(
|
||||
`Failed to load font "${font}" from urls "${Fonts.registered
|
||||
.get(fontFamily)
|
||||
?.fontFaces.map((x) => x.urls)}"`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static *fontFacesStylesGenerator(
|
||||
families: Array<number>,
|
||||
charsPerFamily: Record<number, Set<string>>,
|
||||
): Generator<Promise<void | readonly [number, string]>> {
|
||||
for (const [familyIndex, family] of families.entries()) {
|
||||
const { fontFaces, metadata } = Fonts.registered.get(family) ?? {};
|
||||
|
||||
if (!Array.isArray(fontFaces)) {
|
||||
console.error(
|
||||
`Couldn't find registered fonts for font-family "${family}"`,
|
||||
Fonts.registered,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (metadata?.local) {
|
||||
// don't inline local fonts
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const [fontFaceIndex, fontFace] of fontFaces.entries()) {
|
||||
yield promiseTry(async () => {
|
||||
try {
|
||||
const characters = Fonts.getCharacters(charsPerFamily, family);
|
||||
const fontFaceCSS = await fontFace.toCSS(characters);
|
||||
|
||||
if (!fontFaceCSS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// giving a buffer of 10K font faces per family
|
||||
const fontFaceOrder = familyIndex * 10_000 + fontFaceIndex;
|
||||
const fontFaceTuple = [fontFaceOrder, fontFaceCSS] as const;
|
||||
|
||||
return fontFaceTuple;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Couldn't transform font-face to css for family "${fontFace.fontFace.family}"`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new font.
|
||||
*
|
||||
* @param family font family
|
||||
* @param metadata font metadata
|
||||
* @param fontFacesDecriptors font faces descriptors
|
||||
*/
|
||||
private static register(
|
||||
this:
|
||||
| Fonts
|
||||
| {
|
||||
registered: Map<
|
||||
number,
|
||||
{ metadata: FontMetadata; fontFaces: ExcalidrawFontFace[] }
|
||||
>;
|
||||
},
|
||||
family: string,
|
||||
metadata: FontMetadata,
|
||||
...fontFacesDecriptors: ExcalidrawFontFaceDescriptor[]
|
||||
) {
|
||||
// TODO: likely we will need to abandon number value in order to support custom fonts
|
||||
const fontFamily =
|
||||
FONT_FAMILY[family as keyof typeof FONT_FAMILY] ??
|
||||
FONT_FAMILY_FALLBACKS[family as keyof typeof FONT_FAMILY_FALLBACKS];
|
||||
|
||||
const registeredFamily = this.registered.get(fontFamily);
|
||||
|
||||
if (!registeredFamily) {
|
||||
this.registered.set(fontFamily, {
|
||||
metadata,
|
||||
fontFaces: fontFacesDecriptors.map(
|
||||
({ uri, descriptors }) =>
|
||||
new ExcalidrawFontFace(family, uri, descriptors),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return this.registered;
|
||||
}
|
||||
|
||||
/**
|
||||
* WARN: should be called just once on init, even across multiple instances.
|
||||
*/
|
||||
private static init() {
|
||||
const fonts = {
|
||||
registered: new Map<
|
||||
ValueOf<typeof FONT_FAMILY | typeof FONT_FAMILY_FALLBACKS>,
|
||||
{ metadata: FontMetadata; fontFaces: ExcalidrawFontFace[] }
|
||||
>(),
|
||||
};
|
||||
|
||||
const init = (
|
||||
family: keyof typeof FONT_FAMILY | keyof typeof FONT_FAMILY_FALLBACKS,
|
||||
...fontFacesDescriptors: ExcalidrawFontFaceDescriptor[]
|
||||
) => {
|
||||
const fontFamily =
|
||||
FONT_FAMILY[family as keyof typeof FONT_FAMILY] ??
|
||||
FONT_FAMILY_FALLBACKS[family as keyof typeof FONT_FAMILY_FALLBACKS];
|
||||
|
||||
// default to Excalifont metrics
|
||||
const metadata =
|
||||
FONT_METADATA[fontFamily] ?? FONT_METADATA[FONT_FAMILY.Excalifont];
|
||||
|
||||
Fonts.register.call(fonts, family, metadata, ...fontFacesDescriptors);
|
||||
};
|
||||
|
||||
init("Cascadia", ...CascadiaFontFaces);
|
||||
init("Comic Shanns", ...ComicShannsFontFaces);
|
||||
init("Excalifont", ...ExcalifontFontFaces);
|
||||
// keeping for backwards compatibility reasons, uses system font (Helvetica on MacOS, Arial on Win)
|
||||
init("Helvetica", ...HelveticaFontFaces);
|
||||
// used for server-side pdf & png export instead of helvetica (technically does not need metrics, but kept in for consistency)
|
||||
init("Liberation Sans", ...LiberationFontFaces);
|
||||
init("Lilita One", ...LilitaFontFaces);
|
||||
init("Nunito", ...NunitoFontFaces);
|
||||
init("Virgil", ...VirgilFontFaces);
|
||||
|
||||
// fallback font faces
|
||||
init(CJK_HAND_DRAWN_FALLBACK_FONT, ...XiaolaiFontFaces);
|
||||
init(WINDOWS_EMOJI_FALLBACK_FONT, ...EmojiFontFaces);
|
||||
|
||||
Fonts._initialized = true;
|
||||
|
||||
return fonts.registered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the unique font families for the given elements.
|
||||
*/
|
||||
private static getUniqueFamilies(
|
||||
elements: ReadonlyArray<ExcalidrawElement>,
|
||||
): Array<ExcalidrawTextElement["fontFamily"]> {
|
||||
return Array.from(
|
||||
elements.reduce((families, element) => {
|
||||
if (isTextElement(element)) {
|
||||
families.add(element.fontFamily);
|
||||
}
|
||||
return families;
|
||||
}, new Set<number>()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the unique characters per font family for the given scene.
|
||||
*/
|
||||
private static getCharsPerFamily(
|
||||
elements: ReadonlyArray<ExcalidrawElement>,
|
||||
): Record<number, Set<string>> {
|
||||
const charsPerFamily: Record<number, Set<string>> = {};
|
||||
|
||||
for (const element of elements) {
|
||||
if (!isTextElement(element)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// gather unique codepoints only when inlining fonts
|
||||
for (const char of element.originalText) {
|
||||
if (!charsPerFamily[element.fontFamily]) {
|
||||
charsPerFamily[element.fontFamily] = new Set();
|
||||
}
|
||||
|
||||
charsPerFamily[element.fontFamily].add(char);
|
||||
}
|
||||
}
|
||||
|
||||
return charsPerFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get characters for a given family.
|
||||
*/
|
||||
private static getCharacters(
|
||||
charsPerFamily: Record<number, Set<string>>,
|
||||
family: number,
|
||||
) {
|
||||
return charsPerFamily[family]
|
||||
? Array.from(charsPerFamily[family]).join("")
|
||||
: "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered font families.
|
||||
*/
|
||||
private static getAllFamilies() {
|
||||
return Array.from(Fonts.registered.keys());
|
||||
}
|
||||
}
|
||||
|
||||
export interface ExcalidrawFontFaceDescriptor {
|
||||
uri: string;
|
||||
descriptors?: FontFaceDescriptors;
|
||||
}
|
||||
9
packages/excalidraw/fonts/Helvetica/index.ts
Normal file
9
packages/excalidraw/fonts/Helvetica/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { LOCAL_FONT_PROTOCOL } from "@excalidraw/common";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const HelveticaFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: LOCAL_FONT_PROTOCOL,
|
||||
},
|
||||
];
|
||||
Binary file not shown.
9
packages/excalidraw/fonts/Liberation/index.ts
Normal file
9
packages/excalidraw/fonts/Liberation/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
import LiberationSansRegular from "./LiberationSans-Regular.woff2";
|
||||
|
||||
export const LiberationFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: LiberationSansRegular,
|
||||
},
|
||||
];
|
||||
Binary file not shown.
Binary file not shown.
17
packages/excalidraw/fonts/Lilita/index.ts
Normal file
17
packages/excalidraw/fonts/Lilita/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { GOOGLE_FONTS_RANGES } from "@excalidraw/common";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
import LilitaLatinExt from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2";
|
||||
import LilitaLatin from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2";
|
||||
|
||||
export const LilitaFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: LilitaLatinExt,
|
||||
descriptors: { unicodeRange: GOOGLE_FONTS_RANGES.LATIN_EXT },
|
||||
},
|
||||
{
|
||||
uri: LilitaLatin,
|
||||
descriptors: { unicodeRange: GOOGLE_FONTS_RANGES.LATIN },
|
||||
},
|
||||
];
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
38
packages/excalidraw/fonts/Nunito/index.ts
Normal file
38
packages/excalidraw/fonts/Nunito/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { GOOGLE_FONTS_RANGES } from "@excalidraw/common";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
import Cyrilic from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2";
|
||||
import Latin from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2";
|
||||
import CyrilicExt from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2";
|
||||
import LatinExt from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2";
|
||||
import Vietnamese from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2";
|
||||
|
||||
export const NunitoFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: CyrilicExt,
|
||||
descriptors: {
|
||||
unicodeRange: GOOGLE_FONTS_RANGES.CYRILIC_EXT,
|
||||
weight: "500",
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: Cyrilic,
|
||||
descriptors: { unicodeRange: GOOGLE_FONTS_RANGES.CYRILIC, weight: "500" },
|
||||
},
|
||||
{
|
||||
uri: Vietnamese,
|
||||
descriptors: {
|
||||
unicodeRange: GOOGLE_FONTS_RANGES.VIETNAMESE,
|
||||
weight: "500",
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: LatinExt,
|
||||
descriptors: { unicodeRange: GOOGLE_FONTS_RANGES.LATIN_EXT, weight: "500" },
|
||||
},
|
||||
{
|
||||
uri: Latin,
|
||||
descriptors: { unicodeRange: GOOGLE_FONTS_RANGES.LATIN, weight: "500" },
|
||||
},
|
||||
];
|
||||
BIN
packages/excalidraw/fonts/Virgil/Virgil-Regular.woff2
Normal file
BIN
packages/excalidraw/fonts/Virgil/Virgil-Regular.woff2
Normal file
Binary file not shown.
9
packages/excalidraw/fonts/Virgil/index.ts
Normal file
9
packages/excalidraw/fonts/Virgil/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
import Virgil from "./Virgil-Regular.woff2";
|
||||
|
||||
export const VirgilFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: Virgil,
|
||||
},
|
||||
];
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user