Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cjk fonts #262

Merged
merged 5 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The welcome screen consists of two main groups of subcomponents:

<img
src={require("@site/static/img/welcome-screen-overview.png").default}
alt="Excalidraw logo: Sketch handrawn like diagrams."
alt="Excalidraw logo: Sketch hand-drawn like diagrams."
/>

### Center
Expand Down
2 changes: 1 addition & 1 deletion dev-docs/docs/@excalidraw/excalidraw/api/constants.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { FONT_FAMILY } from "@excalidraw/excalidraw";

| Font Family | Description |
| ----------- | ---------------------- |
| `Virgil` | The `handwritten` font |
| `Virgil` | The `Hand-drawn` font |
| `Helvetica` | The `Normal` Font |
| `Cascadia` | The `Code` Font |

Expand Down
2 changes: 1 addition & 1 deletion excalidraw-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
<!-- Register Assistant as the UI font, before the scene inits -->
<link
rel="stylesheet"
href="../packages/excalidraw/fonts/assets/fonts.css"
href="../packages/excalidraw/fonts/css/fonts.css"
type="text/css"
/>

Expand Down
30 changes: 20 additions & 10 deletions excalidraw-app/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ 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
return "[name]-[hash][extname]";
if (chunkInfo.name.includes("Xiaolai")) {
return "[name][extname]";
} else {
return "[name]-[hash][extname]";
}
}

