diff --git a/.yarn/cache/@react-pdf-fns-npm-2.2.1-77536ed89f-738bc27e45.zip b/.yarn/cache/@react-pdf-fns-npm-2.2.1-77536ed89f-738bc27e45.zip deleted file mode 100644 index 924880076..000000000 Binary files a/.yarn/cache/@react-pdf-fns-npm-2.2.1-77536ed89f-738bc27e45.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-font-npm-2.5.2-fabfa291d1-73d7753ea1.zip b/.yarn/cache/@react-pdf-font-npm-2.5.2-fabfa291d1-73d7753ea1.zip deleted file mode 100644 index cf27d1dd3..000000000 Binary files a/.yarn/cache/@react-pdf-font-npm-2.5.2-fabfa291d1-73d7753ea1.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-image-npm-2.3.6-69cfa0d486-177e221e61.zip b/.yarn/cache/@react-pdf-image-npm-2.3.6-69cfa0d486-177e221e61.zip deleted file mode 100644 index c80896654..000000000 Binary files a/.yarn/cache/@react-pdf-image-npm-2.3.6-69cfa0d486-177e221e61.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-image-npm-3.0.3-9bc75136a5-893ebef74d.zip b/.yarn/cache/@react-pdf-image-npm-3.0.3-9bc75136a5-893ebef74d.zip new file mode 100644 index 000000000..830b611aa Binary files /dev/null and b/.yarn/cache/@react-pdf-image-npm-3.0.3-9bc75136a5-893ebef74d.zip differ diff --git a/.yarn/cache/@react-pdf-layout-npm-3.13.0-cd41a316b7-d4014cc860.zip b/.yarn/cache/@react-pdf-layout-npm-3.13.0-cd41a316b7-d4014cc860.zip deleted file mode 100644 index 60a8d28f5..000000000 Binary files a/.yarn/cache/@react-pdf-layout-npm-3.13.0-cd41a316b7-d4014cc860.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-layout-npm-4.4.1-8639686b1e-5d7baa7f92.zip b/.yarn/cache/@react-pdf-layout-npm-4.4.1-8639686b1e-5d7baa7f92.zip new file mode 100644 index 000000000..6626ca0dd Binary files /dev/null and b/.yarn/cache/@react-pdf-layout-npm-4.4.1-8639686b1e-5d7baa7f92.zip differ diff --git a/.yarn/cache/@react-pdf-pdfkit-npm-3.2.0-c9d9adfdbc-2c95c9dbc3.zip b/.yarn/cache/@react-pdf-pdfkit-npm-3.2.0-c9d9adfdbc-2c95c9dbc3.zip deleted file mode 100644 index c08b74fab..000000000 Binary files a/.yarn/cache/@react-pdf-pdfkit-npm-3.2.0-c9d9adfdbc-2c95c9dbc3.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-png-js-npm-2.3.1-e17b137dee-bab00c8380.zip b/.yarn/cache/@react-pdf-png-js-npm-2.3.1-e17b137dee-bab00c8380.zip deleted file mode 100644 index b4025f598..000000000 Binary files a/.yarn/cache/@react-pdf-png-js-npm-2.3.1-e17b137dee-bab00c8380.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-primitives-npm-3.1.1-3033dff230-a52c0cfff7.zip b/.yarn/cache/@react-pdf-primitives-npm-3.1.1-3033dff230-a52c0cfff7.zip deleted file mode 100644 index 208d36d8c..000000000 Binary files a/.yarn/cache/@react-pdf-primitives-npm-3.1.1-3033dff230-a52c0cfff7.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-reconciler-npm-1.1.4-0a87628369-d920898a1c.zip b/.yarn/cache/@react-pdf-reconciler-npm-1.1.4-0a87628369-d920898a1c.zip new file mode 100644 index 000000000..cc19ec267 Binary files /dev/null and b/.yarn/cache/@react-pdf-reconciler-npm-1.1.4-0a87628369-d920898a1c.zip differ diff --git a/.yarn/cache/@react-pdf-render-npm-3.5.0-1e3290b32b-aac990fcac.zip b/.yarn/cache/@react-pdf-render-npm-3.5.0-1e3290b32b-aac990fcac.zip deleted file mode 100644 index edabea923..000000000 Binary files a/.yarn/cache/@react-pdf-render-npm-3.5.0-1e3290b32b-aac990fcac.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-render-npm-4.3.1-27f48b54c0-2dfea722c1.zip b/.yarn/cache/@react-pdf-render-npm-4.3.1-27f48b54c0-2dfea722c1.zip new file mode 100644 index 000000000..bb655d452 Binary files /dev/null and b/.yarn/cache/@react-pdf-render-npm-4.3.1-27f48b54c0-2dfea722c1.zip differ diff --git a/.yarn/cache/@react-pdf-renderer-npm-3.4.5-72da3ddb5d-f3e9a67796.zip b/.yarn/cache/@react-pdf-renderer-npm-3.4.5-72da3ddb5d-f3e9a67796.zip deleted file mode 100644 index 62453388e..000000000 Binary files a/.yarn/cache/@react-pdf-renderer-npm-3.4.5-72da3ddb5d-f3e9a67796.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-renderer-npm-4.3.1-2c58b5a4d3-0e9e2d9b30.zip b/.yarn/cache/@react-pdf-renderer-npm-4.3.1-2c58b5a4d3-0e9e2d9b30.zip new file mode 100644 index 000000000..77b8cebeb Binary files /dev/null and b/.yarn/cache/@react-pdf-renderer-npm-4.3.1-2c58b5a4d3-0e9e2d9b30.zip differ diff --git a/.yarn/cache/@react-pdf-stylesheet-npm-4.3.0-9bb826dfe3-9a32aca88e.zip b/.yarn/cache/@react-pdf-stylesheet-npm-4.3.0-9bb826dfe3-9a32aca88e.zip deleted file mode 100644 index ac58559e7..000000000 Binary files a/.yarn/cache/@react-pdf-stylesheet-npm-4.3.0-9bb826dfe3-9a32aca88e.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-textkit-npm-4.4.1-46021e9afd-e07b7574ba.zip b/.yarn/cache/@react-pdf-textkit-npm-4.4.1-46021e9afd-e07b7574ba.zip deleted file mode 100644 index 4fec8af5d..000000000 Binary files a/.yarn/cache/@react-pdf-textkit-npm-4.4.1-46021e9afd-e07b7574ba.zip and /dev/null differ diff --git a/.yarn/cache/@react-pdf-textkit-npm-6.0.0-ad8481d0d8-5bc087ad2d.zip b/.yarn/cache/@react-pdf-textkit-npm-6.0.0-ad8481d0d8-5bc087ad2d.zip new file mode 100644 index 000000000..e72524cf5 Binary files /dev/null and b/.yarn/cache/@react-pdf-textkit-npm-6.0.0-ad8481d0d8-5bc087ad2d.zip differ diff --git a/.yarn/cache/cross-fetch-npm-3.2.0-267029ff2f-8ded5ea35f.zip b/.yarn/cache/cross-fetch-npm-3.2.0-267029ff2f-8ded5ea35f.zip deleted file mode 100644 index 55d3c4191..000000000 Binary files a/.yarn/cache/cross-fetch-npm-3.2.0-267029ff2f-8ded5ea35f.zip and /dev/null differ diff --git a/.yarn/cache/emoji-regex-npm-10.3.0-0c9fc2ef7f-5da48edfeb.zip b/.yarn/cache/emoji-regex-npm-10.3.0-0c9fc2ef7f-5da48edfeb.zip deleted file mode 100644 index d57801449..000000000 Binary files a/.yarn/cache/emoji-regex-npm-10.3.0-0c9fc2ef7f-5da48edfeb.zip and /dev/null differ diff --git a/.yarn/cache/emoji-regex-xs-npm-1.0.0-f4e3bfb84a-c33be159da.zip b/.yarn/cache/emoji-regex-xs-npm-1.0.0-f4e3bfb84a-c33be159da.zip new file mode 100644 index 000000000..8c0535336 Binary files /dev/null and b/.yarn/cache/emoji-regex-xs-npm-1.0.0-f4e3bfb84a-c33be159da.zip differ diff --git a/.yarn/cache/scheduler-npm-0.17.0-5c42088cec-18d1e66cad.zip b/.yarn/cache/scheduler-npm-0.17.0-5c42088cec-18d1e66cad.zip deleted file mode 100644 index bb0e52058..000000000 Binary files a/.yarn/cache/scheduler-npm-0.17.0-5c42088cec-18d1e66cad.zip and /dev/null differ diff --git a/.yarn/cache/scheduler-npm-0.25.0-rc-603e6108-20241029-47acf774fb-c24fb37561.zip b/.yarn/cache/scheduler-npm-0.25.0-rc-603e6108-20241029-47acf774fb-c24fb37561.zip new file mode 100644 index 000000000..2802bfd87 Binary files /dev/null and b/.yarn/cache/scheduler-npm-0.25.0-rc-603e6108-20241029-47acf774fb-c24fb37561.zip differ diff --git a/.yarn/cache/unicode-script-npm-1.2.0-89c647c38e-8081850e75.zip b/.yarn/cache/unicode-script-npm-1.2.0-89c647c38e-8081850e75.zip new file mode 100644 index 000000000..f7d0c9ae2 Binary files /dev/null and b/.yarn/cache/unicode-script-npm-1.2.0-89c647c38e-8081850e75.zip differ diff --git a/.yarn/cache/yoga-layout-npm-2.0.1-d25883a49a-65a83b1bf0.zip b/.yarn/cache/yoga-layout-npm-2.0.1-d25883a49a-65a83b1bf0.zip deleted file mode 100644 index 87d7f3511..000000000 Binary files a/.yarn/cache/yoga-layout-npm-2.0.1-d25883a49a-65a83b1bf0.zip and /dev/null differ diff --git a/.yarn/cache/yoga-layout-npm-3.2.1-51ec934670-6d75e73f6b.zip b/.yarn/cache/yoga-layout-npm-3.2.1-51ec934670-6d75e73f6b.zip new file mode 100644 index 000000000..6f4131639 Binary files /dev/null and b/.yarn/cache/yoga-layout-npm-3.2.1-51ec934670-6d75e73f6b.zip differ diff --git a/packages/web/package.json b/packages/web/package.json index 24867de4b..a37261219 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -116,9 +116,10 @@ "@lexical/rich-text": "0.38.1", "@lexical/utils": "0.38.1", "@radix-ui/react-slot": "^1.0.1", - "@react-pdf/renderer": "^3.3.2", + "@react-pdf/renderer": "^4.3.0", "comlink": "^4.4.1", "fast-diff": "^1.3.0", - "lexical": "0.38.1" + "lexical": "0.38.1", + "unicode-script": "^1.2.0" } } diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/FontConfig.ts b/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/FontConfig.ts new file mode 100644 index 000000000..7c3011071 --- /dev/null +++ b/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/FontConfig.ts @@ -0,0 +1,287 @@ +import { Font } from '@react-pdf/renderer' +import { LexicalNode } from 'lexical' +// @ts-expect-error No typing for this package +import { unicodeScripts } from 'unicode-script' + +enum UnicodeScript { + Latin = 'Latin', + Common = 'Common', + Cyrillic = 'Cyrillic', + Greek = 'Greek', + Hebrew = 'Hebrew', + Arabic = 'Arabic', + Devanagari = 'Devanagari', + Bengali = 'Bengali', + Tamil = 'Tamil', + Telugu = 'Telugu', + Gujarati = 'Gujarati', + Gurmukhi = 'Gurmukhi', + Malayalam = 'Malayalam', + Sinhala = 'Sinhala', + Thai = 'Thai', + Armenian = 'Armenian', + Georgian = 'Georgian', + Ethiopic = 'Ethiopic', + Myanmar = 'Myanmar', + Khmer = 'Khmer', + Lao = 'Lao', + Tibetan = 'Tibetan', + Vietnamese = 'Vietnamese', + Chinese = 'Chinese', + Han = 'Han', + Japanese = 'Japanese', + Korean = 'Korean', + Hangul = 'Hangul', +} + +export enum FontFamily { + NotoSans = 'Noto Sans', + NotoSansHebrew = 'Noto Sans Hebrew', + NotoSansArabic = 'Noto Sans Arabic', + NotoSansDevanagari = 'Noto Sans Devanagari', + NotoSansBengali = 'Noto Sans Bengali', + NotoSansTamil = 'Noto Sans Tamil', + NotoSansTelugu = 'Noto Sans Telugu', + NotoSansGujarati = 'Noto Sans Gujarati', + NotoSansGurmukhi = 'Noto Sans Gurmukhi', + NotoSansMalayalam = 'Noto Sans Malayalam', + NotoSansSinhala = 'Noto Sans Sinhala', + NotoSansThai = 'Noto Sans Thai', + NotoSansArmenian = 'Noto Sans Armenian', + NotoSansGeorgian = 'Noto Sans Georgian', + NotoSansEthiopic = 'Noto Sans Ethiopic', + NotoSansMyanmar = 'Noto Sans Myanmar', + NotoSansKhmer = 'Noto Sans Khmer', + NotoSansLao = 'Noto Sans Lao', + NotoSansTibetan = 'Noto Sans Tibetan', + NotoSansSC = 'Noto Sans SC', + NotoSansJP = 'Noto Sans JP', + NotoSansKR = 'Noto Sans KR', + Courier = 'Courier', + Helvetica = 'Helvetica', +} + +enum FontVariant { + Normal = 'normal', + Bold = 'bold', + Italic = 'italic', + BoldItalic = 'bolditalic', +} + +type FontWeight = 'normal' | 'bold' +type FontStyle = 'normal' | 'italic' + +const FONT_VARIANT_TO_FONT_OPTIONS: Record = { + [FontVariant.Normal]: { + fontWeight: 'normal', + fontStyle: 'normal', + }, + [FontVariant.Bold]: { + fontWeight: 'bold', + fontStyle: 'normal', + }, + [FontVariant.Italic]: { + fontWeight: 'normal', + fontStyle: 'italic', + }, + [FontVariant.BoldItalic]: { + fontWeight: 'bold', + fontStyle: 'italic', + }, +} + +export const FALLBACK_FONT_SOURCE = '/noto-sans/NotoSans-Regular.ttf' + +export const FONT_ASSETS_BASE_PATH = + process.env.NODE_ENV === 'development' + ? 'http://localhost:3001/assets/fonts' + : 'https://assets.standardnotes.com/fonts' +export const FALLBACK_FONT_FAMILY = FontFamily.Helvetica +export const MONOSPACE_FONT_FAMILY = FontFamily.Courier + +const FONT_FAMILY_TO_FONT_SOURCES: Partial>>> = { + [FontFamily.NotoSans]: { + [FontVariant.Normal]: '/noto-sans/NotoSans-Regular.ttf', + [FontVariant.Bold]: '/noto-sans/NotoSans-Bold.ttf', + [FontVariant.Italic]: '/noto-sans/NotoSans-Italic.ttf', + [FontVariant.BoldItalic]: '/noto-sans/NotoSans-BoldItalic.ttf', + }, + [FontFamily.NotoSansHebrew]: { + [FontVariant.Normal]: '/noto-sans-hebrew/NotoSansHebrew-Regular.ttf', + [FontVariant.Bold]: '/noto-sans-hebrew/NotoSansHebrew-Bold.ttf', + }, + [FontFamily.NotoSansArabic]: { + [FontVariant.Normal]: '/noto-sans-arabic/NotoSansArabic-Regular.ttf', + [FontVariant.Bold]: '/noto-sans-arabic/NotoSansArabic-Bold.ttf', + }, + [FontFamily.NotoSansDevanagari]: { + [FontVariant.Normal]: '/noto-sans-devanagari/NotoSansDevanagari-Regular.ttf', + [FontVariant.Bold]: '/noto-sans-devanagari/NotoSansDevanagari-Bold.ttf', + }, + [FontFamily.NotoSansBengali]: { + [FontVariant.Normal]: '/noto-sans-bengali/NotoSansBengali-Regular.ttf', + }, + [FontFamily.NotoSansTamil]: { + [FontVariant.Normal]: '/noto-sans-tamil/NotoSansTamil-Regular.ttf', + }, + [FontFamily.NotoSansTelugu]: { + [FontVariant.Normal]: '/noto-sans-telugu/NotoSansTelugu-Regular.ttf', + }, + [FontFamily.NotoSansGujarati]: { + [FontVariant.Normal]: '/noto-sans-gujarati/NotoSansGujarati-Regular.ttf', + }, + [FontFamily.NotoSansGurmukhi]: { + [FontVariant.Normal]: '/noto-sans-gurmukhi/NotoSansGurmukhi-Regular.ttf', + }, + [FontFamily.NotoSansMalayalam]: { + [FontVariant.Normal]: '/noto-sans-malayalam/NotoSansMalayalam-Regular.ttf', + }, + [FontFamily.NotoSansSinhala]: { + [FontVariant.Normal]: '/noto-sans-sinhala/NotoSansSinhala-Regular.ttf', + }, + [FontFamily.NotoSansThai]: { + [FontVariant.Normal]: '/noto-sans-thai/NotoSansThai-Regular.ttf', + }, + [FontFamily.NotoSansArmenian]: { + [FontVariant.Normal]: '/noto-sans-armenian/NotoSansArmenian-Regular.ttf', + }, + [FontFamily.NotoSansGeorgian]: { + [FontVariant.Normal]: '/noto-sans-georgian/NotoSansGeorgian-Regular.ttf', + }, + [FontFamily.NotoSansEthiopic]: { + [FontVariant.Normal]: '/noto-sans-ethiopic/NotoSansEthiopic-Regular.ttf', + }, + [FontFamily.NotoSansMyanmar]: { + [FontVariant.Normal]: '/noto-sans-myanmar/NotoSansMyanmar-Regular.ttf', + }, + [FontFamily.NotoSansKhmer]: { + [FontVariant.Normal]: '/noto-sans-khmer/NotoSansKhmer-Regular.ttf', + }, + [FontFamily.NotoSansLao]: { + [FontVariant.Normal]: '/noto-sans-lao/NotoSansLao-Regular.ttf', + }, + [FontFamily.NotoSansTibetan]: { + [FontVariant.Normal]: '/noto-sans-tibetan/NotoSansTibetan-Regular.ttf', + }, + [FontFamily.NotoSansSC]: { + [FontVariant.Normal]: '/noto-sans-sc/NotoSansSC-Regular.ttf', + }, + [FontFamily.NotoSansJP]: { + [FontVariant.Normal]: '/noto-sans-jp/NotoSansJP-Regular.ttf', + }, + [FontFamily.NotoSansKR]: { + [FontVariant.Normal]: '/noto-sans-kr/NotoSansKR-Regular.ttf', + }, +} + +export const getFontFamilyForUnicodeScript = (script: UnicodeScript): FontFamily => { + switch (script) { + case UnicodeScript.Common: + case UnicodeScript.Latin: + case UnicodeScript.Cyrillic: + case UnicodeScript.Greek: + case UnicodeScript.Vietnamese: + return FontFamily.NotoSans + + case UnicodeScript.Hebrew: + return FontFamily.NotoSansHebrew + + case UnicodeScript.Arabic: + return FontFamily.NotoSansArabic + + case UnicodeScript.Devanagari: + return FontFamily.NotoSansDevanagari + + case UnicodeScript.Bengali: + return FontFamily.NotoSansBengali + + case UnicodeScript.Tamil: + return FontFamily.NotoSansTamil + + case UnicodeScript.Telugu: + return FontFamily.NotoSansTelugu + + case UnicodeScript.Gujarati: + return FontFamily.NotoSansGujarati + + case UnicodeScript.Gurmukhi: + return FontFamily.NotoSansGurmukhi + + case UnicodeScript.Malayalam: + return FontFamily.NotoSansMalayalam + + case UnicodeScript.Sinhala: + return FontFamily.NotoSansSinhala + + case UnicodeScript.Thai: + return FontFamily.NotoSansThai + + case UnicodeScript.Armenian: + return FontFamily.NotoSansArmenian + + case UnicodeScript.Georgian: + return FontFamily.NotoSansGeorgian + + case UnicodeScript.Ethiopic: + return FontFamily.NotoSansEthiopic + + case UnicodeScript.Myanmar: + return FontFamily.NotoSansMyanmar + + case UnicodeScript.Khmer: + return FontFamily.NotoSansKhmer + + case UnicodeScript.Lao: + return FontFamily.NotoSansLao + + case UnicodeScript.Tibetan: + return FontFamily.NotoSansTibetan + + case UnicodeScript.Chinese: + case UnicodeScript.Han: + return FontFamily.NotoSansSC + + case UnicodeScript.Japanese: + return FontFamily.NotoSansJP + + case UnicodeScript.Korean: + case UnicodeScript.Hangul: + return FontFamily.NotoSansKR + + default: + return FontFamily.NotoSans + } +} + +const getFontRegisterOptions = (fontFamily: FontFamily) => { + const fallback = FONT_FAMILY_TO_FONT_SOURCES[fontFamily]?.[FontVariant.Normal] ?? FALLBACK_FONT_SOURCE + + return { + family: fontFamily, + fonts: Object.entries(FONT_VARIANT_TO_FONT_OPTIONS).map(([variant, fontOptions]) => ({ + ...fontOptions, + src: `${FONT_ASSETS_BASE_PATH}${FONT_FAMILY_TO_FONT_SOURCES[fontFamily]?.[variant as FontVariant] ?? fallback}`, + })), + } +} + +export const getFontFamiliesFromLexicalNode = (node: LexicalNode) => { + const scripts: UnicodeScript[] = Array.from(unicodeScripts(node.getTextContent())) + const fontFamilies = [FontFamily.NotoSans] + scripts.forEach((script) => { + const fontFamilyForScript = getFontFamilyForUnicodeScript(script) + if (!fontFamilies.includes(fontFamilyForScript)) { + fontFamilies.unshift(fontFamilyForScript) + } + }) + const fontFamiliesSet = new Set(fontFamilies) + return Array.from(fontFamiliesSet) +} + +export const registerPDFFonts = (fontFamilies: FontFamily[]) => { + const fontFamiliesToRegister = new Set(fontFamilies) + fontFamiliesToRegister.forEach((fontFamily) => { + const registerOptions = getFontRegisterOptions(fontFamily) + Font.register(registerOptions) + }) +} diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/PDFExport.tsx b/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/PDFExport.tsx index e677db936..4a6b78ab9 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/PDFExport.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/PDFExport.tsx @@ -24,6 +24,14 @@ import { $isCollapsibleTitleNode } from '../../../Plugins/CollapsiblePlugin/Coll import PDFWorker, { PDFDataNode, PDFWorkerInterface } from './PDFWorker.worker' import { wrap } from 'comlink' import { PrefKey, PrefValue } from '@standardnotes/snjs' +import { + FALLBACK_FONT_FAMILY, + FALLBACK_FONT_SOURCE, + FONT_ASSETS_BASE_PATH, + FontFamily, + MONOSPACE_FONT_FAMILY, + getFontFamiliesFromLexicalNode, +} from './FontConfig' const styles = StyleSheet.create({ page: { @@ -144,6 +152,12 @@ const getFontSizeForHeading = (heading: HeadingNode) => { } const getNodeTextAlignment = (node: ElementNode) => { + const direction = node.getDirection() + + if (direction === 'rtl') { + return 'right' + } + const formatType = node.getFormatType() if (!formatType) { @@ -161,7 +175,16 @@ const getNodeTextAlignment = (node: ElementNode) => { return formatType } -const getPDFDataNodeFromLexicalNode = (node: LexicalNode): PDFDataNode => { +const getNodeDirection = (node: ElementNode) => { + const direction = node.getDirection() + return direction ?? 'ltr' +} + +const getPDFDataNodeFromLexicalNode = ( + node: LexicalNode, + fontFamilies: FontFamily[], + useCustomFonts: boolean = false, +): PDFDataNode => { const parent = node.getParent() if ($isLineBreakNode(node)) { @@ -177,15 +200,15 @@ const getPDFDataNodeFromLexicalNode = (node: LexicalNode): PDFDataNode => { const isBold = node.hasFormat('bold') const isItalic = node.hasFormat('italic') const isHighlight = node.hasFormat('highlight') + let fontFamily: FontFamily[] | FontFamily = FALLBACK_FONT_FAMILY - let font = isInlineCode || isCodeNodeText ? 'Courier' : 'Helvetica' - if (isBold || isItalic) { - font += '-' - if (isBold) { - font += 'Bold' - } - if (isItalic) { - font += 'Oblique' + if (isInlineCode && isCodeNodeText) { + fontFamily = MONOSPACE_FONT_FAMILY + } else { + if (useCustomFonts) { + const nodeFontFamilies = getFontFamiliesFromLexicalNode(node) + fontFamily = [...nodeFontFamilies, FALLBACK_FONT_FAMILY] + fontFamilies.push(...nodeFontFamilies) } } @@ -193,7 +216,10 @@ const getPDFDataNodeFromLexicalNode = (node: LexicalNode): PDFDataNode => { type: 'Text', children: node.getTextContent(), style: { - fontFamily: font, + fontFamily, + fontWeight: isBold ? 'bold' : 'normal', + fontStyle: isItalic ? 'italic' : 'normal', + direction: $isElementNode(parent) ? getNodeDirection(parent) : 'ltr', textDecoration: node.hasFormat('underline') ? 'underline' : node.hasFormat('strikethrough') @@ -237,7 +263,7 @@ const getPDFDataNodeFromLexicalNode = (node: LexicalNode): PDFDataNode => { type: 'View', style: [styles.row, styles.wrap], children: line.map((child) => { - return getPDFDataNodeFromLexicalNode(child) + return getPDFDataNodeFromLexicalNode(child, fontFamilies, useCustomFonts) }), } }), @@ -267,7 +293,7 @@ const getPDFDataNodeFromLexicalNode = (node: LexicalNode): PDFDataNode => { const children = $isElementNode(node) || $isTableNode(node) || $isTableCellNode(node) || $isTableRowNode(node) ? node.getChildren().map((child) => { - return getPDFDataNodeFromLexicalNode(child) + return getPDFDataNodeFromLexicalNode(child, fontFamilies, useCustomFonts) }) : undefined @@ -427,28 +453,52 @@ const getPDFDataNodeFromLexicalNode = (node: LexicalNode): PDFDataNode => { } } -const getPDFDataNodesFromLexicalNodes = (nodes: LexicalNode[]): PDFDataNode[] => { - return nodes.map(getPDFDataNodeFromLexicalNode) +const getPDFDataNodesFromLexicalNodes = ( + nodes: LexicalNode[], + fontFamilies: FontFamily[], + useCustomFonts: boolean, +): PDFDataNode[] => { + return nodes.map((node) => getPDFDataNodeFromLexicalNode(node, fontFamilies, useCustomFonts)) } const pdfWorker = new PDFWorker() const PDFWorkerComlink = wrap(pdfWorker) +const shouldUseCustomFonts = async () => { + try { + const response = await fetch(`${FONT_ASSETS_BASE_PATH}${FALLBACK_FONT_SOURCE}`, { method: 'HEAD' }) + return response.ok + } catch { + return false + } +} + /** * @returns The PDF as an object url */ export function $generatePDFFromNodes(editor: LexicalEditor, pageSize: PrefValue[PrefKey.SuperNoteExportPDFPageSize]) { - return new Promise((resolve) => { - editor.getEditorState().read(() => { - const root = $getRoot() - const nodes = root.getChildren() + return new Promise((resolve, reject) => { + shouldUseCustomFonts() + .then((useCustomFonts) => { + editor.getEditorState().read(() => { + const root = $getRoot() + const nodes = root.getChildren() + const fontFamilies: FontFamily[] = [] - const pdfDataNodes = getPDFDataNodesFromLexicalNodes(nodes) + const pdfDataNodes = getPDFDataNodesFromLexicalNodes(nodes, fontFamilies, useCustomFonts) - void PDFWorkerComlink.renderPDF(pdfDataNodes, pageSize).then((blob) => { - const url = URL.createObjectURL(blob) - resolve(url) + void PDFWorkerComlink.renderPDF(pdfDataNodes, pageSize, fontFamilies, useCustomFonts) + .then((blob) => { + const url = URL.createObjectURL(blob) + resolve(url) + }) + .catch((error) => { + reject(error) + }) + }) + }) + .catch((error) => { + reject(error) }) - }) }) } diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/PDFWorker.worker.tsx b/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/PDFWorker.worker.tsx index 6b130c84f..f35b974b8 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/PDFWorker.worker.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Lexical/Utils/PDFExport/PDFWorker.worker.tsx @@ -17,6 +17,7 @@ import { PageProps, } from '@react-pdf/renderer' import { expose } from 'comlink' +import { FontFamily, registerPDFFonts } from './FontConfig' export type PDFDataNode = | (( @@ -94,7 +95,15 @@ const PDFDocument = ({ nodes, pageSize }: { nodes: PDFDataNode[]; pageSize: Page ) } -const renderPDF = (nodes: PDFDataNode[], pageSize: PageProps['size']) => { +const renderPDF = ( + nodes: PDFDataNode[], + pageSize: PageProps['size'], + fontFamilies: FontFamily[], + useCustomFonts: boolean = false, +) => { + if (useCustomFonts) { + registerPDFFonts(fontFamilies) + } return pdf().toBlob() } diff --git a/packages/web/src/javascripts/Components/SuperEditor/Tools/HeadlessSuperConverter.tsx b/packages/web/src/javascripts/Components/SuperEditor/Tools/HeadlessSuperConverter.tsx index 16013d1c4..97b1ce578 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Tools/HeadlessSuperConverter.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Tools/HeadlessSuperConverter.tsx @@ -64,6 +64,7 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface { } }, ): Promise { + let didThrow = false if (superString.length === 0) { return superString } @@ -81,7 +82,7 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface { let content: string | undefined - await new Promise((resolve) => { + await new Promise((resolve, reject) => { const handleFileNodes = () => { if (embedBehavior === 'reference') { resolve() @@ -136,12 +137,16 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface { }), ) .then(() => resolve()) - .catch(console.error) + .catch((error) => { + didThrow = true + console.error(error) + reject(error) + }) } this.exportEditor.update(handleFileNodes, { discrete: true }) }) - await new Promise((resolve) => { + await new Promise((resolve, reject) => { const convertToFormat = () => { switch (toFormat) { case 'txt': @@ -164,10 +169,16 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface { break case 'pdf': { void import('../Lexical/Utils/PDFExport/PDFExport').then(({ $generatePDFFromNodes }): void => { - void $generatePDFFromNodes(this.exportEditor, config?.pdf?.pageSize || 'A4').then((pdf) => { - content = pdf - resolve() - }) + void $generatePDFFromNodes(this.exportEditor, config?.pdf?.pageSize || 'A4') + .then((pdf) => { + content = pdf + resolve() + }) + .catch((error) => { + didThrow = true + console.error(error) + reject(error) + }) }) break } @@ -181,7 +192,7 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface { this.exportEditor.update(convertToFormat, { discrete: true }) }) - if (typeof content !== 'string') { + if (didThrow || typeof content !== 'string') { throw new Error('Could not export note') } diff --git a/packages/web/web.webpack.config.js b/packages/web/web.webpack.config.js index b6898c0f7..a2cf68aba 100644 --- a/packages/web/web.webpack.config.js +++ b/packages/web/web.webpack.config.js @@ -1,3 +1,4 @@ +/* eslint-disable */ const path = require('path') const webpack = require('webpack') const MiniCssExtractPlugin = require('mini-css-extract-plugin') diff --git a/yarn.lock b/yarn.lock index 3a2b2849d..8188c74ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6771,15 +6771,6 @@ __metadata: languageName: node linkType: hard -"@react-pdf/fns@npm:2.2.1": - version: 2.2.1 - resolution: "@react-pdf/fns@npm:2.2.1" - dependencies: - "@babel/runtime": ^7.20.13 - checksum: 738bc27e45ee3253e9abf74924696428ac856e679ac617b030936052da773996bec3d314ca9ad67c675d3eb82c8c74a9c24a3dbd0473b66773dbaefb1f202826 - languageName: node - linkType: hard - "@react-pdf/fns@npm:3.1.2": version: 3.1.2 resolution: "@react-pdf/fns@npm:3.1.2" @@ -6787,19 +6778,6 @@ __metadata: languageName: node linkType: hard -"@react-pdf/font@npm:^2.5.2": - version: 2.5.2 - resolution: "@react-pdf/font@npm:2.5.2" - dependencies: - "@babel/runtime": ^7.20.13 - "@react-pdf/types": ^2.6.0 - cross-fetch: ^3.1.5 - fontkit: ^2.0.2 - is-url: ^1.2.4 - checksum: 73d7753ea13fe4b4c1469bae181b7a7fee5fb39db829253428a2010c7f28e3c8982273b6a92f63059c8e19f36a7b99d0838943121bae1f2992a8f93d44165d40 - languageName: node - linkType: hard - "@react-pdf/font@npm:^4.0.3": version: 4.0.3 resolution: "@react-pdf/font@npm:4.0.3" @@ -6812,50 +6790,30 @@ __metadata: languageName: node linkType: hard -"@react-pdf/image@npm:^2.3.6": - version: 2.3.6 - resolution: "@react-pdf/image@npm:2.3.6" +"@react-pdf/image@npm:^3.0.3": + version: 3.0.3 + resolution: "@react-pdf/image@npm:3.0.3" dependencies: - "@babel/runtime": ^7.20.13 - "@react-pdf/png-js": ^2.3.1 - cross-fetch: ^3.1.5 - jay-peg: ^1.0.2 - checksum: 177e221e61714d7efd6c7c7c1f78b7dc9b343eaf6fd7d11f22ebd0830114f4188fb0be5b6dab71e734f8e9aa0f6ca0cad946011b2872f771526ab75c14a5a039 + "@react-pdf/png-js": ^3.0.0 + jay-peg: ^1.1.1 + checksum: 893ebef74d62d9d163af7035401c2bd0c5e43ceb7d6b9cc7e50d3ce2a2e7af7888b98e83e713a655ad6be5b0f246a1ac8a773a679ef30aaaac5b2579f0f8712f languageName: node linkType: hard -"@react-pdf/layout@npm:^3.13.0": - version: 3.13.0 - resolution: "@react-pdf/layout@npm:3.13.0" +"@react-pdf/layout@npm:^4.4.1": + version: 4.4.1 + resolution: "@react-pdf/layout@npm:4.4.1" dependencies: - "@babel/runtime": ^7.20.13 - "@react-pdf/fns": 2.2.1 - "@react-pdf/image": ^2.3.6 - "@react-pdf/pdfkit": ^3.2.0 - "@react-pdf/primitives": ^3.1.1 - "@react-pdf/stylesheet": ^4.3.0 - "@react-pdf/textkit": ^4.4.1 - "@react-pdf/types": ^2.6.0 - cross-fetch: ^3.1.5 - emoji-regex: ^10.3.0 + "@react-pdf/fns": 3.1.2 + "@react-pdf/image": ^3.0.3 + "@react-pdf/primitives": ^4.1.1 + "@react-pdf/stylesheet": ^6.1.1 + "@react-pdf/textkit": ^6.0.0 + "@react-pdf/types": ^2.9.1 + emoji-regex-xs: ^1.0.0 queue: ^6.0.1 - yoga-layout: ^2.0.1 - checksum: d4014cc860292d0aba6b2d174647afa5003067bb3e98b53f983cb2d32e397514f77a030b53341a082e2682f257badac98792156e0483303a7df79be5e414eaab - languageName: node - linkType: hard - -"@react-pdf/pdfkit@npm:^3.2.0": - version: 3.2.0 - resolution: "@react-pdf/pdfkit@npm:3.2.0" - dependencies: - "@babel/runtime": ^7.20.13 - "@react-pdf/png-js": ^2.3.1 - browserify-zlib: ^0.2.0 - crypto-js: ^4.2.0 - fontkit: ^2.0.2 - jay-peg: ^1.0.2 - vite-compatible-readable-stream: ^3.6.1 - checksum: 2c95c9dbc332d21bae45d3480c25a062450d9598ded63a11418e373831526cb8b1986ea9d0371a75a571a4acc3a4b3832b49364e1ceb036d22cb8617a1f56019 + yoga-layout: ^3.2.1 + checksum: 5d7baa7f92cf569aa795af4b61690e2afa0e584481d983a50edc32462b8a909ad574f67c5ceca89219d74fa2becbfa317a43fd5190eb66b513e05830d0eea56f languageName: node linkType: hard @@ -6875,15 +6833,6 @@ __metadata: languageName: node linkType: hard -"@react-pdf/png-js@npm:^2.3.1": - version: 2.3.1 - resolution: "@react-pdf/png-js@npm:2.3.1" - dependencies: - browserify-zlib: ^0.2.0 - checksum: bab00c8380cb1f2126c2844568929fe04753ee8611d9d9d0f6f9d5d4fdc3c8aafd265297d354941610b9e2fadcafca6e1cd2f36b137f2847e35985d226d056bc - languageName: node - linkType: hard - "@react-pdf/png-js@npm:^3.0.0": version: 3.0.0 resolution: "@react-pdf/png-js@npm:3.0.0" @@ -6893,13 +6842,6 @@ __metadata: languageName: node linkType: hard -"@react-pdf/primitives@npm:^3.1.1": - version: 3.1.1 - resolution: "@react-pdf/primitives@npm:3.1.1" - checksum: a52c0cfff74d29d36e2e4c1c2b8935faf2f13bbe3800901e93354ea044385d8716166e45f3a49bb729e6d9944d7a8239056f5af80b345cb2984e245b2e719c1d - languageName: node - linkType: hard - "@react-pdf/primitives@npm:^4.1.1": version: 4.1.1 resolution: "@react-pdf/primitives@npm:4.1.1" @@ -6907,58 +6849,56 @@ __metadata: languageName: node linkType: hard -"@react-pdf/render@npm:^3.5.0": - version: 3.5.0 - resolution: "@react-pdf/render@npm:3.5.0" +"@react-pdf/reconciler@npm:^1.1.4": + version: 1.1.4 + resolution: "@react-pdf/reconciler@npm:1.1.4" + dependencies: + object-assign: ^4.1.1 + scheduler: 0.25.0-rc-603e6108-20241029 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: d920898a1c6bee70fb257aad1d53c062fedd9d2ad6ca6c63902bfc03c71713f4ab01a18cbfba55724ab7ca74985a2bc4774c51f690471182a7622a8a48056054 + languageName: node + linkType: hard + +"@react-pdf/render@npm:^4.3.1": + version: 4.3.1 + resolution: "@react-pdf/render@npm:4.3.1" dependencies: "@babel/runtime": ^7.20.13 - "@react-pdf/fns": 2.2.1 - "@react-pdf/primitives": ^3.1.1 - "@react-pdf/textkit": ^4.4.1 - "@react-pdf/types": ^2.6.0 + "@react-pdf/fns": 3.1.2 + "@react-pdf/primitives": ^4.1.1 + "@react-pdf/textkit": ^6.0.0 + "@react-pdf/types": ^2.9.1 abs-svg-path: ^0.1.1 color-string: ^1.9.1 normalize-svg-path: ^1.1.0 parse-svg-path: ^0.1.2 svg-arc-to-cubic-bezier: ^3.2.0 - checksum: aac990fcac1d1b3885c119d83955a3d42aa6548156a9c9660ba72c85a208d1ddae1b19290ed06194499460c8f93564e5b894a9a05a5850a83c72a3e7e55575cf + checksum: 2dfea722c1251030868e5c0ac6fd83179d01027f1cea3848cc637c3f98aa6144eb0672211cdce6a13bd89d8b8dbeaf59b9d3d85ae91abe19ca615e1bfb8e5fcb languageName: node linkType: hard -"@react-pdf/renderer@npm:^3.3.2": - version: 3.4.5 - resolution: "@react-pdf/renderer@npm:3.4.5" +"@react-pdf/renderer@npm:^4.3.0": + version: 4.3.1 + resolution: "@react-pdf/renderer@npm:4.3.1" dependencies: "@babel/runtime": ^7.20.13 - "@react-pdf/font": ^2.5.2 - "@react-pdf/layout": ^3.13.0 - "@react-pdf/pdfkit": ^3.2.0 - "@react-pdf/primitives": ^3.1.1 - "@react-pdf/render": ^3.5.0 - "@react-pdf/types": ^2.6.0 + "@react-pdf/fns": 3.1.2 + "@react-pdf/font": ^4.0.3 + "@react-pdf/layout": ^4.4.1 + "@react-pdf/pdfkit": ^4.0.4 + "@react-pdf/primitives": ^4.1.1 + "@react-pdf/reconciler": ^1.1.4 + "@react-pdf/render": ^4.3.1 + "@react-pdf/types": ^2.9.1 events: ^3.3.0 object-assign: ^4.1.1 prop-types: ^15.6.2 queue: ^6.0.1 - scheduler: ^0.17.0 peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: f3e9a6779654de14fd755352d22cd0099c9f78317c8a2771b62e8d9757932a4bff47b59fdde390d46bf8515e5509694a3e32a055733a1d35eab0d979a173635d - languageName: node - linkType: hard - -"@react-pdf/stylesheet@npm:^4.3.0": - version: 4.3.0 - resolution: "@react-pdf/stylesheet@npm:4.3.0" - dependencies: - "@babel/runtime": ^7.20.13 - "@react-pdf/fns": 2.2.1 - "@react-pdf/types": ^2.6.0 - color-string: ^1.9.1 - hsl-to-hex: ^1.0.0 - media-engine: ^1.0.3 - postcss-value-parser: ^4.1.0 - checksum: 9a32aca88efdb1967fac66013607c79592e9863ded756c373da408840113411b20b16d8c1d0fe22c460f812ddaa9c03abb721b44c870ae1c171e4f4f81b45cec + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 0e9e2d9b30fd238f9bebe3d2409496b6ea40c45f9960f7f72efd21cde9d8eda7fe835eeca0faf30511abc66cc62c8862f3114642901b1bce92fde89bc19d8516 languageName: node linkType: hard @@ -6976,20 +6916,19 @@ __metadata: languageName: node linkType: hard -"@react-pdf/textkit@npm:^4.4.1": - version: 4.4.1 - resolution: "@react-pdf/textkit@npm:4.4.1" +"@react-pdf/textkit@npm:^6.0.0": + version: 6.0.0 + resolution: "@react-pdf/textkit@npm:6.0.0" dependencies: - "@babel/runtime": ^7.20.13 - "@react-pdf/fns": 2.2.1 + "@react-pdf/fns": 3.1.2 bidi-js: ^1.0.2 hyphen: ^1.6.4 unicode-properties: ^1.4.1 - checksum: e07b7574bab13999a2859dd6e8ddbbe3650ae63736c77ea36fc7f355d743a8954bfa8e59f43f8d0ecd5379e239a64882d0b88eb47a654019fb7575df0a235510 + checksum: 5bc087ad2da60d1c3c3a06f02639adaca3d059f526ee9ec79fc7877ef224813ec2eef8f064b36872b5a6e659c510813abe0d89b6fecc0c014833dd5437dc7ef9 languageName: node linkType: hard -"@react-pdf/types@npm:^2.6.0, @react-pdf/types@npm:^2.9.1": +"@react-pdf/types@npm:^2.9.1": version: 2.9.1 resolution: "@react-pdf/types@npm:2.9.1" dependencies: @@ -8927,7 +8866,7 @@ __metadata: "@lexical/utils": 0.38.1 "@pmmmwh/react-refresh-webpack-plugin": ^0.5.10 "@radix-ui/react-slot": ^1.0.1 - "@react-pdf/renderer": ^3.3.2 + "@react-pdf/renderer": ^4.3.0 "@simplewebauthn/browser": ^8.0.2 "@standardnotes/authenticator": ^2.4.0 "@standardnotes/autobiography-theme": ^1.2.7 @@ -8998,6 +8937,7 @@ __metadata: ts-jest: ^29.0.3 ts-loader: ^9.4.2 typescript: "*" + unicode-script: ^1.2.0 webextension-polyfill: ^0.10.0 webpack: "*" webpack-dev-server: "*" @@ -13821,15 +13761,6 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:^3.1.5": - version: 3.2.0 - resolution: "cross-fetch@npm:3.2.0" - dependencies: - node-fetch: ^2.7.0 - checksum: 8ded5ea35f705e81e569e7db244a3f96e05e95996ff51877c89b0c1ec1163c76bb5dad77d0f8fba6bb35a0abacb36403d7271dc586d8b1f636110ee7a8d959fd - languageName: node - linkType: hard - "cross-fetch@npm:^4.0.0": version: 4.0.0 resolution: "cross-fetch@npm:4.0.0" @@ -14947,10 +14878,10 @@ __metadata: languageName: node linkType: hard -"emoji-regex@npm:^10.3.0": - version: 10.3.0 - resolution: "emoji-regex@npm:10.3.0" - checksum: 5da48edfeb9462fb1ae5495cff2d79129974c696853fb0ce952cbf560f29a2756825433bf51cfd5157ec7b9f93f46f31d712e896d63e3d8ac9c3832bdb45ab73 +"emoji-regex-xs@npm:^1.0.0": + version: 1.0.0 + resolution: "emoji-regex-xs@npm:1.0.0" + checksum: c33be159da769836f83281f2802d90169093ebf3c2c1643d6801d891c53beac5ef785fd8279f9b02fa6dc6c47c367818e076949f1e13bd1b3f921b416de4cbea languageName: node linkType: hard @@ -18993,7 +18924,7 @@ __metadata: languageName: node linkType: hard -"jay-peg@npm:^1.0.2, jay-peg@npm:^1.1.1": +"jay-peg@npm:^1.1.1": version: 1.1.1 resolution: "jay-peg@npm:1.1.1" dependencies: @@ -22053,7 +21984,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.12, node-fetch@npm:^2.7.0": +"node-fetch@npm:^2.6.12": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -25512,13 +25443,10 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.17.0": - version: 0.17.0 - resolution: "scheduler@npm:0.17.0" - dependencies: - loose-envify: ^1.1.0 - object-assign: ^4.1.1 - checksum: 18d1e66cad3d26e3becd99b006d0744cda3556dbb356fc5b30df6d5499c85a308d18ee55353e01595f7c047b526564603ea80ef3d927a325faedc53ede03680c +"scheduler@npm:0.25.0-rc-603e6108-20241029": + version: 0.25.0-rc-603e6108-20241029 + resolution: "scheduler@npm:0.25.0-rc-603e6108-20241029" + checksum: c24fb37561cf73c54177f47fa0e92c95f8555eaf25d42d0cd2c4280058c8a2bf57b0f68f179bf766178ce6b6ea8c27b9a0cf0832bb3c6cd4ed3a15174dadaf04 languageName: node linkType: hard @@ -28063,6 +27991,13 @@ __metadata: languageName: node linkType: hard +"unicode-script@npm:^1.2.0": + version: 1.2.0 + resolution: "unicode-script@npm:1.2.0" + checksum: 8081850e75bfc858d718a64520286e2ca77c1ffa90808405c98febcd9ebfade660af28c1c18a90a9007205531ba960f7429646eff5e0307fb44d5876b97bc9ed + languageName: node + linkType: hard + "unicode-trie@npm:^2.0.0": version: 2.0.0 resolution: "unicode-trie@npm:2.0.0" @@ -29365,10 +29300,10 @@ __metadata: languageName: node linkType: hard -"yoga-layout@npm:^2.0.1": - version: 2.0.1 - resolution: "yoga-layout@npm:2.0.1" - checksum: 65a83b1bf019dcb506c1b10cb0c278718b8eb9ef07c2967c1c8c66c2ce8b3edf44028fbc75e05c5b6492060add7e8e742da22b4afbb44ab199f7bae76114a92a +"yoga-layout@npm:^3.2.1": + version: 3.2.1 + resolution: "yoga-layout@npm:3.2.1" + checksum: 6d75e73f6b044414def48d2bcc05b0bbc44f9d21e2dd0e2df696edddb76ea2c7fa6a2821069152bf5bfeeadd86494847a918c25dd08881f911f7915638f2fc39 languageName: node linkType: hard