diff --git a/apps/web/index.html b/apps/web/index.html index 8805a8961..e793c168c 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -4,6 +4,13 @@ + + + + 小虾 SaaS - 自动化视频剪辑平台 diff --git a/apps/web/src/components/title/constants.ts b/apps/web/src/components/title/constants.ts index 2b039de02..7f4bd24c0 100644 --- a/apps/web/src/components/title/constants.ts +++ b/apps/web/src/components/title/constants.ts @@ -20,54 +20,84 @@ export const FONT_OPTIONS: FontOption[] = [ { value: "优设标题黑", label: "优设标题黑", + // 原版"优设标题黑"为商用字体,非开源;这里优先使用本地已安装字体,兜底用 ZCOOL QingKe HuangYou(站酷庆科黄油体,同为厚重黑体/海报风格,可从 Google Fonts 加载) family: - '"YouShe Title Black","YouSheBiaoTiHei","Source Han Sans SC Heavy","Noto Sans SC","PingFang SC",sans-serif', + '"YouSheBiaoTiHei","YouShe Title Black","ZCOOL QingKe HuangYou","Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif', tag: "hot", }, { value: "阿里普惠体Bold", label: "阿里普惠体Bold", + // 阿里普惠体需要从阿里官网下载;兜底用 Noto Sans SC 900(同等字重) family: - '"Alibaba PuHuiTi Bold","Alibaba PuHuiTi","Source Han Sans SC","PingFang SC",sans-serif', + '"Alibaba PuHuiTi","Alibaba PuHuiTi Bold","Alibaba Sans","Noto Sans SC",system-ui,"PingFang SC","Microsoft YaHei",sans-serif', tag: "hot", }, { value: "抖音美好体", label: "抖音美好体", - family: '"Douyin Sans","DouyinSans","Source Han Sans SC","PingFang SC",sans-serif', + // 抖音美好体版权字体兜底 ZCOOL KuaiLe(站酷快乐体,圆润卡通风格近似) + family: + '"Douyin Sans","DouyinSans","ZCOOL KuaiLe","Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif', tag: "hot", }, { value: "思源黑体Heavy", label: "思源黑体Heavy", family: - '"Source Han Sans SC Heavy","Noto Sans SC","Source Han Sans CN Heavy","PingFang SC",sans-serif', + '"Noto Sans SC","Source Han Sans SC","Source Han Sans CN Heavy","PingFang SC","Microsoft YaHei",sans-serif', tag: "new", }, { value: "思源黑体", label: "思源黑体", - family: '"Source Han Sans SC","Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif', + family: '"Noto Sans SC","Source Han Sans SC","PingFang SC","Microsoft YaHei",sans-serif', }, { value: "思源宋体", label: "思源宋体", - family: '"Source Han Serif SC","Noto Serif SC","Songti SC","SimSun",serif', + family: '"Noto Serif SC","Source Han Serif SC","Songti SC","SimSun",serif', }, { value: "苹方", label: "苹方", - family: '"PingFang SC",-apple-system,"Helvetica Neue",sans-serif', + family: + '"PingFang SC",-apple-system,blinkmacsystemfont,"Helvetica Neue","Noto Sans SC",sans-serif', }, { value: "微软雅黑", label: "微软雅黑", - family: '"Microsoft YaHei","PingFang SC",sans-serif', + family: '"Microsoft YaHei","PingFang SC","Noto Sans SC",sans-serif', }, { value: "楷体", label: "楷体", - family: '"KaiTi","STKaiti","DFKai-SB",serif', + family: '"KaiTi","STKaiti","DFKai-SB","Kaiti SC",serif', + }, + { + value: "站酷小薇体", + label: "站酷小薇体", + family: '"ZCOOL XiaoWei","Noto Serif SC",serif', + }, + { + value: "马善政毛笔", + label: "马善政毛笔", + family: '"Ma Shan Zheng","STXingkai","KaiTi",cursive', + }, + { + value: "龙藏体", + label: "龙藏体", + family: '"Long Cang","STXingkai","KaiTi",cursive', + }, + { + value: "流江毛笔草", + label: "流江毛笔草", + family: '"Liu Jian Mao Cao","STXingkai",cursive', + }, + { + value: "志莽行书", + label: "志莽行书", + family: '"Zhi Mang Xing","STXingkai",cursive', }, ] diff --git a/apps/web/src/pages/ai-avatar/components/PanelTitleConfig.tsx b/apps/web/src/pages/ai-avatar/components/PanelTitleConfig.tsx index c9168ab41..f02b66a5a 100644 --- a/apps/web/src/pages/ai-avatar/components/PanelTitleConfig.tsx +++ b/apps/web/src/pages/ai-avatar/components/PanelTitleConfig.tsx @@ -412,7 +412,7 @@ const PanelTitleConfig: React.FC = ({ titleConfig, onUpda onUpdateStyle={handleUpdateStyle} showCoverToggle previewWidth={280} - enableTemplates + enableTemplates={false} selectedTemplateId={selectedTemplateId} onApplyTemplate={handleApplyTemplate} activePreset={activePreset} diff --git a/apps/web/src/pages/generate/GeneratePage.tsx b/apps/web/src/pages/generate/GeneratePage.tsx index cc5b09c86..8cc5f4045 100644 --- a/apps/web/src/pages/generate/GeneratePage.tsx +++ b/apps/web/src/pages/generate/GeneratePage.tsx @@ -137,6 +137,9 @@ const GeneratePage: React.FC = () => { } }, [selectedVoice, isBatch, voiceModePerVideo, setVoiceLibraryIds]) + /* ── 标题面板模式:默认展示样式参数(false);需要大卡片模板网格时再切 true ── */ + const enableTemplates = false + /* ── Step5 保存中状态 ── */ const [finishing, setFinishing] = useState(false) @@ -538,7 +541,7 @@ const GeneratePage: React.FC = () => { onUpdateStyle={styleUpdaters.updateStyle} activePreset={styleUpdaters.activePreset} titlePresets={styleUpdaters.titlePresets} - enableTemplates + enableTemplates={enableTemplates} selectedTemplateId={selectedTitleTemplateId} onApplyTemplate={(settings, tpl) => { styleUpdaters.applyTemplate(settings) diff --git a/apps/web/src/pages/generate/components/cover-settings/CoverEditorModal.tsx b/apps/web/src/pages/generate/components/cover-settings/CoverEditorModal.tsx index 7c4d85738..21e320b6e 100644 --- a/apps/web/src/pages/generate/components/cover-settings/CoverEditorModal.tsx +++ b/apps/web/src/pages/generate/components/cover-settings/CoverEditorModal.tsx @@ -7,7 +7,7 @@ import type { TextDirection, StrokeStyle, } from "../../types/cover" -import { DEFAULT_EDITOR_CONFIG, PRESET_FONTS, SYSTEM_FONTS, ALL_FONTS } from "../../types/cover" +import { DEFAULT_EDITOR_CONFIG, ALL_FONTS } from "../../types/cover" import Modal from "@/components/ui/Modal" import Button from "@/components/ui/Button" import "@/components/cover/cover.css" @@ -109,27 +109,23 @@ const PositionPair: React.FC<{ ) -/* ── Font Select options ── */ -const fontOptions = [ - ...PRESET_FONTS.map((f) => ({ - label: ( - - - {f.name} - - ), - value: f.name, - })), - ...SYSTEM_FONTS.map((f) => ({ - label: ( - - - {f.name} - - ), - value: f.name, - })), -] +/* ── Font Select options(合并预设+系统,用圆点颜色区分 tag) ── */ +const fontDotClass = (tag?: string) => { + if (tag === "preset") return "xx-ce-font-dot xx-ce-font-dot--preset" + if (tag === "hand") return "xx-ce-font-dot xx-ce-font-dot--hand" + if (tag === "serif") return "xx-ce-font-dot xx-ce-font-dot--serif" + if (tag === "mono") return "xx-ce-font-dot xx-ce-font-dot--mono" + return "xx-ce-font-dot xx-ce-font-dot--system" +} +const fontOptions = ALL_FONTS.map((f) => ({ + label: ( + + + {f.name} + + ), + value: f.name, +})) /* ── Find font family string from name ── */ const getFontFamily = (name: string): string => { @@ -599,6 +595,7 @@ const CoverEditorModal: React.FC = ({ open, onClose, temp userSelect: "none", cursor: "grab", padding: 0, + pointerEvents: "auto", } if (tc.shadows.length > 0) { style.textShadow = tc.shadows diff --git a/apps/web/src/pages/generate/components/title/TitleMiniPreview.tsx b/apps/web/src/pages/generate/components/title/TitleMiniPreview.tsx index 51c51e7b1..025bd4e84 100644 --- a/apps/web/src/pages/generate/components/title/TitleMiniPreview.tsx +++ b/apps/web/src/pages/generate/components/title/TitleMiniPreview.tsx @@ -68,112 +68,96 @@ const TitleMiniPreview: React.FC = ({ const text = (sampleText || settings.title || "预览标题").trim() || "预览标题" useEffect(() => { - const cvs = canvasRef.current - if (!cvs) return - const dpr = window.devicePixelRatio || 1 - cvs.width = width * dpr - cvs.height = h * dpr - cvs.style.width = `${width}px` - cvs.style.height = `${h}px` - const ctx = cvs.getContext("2d") - if (!ctx) return - ctx.scale(dpr, dpr) - ctx.clearRect(0, 0, width, h) + let cancelled = false + const draw = () => { + if (cancelled) return + const cvs = canvasRef.current + if (!cvs) return + const dpr = window.devicePixelRatio || 1 + cvs.width = width * dpr + cvs.height = h * dpr + cvs.style.width = `${width}px` + cvs.style.height = `${h}px` + const ctx = cvs.getContext("2d") + if (!ctx) return + ctx.scale(dpr, dpr) + ctx.clearRect(0, 0, width, h) - // 背景(transparent 时跳过,用于叠加在图片上) - if (!transparent) { - ctx.fillStyle = "#111827" - ctx.fillRect(0, 0, width, h) - } + // 背景(transparent 时跳过,用于叠加在图片上) + if (!transparent) { + ctx.fillStyle = "#111827" + ctx.fillRect(0, 0, width, h) + } - // 分辨率缩放:以 360 宽为基准(对应 720p 的一半) - const scale = width / 360 - const r = (v: number) => Math.round(v * scale) + // 分辨率缩放:以 360 宽为基准(对应 720p 的一半) + const scale = width / 360 + const r = (v: number) => Math.round(v * scale) - // 字体 - const size = r(settings.size) - const ff = getFontFamily(settings.font) - const parts: string[] = [] - if (settings.italic) parts.push("italic") - if (settings.bold) parts.push("bold") - parts.push(`${size}px`, ff) - ctx.font = parts.join(" ") - ctx.textAlign = "center" - ctx.textBaseline = "middle" - ctx.fillStyle = settings.color - ctx.lineJoin = "round" + // 字体 + const size = r(settings.size) + const ff = getFontFamily(settings.font) + const parts: string[] = [] + if (settings.italic) parts.push("italic") + if (settings.bold) parts.push("bold") + parts.push(`${size}px`, ff) + ctx.font = parts.join(" ") + ctx.textAlign = "center" + ctx.textBaseline = "middle" + ctx.fillStyle = settings.color + ctx.lineJoin = "round" - // 阴影 - const shadowEnabled = !!settings.shadow - const prevShadow = { - c: ctx.shadowColor, - b: ctx.shadowBlur, - ox: ctx.shadowOffsetX, - oy: ctx.shadowOffsetY, - } - if (shadowEnabled) { - ctx.shadowColor = settings.shadowColor ?? "rgba(0,0,0,0.8)" - ctx.shadowBlur = r(settings.shadowBlur ?? 4) - ctx.shadowOffsetX = r(settings.shadowOffsetX ?? 2) - ctx.shadowOffsetY = r(settings.shadowOffsetY ?? 2) - } - - // 换行 - const lines = wrapLines(text, settings.maxCharsPerLine ?? 0) - const lineH = size * (settings.lineHeight ?? 1.2) - const totalH = lines.length * lineH - let startY: number - if (settings.position === "top") { - startY = size / 2 + r(settings.marginTop ?? 24) - } else if (settings.position === "center") { - startY = h / 2 - totalH / 2 + size / 2 - } else { - // bottom - const botMargin = portrait ? r(24) : r(16) - startY = h - totalH - botMargin + size / 2 - } - let centerX = width / 2 - if (settings.position === "custom" && settings.posX != null) { - centerX = (settings.posX / 100) * width - } - - // 背景块 - if (settings.bgEnabled) { - const pad = r(settings.bgPadding ?? 12) - const rad = r(settings.bgRadius ?? 8) - let maxLineW = 0 - for (const l of lines) { - const m = ctx.measureText(l) - if (m.width > maxLineW) maxLineW = m.width + // 阴影 + const shadowEnabled = !!settings.shadow + const prevShadow = { + c: ctx.shadowColor, + b: ctx.shadowBlur, + ox: ctx.shadowOffsetX, + oy: ctx.shadowOffsetY, } - const bw = maxLineW + pad * 2 - const bh = totalH + pad * 2 - const bx = centerX - bw / 2 - const by = startY - size / 2 - pad + (size - lineH) / 2 - ctx.shadowColor = "rgba(0,0,0,0)" - ctx.shadowBlur = 0 - ctx.fillStyle = settings.bgColor ?? "rgba(0,0,0,0.5)" - roundRect(ctx, bx, by, bw, bh, rad) - ctx.fill() - // 恢复阴影 if (shadowEnabled) { ctx.shadowColor = settings.shadowColor ?? "rgba(0,0,0,0.8)" ctx.shadowBlur = r(settings.shadowBlur ?? 4) ctx.shadowOffsetX = r(settings.shadowOffsetX ?? 2) ctx.shadowOffsetY = r(settings.shadowOffsetY ?? 2) } - } - // 描边(先画,再画填充) - const strokeEnabled = !!settings.stroke && (settings.strokeWidth ?? 0) > 0 - lines.forEach((line, i) => { - const y = startY + i * lineH - if (strokeEnabled) { + // 换行 + const lines = wrapLines(text, settings.maxCharsPerLine ?? 0) + const lineH = size * (settings.lineHeight ?? 1.2) + const totalH = lines.length * lineH + let startY: number + if (settings.position === "top") { + startY = size / 2 + r(settings.marginTop ?? 24) + } else if (settings.position === "center") { + startY = h / 2 - totalH / 2 + size / 2 + } else { + // bottom + const botMargin = portrait ? r(24) : r(16) + startY = h - totalH - botMargin + size / 2 + } + let centerX = width / 2 + if (settings.position === "custom" && settings.posX != null) { + centerX = (settings.posX / 100) * width + } + + // 背景块 + if (settings.bgEnabled) { + const pad = r(settings.bgPadding ?? 12) + const rad = r(settings.bgRadius ?? 8) + let maxLineW = 0 + for (const l of lines) { + const m = ctx.measureText(l) + if (m.width > maxLineW) maxLineW = m.width + } + const bw = maxLineW + pad * 2 + const bh = totalH + pad * 2 + const bx = centerX - bw / 2 + const by = startY - size / 2 - pad + (size - lineH) / 2 ctx.shadowColor = "rgba(0,0,0,0)" ctx.shadowBlur = 0 - ctx.lineWidth = r(settings.strokeWidth ?? 4) - ctx.strokeStyle = settings.strokeColor ?? "#000000" - ctx.strokeText(line, centerX, y) + ctx.fillStyle = settings.bgColor ?? "rgba(0,0,0,0.5)" + roundRect(ctx, bx, by, bw, bh, rad) + ctx.fill() // 恢复阴影 if (shadowEnabled) { ctx.shadowColor = settings.shadowColor ?? "rgba(0,0,0,0.8)" @@ -182,14 +166,46 @@ const TitleMiniPreview: React.FC = ({ ctx.shadowOffsetY = r(settings.shadowOffsetY ?? 2) } } - ctx.fillText(line, centerX, y) - }) - // 恢复 - ctx.shadowColor = prevShadow.c - ctx.shadowBlur = prevShadow.b - ctx.shadowOffsetX = prevShadow.ox - ctx.shadowOffsetY = prevShadow.oy + // 描边(先画,再画填充) + const strokeEnabled = !!settings.stroke && (settings.strokeWidth ?? 0) > 0 + lines.forEach((line, i) => { + const y = startY + i * lineH + if (strokeEnabled) { + ctx.shadowColor = "rgba(0,0,0,0)" + ctx.shadowBlur = 0 + ctx.lineWidth = r(settings.strokeWidth ?? 4) + ctx.strokeStyle = settings.strokeColor ?? "#000000" + ctx.strokeText(line, centerX, y) + // 恢复阴影 + if (shadowEnabled) { + ctx.shadowColor = settings.shadowColor ?? "rgba(0,0,0,0.8)" + ctx.shadowBlur = r(settings.shadowBlur ?? 4) + ctx.shadowOffsetX = r(settings.shadowOffsetX ?? 2) + ctx.shadowOffsetY = r(settings.shadowOffsetY ?? 2) + } + } + ctx.fillText(line, centerX, y) + }) + + // 恢复 + ctx.shadowColor = prevShadow.c + ctx.shadowBlur = prevShadow.b + ctx.shadowOffsetX = prevShadow.ox + ctx.shadowOffsetY = prevShadow.oy + } + // Web fonts (Google Fonts 等) 加载需要时间,等 fonts.ready 后再画, + // 避免第一次渲染用 sans-serif 画完再切字体造成「字体选择没反应」的错觉 + if (typeof document !== "undefined" && document.fonts && document.fonts.ready) { + document.fonts.ready.then(() => { + if (cancelled) return + draw() + }) + } + draw() + return () => { + cancelled = true + } }, [settings, width, h, text, transparent, portrait, background]) return ( diff --git a/apps/web/src/pages/generate/generate.css b/apps/web/src/pages/generate/generate.css index 63003488c..a2fc9e0eb 100644 --- a/apps/web/src/pages/generate/generate.css +++ b/apps/web/src/pages/generate/generate.css @@ -3540,10 +3540,19 @@ vertical-align: middle; } .xx-ce-font-dot--preset { - background: #10b981; + background: #10b981; /* 绿:预置爆款中文字体 */ +} +.xx-ce-font-dot--hand { + background: #f59e0b; /* 橙:手写/书法字体 */ +} +.xx-ce-font-dot--serif { + background: #8b5cf6; /* 紫:衬线字体 */ +} +.xx-ce-font-dot--mono { + background: #6b7280; /* 灰:等宽字体 */ } .xx-ce-font-dot--system { - background: #3b82f6; + background: #3b82f6; /* 蓝:系统无衬线 */ } /* Shadow actions */ @@ -3780,7 +3789,12 @@ align-items: center; } -/* Canvas text elements — ensure proper stacking */ -.xx-ce-canvas > div { +/* Canvas 装饰层(背景/装饰/遮罩/底色/人物/文字背景色块)不接收鼠标事件, + 但拖拽的标题/副标题文字(内联 cursor:grab)需要接收 mousedown。 + 已通过 renderTextStyle 显式设 pointer-events 以外的样式,因此此处只关掉纯装饰层。 */ +.xx-ce-canvas-base, +.xx-ce-el-bg, +.xx-ce-el-portrait, +.xx-ce-el-mask { pointer-events: none; } diff --git a/apps/web/src/pages/generate/types/cover.ts b/apps/web/src/pages/generate/types/cover.ts index 0a474001a..c52846c85 100644 --- a/apps/web/src/pages/generate/types/cover.ts +++ b/apps/web/src/pages/generate/types/cover.ts @@ -217,46 +217,89 @@ export const DEFAULT_EDITOR_CONFIG: CoverEditorConfig = { maskShape: "矩形", } -/** 预置字体 */ -export const PRESET_FONTS = [ - { name: "思源黑体", family: "'Noto Sans SC', sans-serif" }, - { name: "斗鱼追光体2.0", family: "'DouYu ZhuangGuangTi', sans-serif" }, - { name: "抖音美好体", family: "'DouYin MeiHaoTi', sans-serif" }, +/** 预置字体(已与 @/components/title/constants 字体表保持一致;自定义商业字体兜底 Google Fonts 开源中文字体) */ +// 封面编辑器预置字体:与标题样式字体列表保持一致(从 @/components/title/constants 同步), +// 并补全西文常用系统字体,保证在中英文环境下都有可用字体。 +// 注:需要配合 index.html 引入的 Google Fonts(Noto Sans SC / ZCOOL / Ma Shan Zheng 等)。 +export interface CoverFont { + name: string + family: string + tag?: "preset" | "hand" | "serif" | "sans" | "mono" +} + +/** 预置中文字体(爆款/常用) */ +export const PRESET_FONTS: CoverFont[] = [ + { + name: "优设标题黑", + family: + '"YouSheBiaoTiHei","ZCOOL QingKe HuangYou","Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif', + tag: "preset", + }, + { + name: "阿里普惠体Bold", + family: + '"Alibaba PuHuiTi","Alibaba Sans","Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif', + tag: "preset", + }, + { + name: "抖音美好体", + family: + '"Douyin Sans","ZCOOL KuaiLe","Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif', + tag: "preset", + }, + { + name: "思源黑体Heavy", + family: '"Noto Sans SC","Source Han Sans SC Heavy","PingFang SC","Microsoft YaHei",sans-serif', + tag: "preset", + }, + { + name: "思源黑体", + family: '"Noto Sans SC","Source Han Sans SC","PingFang SC","Microsoft YaHei",sans-serif', + tag: "preset", + }, + { + name: "思源宋体", + family: '"Noto Serif SC","Source Han Serif SC","Songti SC","SimSun",serif', + tag: "serif", + }, + { name: "站酷小薇体", family: '"ZCOOL XiaoWei","Noto Serif SC",serif', tag: "preset" }, + { name: "马善政毛笔", family: '"Ma Shan Zheng","STXingkai","KaiTi",cursive', tag: "hand" }, + { name: "龙藏体", family: '"Long Cang","STXingkai",cursive', tag: "hand" }, + { name: "楷体", family: '"KaiTi","STKaiti","DFKai-SB",serif', tag: "serif" }, + { + name: "苹方", + family: '"PingFang SC",-apple-system,"Helvetica Neue",sans-serif', + tag: "sans", + }, + { + name: "微软雅黑", + family: '"Microsoft YaHei","PingFang SC","Noto Sans SC",sans-serif', + tag: "sans", + }, ] -/** 系统字体 */ -export const SYSTEM_FONTS = [ - { name: "Arial", family: "Arial, sans-serif" }, - { name: "Helvetica", family: "Helvetica, sans-serif" }, - { name: "Times New Roman", family: "'Times New Roman', serif" }, - { name: "Georgia", family: "Georgia, serif" }, - { name: "Verdana", family: "Verdana, sans-serif" }, - { name: "Tahoma", family: "Tahoma, sans-serif" }, - { name: "Impact", family: "Impact, sans-serif" }, - { name: "Comic Sans MS", family: "'Comic Sans MS', cursive" }, - { name: "Courier New", family: "'Courier New', monospace" }, - { name: "微软雅黑", family: "'Microsoft YaHei', sans-serif" }, - { name: "宋体", family: "SimSun, serif" }, - { name: "黑体", family: "SimHei, sans-serif" }, - { name: "楷体", family: "KaiTi, serif" }, - { name: "仿宋", family: "FangSong, serif" }, - { name: "Trebuchet MS", family: "'Trebuchet MS', sans-serif" }, - { name: "Lucida Console", family: "'Lucida Console', monospace" }, - { name: "Palatino", family: "Palatino, serif" }, - { name: "Garamond", family: "Garamond, serif" }, - { name: "Bookman", family: "Bookman, serif" }, - { name: "Avant Garde", family: "'Avant Garde', sans-serif" }, - { name: "Calibri", family: "Calibri, sans-serif" }, - { name: "Cambria", family: "Cambria, serif" }, - { name: "Candara", family: "Candara, sans-serif" }, - { name: "Consolas", family: "Consolas, monospace" }, - { name: "Constantia", family: "Constantia, serif" }, - { name: "Corbel", family: "Corbel, sans-serif" }, - { name: "Franklin Gothic", family: "'Franklin Gothic', sans-serif" }, - { name: "Gill Sans", family: "'Gill Sans', sans-serif" }, - { name: "Optima", family: "Optima, sans-serif" }, - { name: "Futura", family: "Futura, sans-serif" }, - { name: "Rockwell", family: "Rockwell, serif" }, +/** 系统字体(西文 + 通用中文) */ +export const SYSTEM_FONTS: CoverFont[] = [ + { name: "Arial", family: "Arial, Helvetica, sans-serif", tag: "sans" }, + { name: "Helvetica", family: "Helvetica, Arial, sans-serif", tag: "sans" }, + { name: "Times New Roman", family: '"Times New Roman", Times, serif', tag: "serif" }, + { name: "Georgia", family: "Georgia, serif", tag: "serif" }, + { name: "Verdana", family: "Verdana, Geneva, sans-serif", tag: "sans" }, + { name: "Tahoma", family: "Tahoma, Geneva, sans-serif", tag: "sans" }, + { name: "Impact", family: 'Impact, "Arial Black", sans-serif', tag: "sans" }, + { name: "Comic Sans MS", family: '"Comic Sans MS", cursive', tag: "hand" }, + { name: "Courier New", family: '"Courier New", Courier, monospace', tag: "mono" }, + { name: "宋体", family: "SimSun, 'Noto Serif SC', serif", tag: "serif" }, + { name: "黑体", family: "SimHei, 'Noto Sans SC', sans-serif", tag: "sans" }, + { name: "仿宋", family: "FangSong, 'Noto Serif SC', serif", tag: "serif" }, + { name: "Trebuchet MS", family: '"Trebuchet MS", sans-serif', tag: "sans" }, + { name: "Lucida Console", family: '"Lucida Console", Monaco, monospace', tag: "mono" }, + { name: "Palatino", family: 'Palatino, "Palatino Linotype", serif', tag: "serif" }, + { name: "Garamond", family: "Garamond, serif", tag: "serif" }, + { name: "Calibri", family: "Calibri, sans-serif", tag: "sans" }, + { name: "Cambria", family: "Cambria, serif", tag: "serif" }, + { name: "Candara", family: "Candara, sans-serif", tag: "sans" }, + { name: "Consolas", family: "Consolas, monospace", tag: "mono" }, ] /** 所有字体列表 */