return "assets/[name]-[hash][extname]";
Expand Down Expand Up @@ -75,17 +81,21 @@ export default defineConfig({
},

workbox: {
// Don't push fonts, locales and wasm to app precache
globIgnores: ["fonts.css", "**/locales/**", "service-worker.js", "**/*.wasm-*.js"],
// don't precache fonts, locales and separate chunks
globIgnores: ["fonts.css", "**/locales/**", "service-worker.js", "**/*.chunk-*.js"],
runtimeCaching: [
{
urlPattern: new RegExp("/.+.(ttf|woff2|otf)"),
handler: "CacheFirst",
urlPattern: new RegExp(".+.woff2"),
handler: 'CacheFirst',
options: {
cacheName: "fonts",
cacheName: 'fonts',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 * 90, // <== 90 days
maxEntries: 1000,
maxAgeSeconds: 60 * 60 * 24 * 90, // 90 days
},
cacheableResponse: {
// 0 to cache "opaque" responses from cross-origin requests (i.e. CDN)
statuses: [0, 200],
},
},
},
Expand All @@ -111,10 +121,10 @@ export default defineConfig({
},
},
{
urlPattern: new RegExp(".wasm-.+.js"),
urlPattern: new RegExp(".chunk-.+.js"),
handler: "CacheFirst",
options: {
cacheName: "wasm",
cacheName: "chunk",
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 * 90, // <== 90 days
Expand Down
2 changes: 2 additions & 0 deletions packages/excalidraw/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Please add the latest change on the top under the correct section.

### Features

- Added hand-drawn font for Chinese, Japanese and Korean (CJK) as a fallback for Excalifont. Improved overal text wrapping algorithm, not only accounting for CJK, but covering various edge cases with white spaces and text-align center/right. Added support for multi-codepoint emojis wrapping. Offloaded SVG export to Web Workers, with an automatic fallback to the main thread if not supported or not desired.[#8530](https://github.com/excalidraw/excalidraw/pull/8530)

- Prefer user defined coordinates and dimensions when creating a frame using [`convertToExcalidrawElements`](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton#converttoexcalidrawelements) [#8517](https://github.com/excalidraw/excalidraw/pull/8517)

- `props.initialData` can now be a function that returns `ExcalidrawInitialDataState` or `Promise<ExcalidrawInitialDataState>`. [#8107](https://github.com/excalidraw/excalidraw/pull/8135)
Expand Down
20 changes: 19 additions & 1 deletion packages/excalidraw/actions/actionClipboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,32 @@ export const actionCopyAsSvg = register({
name: app.getName(),
},
);

const selectedElements = app.scene.getSelectedElements({
selectedElementIds: appState.selectedElementIds,
includeBoundTextElement: true,
includeElementsInFrames: true,
});

return {
appState: {
toast: {
message: t("toast.copyToClipboardAsSvg", {
exportSelection: selectedElements.length
? t("toast.selection")
: t("toast.canvas"),
exportColorScheme: appState.exportWithDarkMode
? t("buttons.darkMode")
: t("buttons.lightMode"),
}),
},
},
storeAction: StoreAction.NONE,
};
} catch (error: any) {
console.error(error);
return {
appState: {
...appState,
errorMessage: error.message,
},
storeAction: StoreAction.NONE,
Expand Down
2 changes: 0 additions & 2 deletions packages/excalidraw/actions/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export type ShortcutName =
| "sendToBack"
| "bringToFront"
| "copyAsPng"
| "copyAsSvg"
| "group"
| "ungroup"
| "gridMode"
Expand Down Expand Up @@ -89,7 +88,6 @@ const shortcutMap: Record<ShortcutName, string[]> = {
: getShortcutKey("CtrlOrCmd+Shift+]"),
],
copyAsPng: [getShortcutKey("Shift+Alt+C")],
copyAsSvg: [],
group: [getShortcutKey("CtrlOrCmd+G")],
ungroup: [getShortcutKey("CtrlOrCmd+Shift+G")],
gridMode: [getShortcutKey("CtrlOrCmd+'")],
Expand Down
6 changes: 1 addition & 5 deletions packages/excalidraw/actions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import type {
BinaryFiles,
UIAppState,
} from "../types";
import type { MarkOptional } from "../utility-types";
import type { StoreActionType } from "../store";

export type ActionSource =
Expand All @@ -24,10 +23,7 @@ export type ActionSource =
export type ActionResult =
| {
elements?: readonly ExcalidrawElement[] | null;
appState?: MarkOptional<
AppState,
"offsetTop" | "offsetLeft" | "width" | "height"
> | null;
appState?: Partial<AppState> | null;
files?: BinaryFiles | null;
storeAction: StoreActionType;
replaceFiles?: boolean;
Expand Down
34 changes: 12 additions & 22 deletions packages/excalidraw/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2260,11 +2260,12 @@
editingTextElement = null;
}

this.setState((state) => {
// using Object.assign instead of spread to fool TS 4.2.2+ into
// regarding the resulting type as not containing undefined
// (which the following expression will never contain)
return Object.assign(actionResult.appState || {}, {
this.setState((prevAppState) => {
const actionAppState = actionResult.appState || {};

return {
...prevAppState,
...actionAppState,
// NOTE this will prevent opening context menu using an action
// or programmatically from the host, so it will need to be
// rewritten later
Expand All @@ -2275,7 +2276,7 @@
theme,
name,
errorMessage,
});
};
});

didUpdate = true;
Expand Down Expand Up @@ -4221,7 +4222,7 @@
?.getBoundingClientRect();
const PADDING = 16;

const adjustRectValueForOffset = ( //zsviczian https://github.com/excalidraw/excalidraw/issues/8561

Check warning on line 4225 in packages/excalidraw/components/App.tsx

View workflow job for this annotation

GitHub Actions / lint

Insert `⏎·····`
value: number | undefined,
fallback: number,
) => (value ?? fallback + this.state.offsetLeft) - this.state.offsetLeft;
Expand All @@ -4232,15 +4233,15 @@
right:
Math.max(
this.state.width -
adjustRectValueForOffset( //zsivczian

Check warning on line 4236 in packages/excalidraw/components/App.tsx

View workflow job for this annotation

GitHub Actions / lint

Insert `⏎·················`
propertiesPanelRect?.left,
this.state.width,
),
0,
) + PADDING,
bottom: PADDING,
left: //zsivczian

Check warning on line 4243 in packages/excalidraw/components/App.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `left:·`
Math.max(adjustRectValueForOffset(sidebarRect?.right, 0), 0) +

Check warning on line 4244 in packages/excalidraw/components/App.tsx

View workflow job for this annotation

GitHub Actions / lint

Insert `left:⏎··········`
PADDING,
}
: {
Expand All @@ -4252,7 +4253,7 @@
0,
),
bottom: PADDING,
left: //zsivczian

Check warning on line 4256 in packages/excalidraw/components/App.tsx

View workflow job for this annotation

GitHub Actions / lint

Replace `left:·//zsivczian` with `//zsivczian⏎··········left:`
Math.max(
adjustRectValueForOffset(propertiesPanelRect?.right, 0),
0,
Expand Down Expand Up @@ -5200,24 +5201,13 @@
this.store.shouldCaptureIncrement();
}

this.setState({
newElement: null,
editingTextElement: null,
flushSync(() => {
this.setState({
newElement: null,
editingTextElement: null,
});
});

//zsviczian
//when closing the color palette color picker popup while editing a text element
//sometimes even though setState was executed here successfully, there is a race condition
//resulting in editingTexElement not being set to null
//if this happens, this workaround will force it to null
setTimeout(() => {
if (this.state.editingTextElement) {
this.setState({
newElement: null,
editingTextElement: null,
});
}
});
if (this.state.activeTool.locked) {
setCursorForShape(this.interactiveCanvas, this.state);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/excalidraw/components/FontPicker/FontPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const DEFAULT_FONTS = [
value: FONT_FAMILY.Excalifont,
icon: FreedrawIcon,
text: t("labels.handDrawn"),
testId: "font-family-handrawn",
testId: "font-family-hand-drawn",
},
{
value: FONT_FAMILY.Nunito,
Expand Down
13 changes: 8 additions & 5 deletions packages/excalidraw/components/FontPicker/FontPickerList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { t } from "../../i18n";
import { fontPickerKeyHandler } from "./keyboardNavHandlers";
import { Fonts } from "../../fonts";
import type { ValueOf } from "../../utility-types";
import { FontFamilyNormalIcon } from "../icons";

export interface FontDescriptor {
value: number;
Expand Down Expand Up @@ -62,12 +63,14 @@ export const FontPickerList = React.memo(
const allFonts = useMemo(
() =>
Array.from(Fonts.registered.entries())
.filter(([_, { metadata }]) => !metadata.serverSide)
.map(([familyId, { metadata, fonts }]) => {
.filter(
([_, { metadata }]) => !metadata.serverSide && !metadata.fallback,
)
.map(([familyId, { metadata, fontFaces }]) => {
const fontDescriptor = {
value: familyId,
icon: metadata.icon,
text: fonts[0].fontFace.family,
icon: metadata.icon ?? FontFamilyNormalIcon,
text: fontFaces[0]?.fontFace?.family ?? "Unknown",
};

if (metadata.deprecated) {
Expand All @@ -89,7 +92,7 @@ export const FontPickerList = React.memo(
);

const sceneFamilies = useMemo(
() => new Set(fonts.getSceneFontFamilies()),
() => new Set(fonts.getSceneFamilies()),
// cache per selected font family, so hover re-render won't mess it up
// eslint-disable-next-line react-hooks/exhaustive-deps
[selectedFontFamily],
Expand Down
3 changes: 3 additions & 0 deletions packages/excalidraw/components/PasteChartDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ const ChartPreviewBtn = (props: {
viewBackgroundColor: oc.white,
},
null, // files
{
skipInliningFonts: true,
},
);
svg.querySelector(".style-fonts")?.remove();
previewNode.replaceChildren();
Expand Down
21 changes: 19 additions & 2 deletions packages/excalidraw/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ export const CLASSES = {
SEARCH_MENU_INPUT_WRAPPER: "layer-ui__search-inputWrapper",
};

export const CJK_HAND_DRAWN_FALLBACK_FONT = "Xiaolai";
export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji";

/**
* // TODO: shouldn't be really `const`, likely neither have integers as values, due to value for the custom fonts, which should likely be some hash.
*
Expand All @@ -139,6 +142,22 @@ export const FONT_FAMILY = {
"Liberation Sans": 9,
};

export const FONT_FAMILY_FALLBACKS = {
[CJK_HAND_DRAWN_FALLBACK_FONT]: 100,
[WINDOWS_EMOJI_FALLBACK_FONT]: 1000,
};

export const getFontFamilyFallbacks = (
fontFamily: number,
): Array<keyof typeof FONT_FAMILY_FALLBACKS> => {
switch (fontFamily) {
case FONT_FAMILY.Excalifont:
return [CJK_HAND_DRAWN_FALLBACK_FONT, WINDOWS_EMOJI_FALLBACK_FONT];
default:
return [WINDOWS_EMOJI_FALLBACK_FONT];
}
};

export const THEME = {
LIGHT: "light",
DARK: "dark",
Expand All @@ -160,8 +179,6 @@ export const FRAME_STYLE = {
nameLineHeight: 1.25,
};

export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji";

export const MIN_FONT_SIZE = 1;
export const DEFAULT_FONT_SIZE = 20;
export const DEFAULT_FONT_FAMILY: FontFamilyValues = FONT_FAMILY.Excalifont;
Expand Down
Loading
Loading