mirror of
https://github.com/excalidraw/excalidraw.git
synced 2024-11-10 11:35:52 +01:00
fix: Comic Shanns issues, new fonts structure (#8641)
This commit is contained in:
parent
15ca182333
commit
61623bbeba
2
examples/excalidraw/with-nextjs/.gitignore
vendored
2
examples/excalidraw/with-nextjs/.gitignore
vendored
@ -36,4 +36,4 @@ yarn-error.log*
|
||||
next-env.d.ts
|
||||
|
||||
# copied assets
|
||||
public/*.woff2
|
||||
public/**/*.woff2
|
@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:workspace": "yarn workspace @excalidraw/excalidraw run build:esm && yarn copy:assets",
|
||||
"copy:assets": "cp ../../../packages/excalidraw/dist/browser/prod/excalidraw-assets/*.woff2 ./public",
|
||||
"copy:assets": "cp -r ../../../packages/excalidraw/dist/prod/fonts ./public",
|
||||
"dev": "yarn build:workspace && next dev -p 3005",
|
||||
"build": "yarn build:workspace && next build",
|
||||
"start": "next start -p 3006",
|
||||
|
@ -1,2 +1,2 @@
|
||||
# copied assets
|
||||
public/*.woff2
|
||||
public/**/*.woff2
|
@ -13,7 +13,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build:workspace": "yarn workspace @excalidraw/excalidraw run build:esm && yarn copy:assets",
|
||||
"copy:assets": "cp ../../../packages/excalidraw/dist/browser/prod/excalidraw-assets/*.woff2 ./public",
|
||||
"copy:assets": "cp -r ../../../packages/excalidraw/dist/prod/fonts ./public",
|
||||
"start": "yarn build:workspace && vite",
|
||||
"build": "yarn build:workspace && vite build",
|
||||
"build:preview": "yarn build && vite preview --port 5002"
|
||||
|
@ -133,7 +133,7 @@
|
||||
<!-- Register Assistant as the UI font, before the scene inits -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="../packages/excalidraw/fonts/css/fonts.css"
|
||||
href="../packages/excalidraw/fonts/fonts.css"
|
||||
type="text/css"
|
||||
/>
|
||||
|
||||
|
@ -25,14 +25,8 @@ export default defineConfig({
|
||||
output: {
|
||||
assetFileNames(chunkInfo) {
|
||||
if (chunkInfo?.name?.endsWith(".woff2")) {
|
||||
// TODO: consider splitting all fonts similar to Xiaolai
|
||||
// fonts don't change often, so hash is not necessary
|
||||
// put on root so we are flexible about the CDN path
|
||||
if (chunkInfo.name.includes("Xiaolai")) {
|
||||
return "[name][extname]";
|
||||
} else {
|
||||
return "[name]-[hash][extname]";
|
||||
}
|
||||
const family = chunkInfo.name.split("-")[0];
|
||||
return `fonts/${family}/[name][extname]`;
|
||||
}
|
||||
|
||||
return "assets/[name]-[hash][extname]";
|
||||
|
@ -736,7 +736,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
id: this.id,
|
||||
};
|
||||
|
||||
this.fonts = new Fonts({ scene: this.scene });
|
||||
this.fonts = new Fonts(this.scene);
|
||||
this.history = new History();
|
||||
|
||||
this.actionManager.registerAll(actions);
|
||||
@ -2471,7 +2471,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.renderer.destroy();
|
||||
this.scene.destroy();
|
||||
this.scene = new Scene();
|
||||
this.fonts = new Fonts({ scene: this.scene });
|
||||
this.fonts = new Fonts(this.scene);
|
||||
this.renderer = new Renderer(this.scene);
|
||||
this.files = {};
|
||||
this.imageCache.clear();
|
||||
|
@ -1,5 +1,6 @@
|
||||
import CascadiaCodeRegular from "./CascadiaCode-Regular.woff2";
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../..";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const CascadiaFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
BIN
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2
Normal file
BIN
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2
Normal file
BIN
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2
Normal file
BIN
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2
Normal file
BIN
packages/excalidraw/fonts/ComicShanns/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2
Normal file
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 _0 from "./ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2";
|
||||
import _1 from "./ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2";
|
||||
import _2 from "./ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2";
|
||||
import _3 from "./ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
/* 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 "../FontMetadata";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const EmojiFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: LOCAL_FONT_PROTOCOL,
|
||||
},
|
||||
];
|
@ -1,19 +1,10 @@
|
||||
import { promiseTry } from "../utils";
|
||||
import { LOCAL_FONT_PROTOCOL } from "./metadata";
|
||||
import { subsetWoff2GlyphsByCodepoints } from "./subset/subset-main";
|
||||
import { LOCAL_FONT_PROTOCOL } from "./FontMetadata";
|
||||
import { subsetWoff2GlyphsByCodepoints } from "../subset/subset-main";
|
||||
|
||||
type DataURL = string;
|
||||
|
||||
export interface IExcalidrawFontFace {
|
||||
urls: URL[] | DataURL[];
|
||||
fontFace: FontFace;
|
||||
toCSS(
|
||||
characters: string,
|
||||
codePoints: Array<number>,
|
||||
): Promise<string> | undefined;
|
||||
}
|
||||
|
||||
export class ExcalidrawFontFace implements IExcalidrawFontFace {
|
||||
export class ExcalidrawFontFace {
|
||||
public readonly urls: URL[] | DataURL[];
|
||||
public readonly fontFace: FontFace;
|
||||
|
||||
@ -43,16 +34,17 @@ export class ExcalidrawFontFace implements IExcalidrawFontFace {
|
||||
*
|
||||
* Retrieves `undefined` otherwise.
|
||||
*/
|
||||
public toCSS(
|
||||
characters: string,
|
||||
codePoints: Array<number>,
|
||||
): Promise<string> | undefined {
|
||||
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;
|
||||
}
|
||||
|
||||
return this.getContent(codePoints).then(
|
||||
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}); }`,
|
||||
);
|
||||
@ -98,6 +90,10 @@ export class ExcalidrawFontFace implements IExcalidrawFontFace {
|
||||
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",
|
||||
},
|
||||
|
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2
Normal file
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2
Normal file
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2
Normal file
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2
Normal file
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2
Normal file
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2
Normal file
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2
Normal file
Binary file not shown.
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2
Normal file
BIN
packages/excalidraw/fonts/Excalifont/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2
Normal file
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 _0 from "./Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2";
|
||||
import _1 from "./Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2";
|
||||
import _2 from "./Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2";
|
||||
import _3 from "./Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2";
|
||||
import _4 from "./Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2";
|
||||
import _5 from "./Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2";
|
||||
import _6 from "./Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
/* 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" } },
|
||||
];
|
@ -1,8 +1,8 @@
|
||||
import {
|
||||
FontFamilyCodeIcon,
|
||||
FontFamilyHeadingIcon,
|
||||
FontFamilyNormalIcon,
|
||||
FreedrawIcon,
|
||||
FontFamilyNormalIcon,
|
||||
FontFamilyHeadingIcon,
|
||||
FontFamilyCodeIcon,
|
||||
} from "../components/icons";
|
||||
import { FONT_FAMILY, FONT_FAMILY_FALLBACKS } from "../constants";
|
||||
|
349
packages/excalidraw/fonts/Fonts.ts
Normal file
349
packages/excalidraw/fonts/Fonts.ts
Normal file
@ -0,0 +1,349 @@
|
||||
import {
|
||||
FONT_FAMILY,
|
||||
FONT_FAMILY_FALLBACKS,
|
||||
CJK_HAND_DRAWN_FALLBACK_FONT,
|
||||
WINDOWS_EMOJI_FALLBACK_FONT,
|
||||
} from "../constants";
|
||||
import { isTextElement } from "../element";
|
||||
import { charWidth, getContainerElement } from "../element/textElement";
|
||||
import { ShapeCache } from "../scene/ShapeCache";
|
||||
import { getFontString } from "../utils";
|
||||
import { ExcalidrawFontFace } from "./ExcalidrawFontFace";
|
||||
|
||||
import { CascadiaFontFaces } from "./Cascadia";
|
||||
import { ComicShannsFontFaces } from "./ComicShanns";
|
||||
import { EmojiFontFaces } from "./Emoji";
|
||||
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";
|
||||
|
||||
import { FONT_METADATA, type FontMetadata } from "./FontMetadata";
|
||||
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
|
||||
// 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;
|
||||
|
||||
constructor(scene: Scene) {
|
||||
this.scene = scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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[]) => {
|
||||
// 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 loaded = await Fonts.loadFontFaces(sceneFamilies);
|
||||
this.onLoaded(loaded);
|
||||
return loaded;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load all registered font faces.
|
||||
*/
|
||||
public static loadAllFonts = async (): Promise<FontFace[]> => {
|
||||
const allFamilies = Fonts.getAllFamilies();
|
||||
return Fonts.loadFontFaces(allFamilies);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load font faces for passed elements - use when the scene is unavailable (i.e. export).
|
||||
*/
|
||||
public static loadElementsFonts = async (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
): Promise<FontFace[]> => {
|
||||
const fontFamilies = Fonts.getElementsFamilies(elements);
|
||||
return await Fonts.loadFontFaces(fontFamilies);
|
||||
};
|
||||
|
||||
private static async loadFontFaces(
|
||||
fontFamilies: Array<ExcalidrawTextElement["fontFamily"]>,
|
||||
) {
|
||||
// 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 (!window.document.fonts.has(fontFace)) {
|
||||
window.document.fonts.add(fontFace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const loadedFontFaces = await Promise.all(
|
||||
fontFamilies.map(async (fontFamily) => {
|
||||
const fontString = getFontString({
|
||||
fontFamily,
|
||||
fontSize: 16,
|
||||
});
|
||||
|
||||
// WARN: without "text" param it does not have to mean that all font faces are loaded, instead it could be just one!
|
||||
if (!window.document.fonts.check(fontString)) {
|
||||
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
|
||||
return await window.document.fonts.load(fontString);
|
||||
} catch (e) {
|
||||
// don't let it all fail if just one font fails to load
|
||||
console.error(
|
||||
`Failed to load font "${fontString}" from urls "${Fonts.registered
|
||||
.get(fontFamily)
|
||||
?.fontFaces.map((x) => x.urls)}"`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}),
|
||||
);
|
||||
|
||||
return loadedFontFaces.flat().filter(Boolean) as FontFace[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the font families for the given scene.
|
||||
*/
|
||||
public getSceneFamilies = () => {
|
||||
return Fonts.getElementsFamilies(this.scene.getNonDeletedElements());
|
||||
};
|
||||
|
||||
private static getAllFamilies() {
|
||||
return Array.from(Fonts.registered.keys());
|
||||
}
|
||||
|
||||
private static getElementsFamilies(
|
||||
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>()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
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 "../FontMetadata";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const HelveticaFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
||||
uri: LOCAL_FONT_PROTOCOL,
|
||||
},
|
||||
];
|
3
packages/excalidraw/fonts/woff2/Liberation/index.ts → packages/excalidraw/fonts/Liberation/index.ts
3
packages/excalidraw/fonts/woff2/Liberation/index.ts → packages/excalidraw/fonts/Liberation/index.ts
@ -1,5 +1,6 @@
|
||||
import LiberationSansRegular from "./LiberationSans-Regular.woff2";
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../..";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const LiberationFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
@ -1,8 +1,9 @@
|
||||
import LilitaLatin from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2";
|
||||
import LilitaLatinExt from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2";
|
||||
|
||||
import { GOOGLE_FONTS_RANGES } from "../../metadata";
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../..";
|
||||
import { GOOGLE_FONTS_RANGES } from "../FontMetadata";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const LilitaFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
@ -4,8 +4,9 @@ import Cyrilic from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXW
|
||||
import CyrilicExt from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2";
|
||||
import Vietnamese from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2";
|
||||
|
||||
import { GOOGLE_FONTS_RANGES } from "../../metadata";
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../..";
|
||||
import { GOOGLE_FONTS_RANGES } from "../FontMetadata";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const NunitoFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
@ -1,5 +1,6 @@
|
||||
import Virgil from "./Virgil-Regular.woff2";
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../..";
|
||||
|
||||
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
|
||||
|
||||
export const VirgilFontFaces: ExcalidrawFontFaceDescriptor[] = [
|
||||
{
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user