mirror of
https://github.com/vxcontrol/pentagi.git
synced 2026-09-25 03:35:34 +00:00
perf(frontend): lazy-register CJK font in PDF generator
Skips loading NotoSansSC.otf (8.1 MB) for reports without CJK chars. Measured win on flow #50 download: 22.6 MB → 13.8 MB total traffic. Adds `u` flag to CJK regexes so emoji surrogate pairs don't trigger false positives (was matching U+D83D high surrogate on flow #41). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
adddb45158
commit
02ef55cb30
@@ -3,40 +3,63 @@ import { marked } from 'marked';
|
||||
|
||||
import { Log } from '@/lib/log';
|
||||
|
||||
// Register Noto Sans (covers Latin + Cyrillic + Greek + many other scripts)
|
||||
Font.register({
|
||||
family: 'NotoSans',
|
||||
fonts: [
|
||||
{ fontStyle: 'normal', fontWeight: 'normal', src: '/fonts/NotoSans-Regular.ttf' },
|
||||
{ fontStyle: 'normal', fontWeight: 'bold', src: '/fonts/NotoSans-Bold.ttf' },
|
||||
{ fontStyle: 'italic', fontWeight: 'normal', src: '/fonts/NotoSans-Italic.ttf' },
|
||||
{ fontStyle: 'italic', fontWeight: 'bold', src: '/fonts/NotoSans-BoldItalic.ttf' },
|
||||
],
|
||||
});
|
||||
const CJK_RE = /[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u3000-\u303F\uFF01-\uFF60\uFFE0-\uFFE6]+/gu;
|
||||
const HAS_CJK_RE = /[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u3000-\u303F\uFF01-\uFF60\uFFE0-\uFFE6]/u;
|
||||
|
||||
// Register Noto Sans Mono (covers Latin + Cyrillic for code blocks)
|
||||
Font.register({
|
||||
family: 'NotoSansMono',
|
||||
fonts: [
|
||||
{ fontStyle: 'normal', fontWeight: 'normal', src: '/fonts/NotoSansMono-Regular.ttf' },
|
||||
{ fontStyle: 'normal', fontWeight: 'bold', src: '/fonts/NotoSansMono-Bold.ttf' },
|
||||
],
|
||||
});
|
||||
let hasBaseFonts = false;
|
||||
let hasCJKFonts = false;
|
||||
|
||||
// Register Noto Sans SC (Simplified Chinese, covers CJK + Latin)
|
||||
Font.register({
|
||||
family: 'NotoSansSC',
|
||||
fonts: [
|
||||
{ fontStyle: 'normal', fontWeight: 'normal', src: '/fonts/NotoSansSC-Regular.otf' },
|
||||
{ fontStyle: 'normal', fontWeight: 'bold', src: '/fonts/NotoSansSC-Bold.otf' },
|
||||
],
|
||||
});
|
||||
const registerBaseFonts = (): void => {
|
||||
if (hasBaseFonts) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable word hyphenation (breaks CJK and Cyrillic incorrectly)
|
||||
Font.registerHyphenationCallback((word) => [word]);
|
||||
Font.register({
|
||||
family: 'NotoSans',
|
||||
fonts: [
|
||||
{ fontStyle: 'normal', fontWeight: 'normal', src: '/fonts/NotoSans-Regular.ttf' },
|
||||
{ fontStyle: 'normal', fontWeight: 'bold', src: '/fonts/NotoSans-Bold.ttf' },
|
||||
{ fontStyle: 'italic', fontWeight: 'normal', src: '/fonts/NotoSans-Italic.ttf' },
|
||||
{ fontStyle: 'italic', fontWeight: 'bold', src: '/fonts/NotoSans-BoldItalic.ttf' },
|
||||
],
|
||||
});
|
||||
|
||||
// Regex that matches any CJK unified ideographs or CJK punctuation/fullwidth chars
|
||||
const CJK_RE = /[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u3000-\u303F\uFF01-\uFF60\uFFE0-\uFFE6]+/g;
|
||||
Font.register({
|
||||
family: 'NotoSansMono',
|
||||
fonts: [
|
||||
{ fontStyle: 'normal', fontWeight: 'normal', src: '/fonts/NotoSansMono-Regular.ttf' },
|
||||
{ fontStyle: 'normal', fontWeight: 'bold', src: '/fonts/NotoSansMono-Bold.ttf' },
|
||||
],
|
||||
});
|
||||
|
||||
Font.registerHyphenationCallback((word) => [word]);
|
||||
|
||||
hasBaseFonts = true;
|
||||
};
|
||||
|
||||
const registerCJKFonts = (): void => {
|
||||
if (hasCJKFonts) {
|
||||
return;
|
||||
}
|
||||
|
||||
Font.register({
|
||||
family: 'NotoSansSC',
|
||||
fonts: [
|
||||
{ fontStyle: 'normal', fontWeight: 'normal', src: '/fonts/NotoSansSC-Regular.otf' },
|
||||
{ fontStyle: 'normal', fontWeight: 'bold', src: '/fonts/NotoSansSC-Bold.otf' },
|
||||
],
|
||||
});
|
||||
|
||||
hasCJKFonts = true;
|
||||
};
|
||||
|
||||
const ensureFonts = (content: string): void => {
|
||||
registerBaseFonts();
|
||||
|
||||
if (HAS_CJK_RE.test(content)) {
|
||||
registerCJKFonts();
|
||||
}
|
||||
};
|
||||
|
||||
interface TextSegment {
|
||||
isCJK: boolean;
|
||||
@@ -610,6 +633,8 @@ function PDFReportDocument({ content }: { content: string }) {
|
||||
|
||||
export const generatePDFFromMarkdownNew = async (content: string, fileName: string): Promise<void> => {
|
||||
try {
|
||||
ensureFonts(content);
|
||||
|
||||
const doc = <PDFReportDocument content={content} />;
|
||||
const blob = await pdf(doc).toBlob();
|
||||
|
||||
@@ -630,6 +655,8 @@ export const generatePDFFromMarkdownNew = async (content: string, fileName: stri
|
||||
|
||||
export const generatePDFBlobNew = async (content: string): Promise<Blob> => {
|
||||
try {
|
||||
ensureFonts(content);
|
||||
|
||||
const doc = <PDFReportDocument content={content} />;
|
||||
|
||||
return await pdf(doc).toBlob();
|
||||
|
||||
Reference in New Issue
Block a user