fix(export): 内联 PDF 字体并清理历史记录
This commit is contained in:
@@ -8,10 +8,17 @@ vi.mock('@/stores/markdownPreferences',()=>({useMarkdownPreferencesStore:()=>({n
|
||||
vi.mock('@/stores/headingAppearance',()=>({useHeadingAppearanceStore:()=>({cssVariables:{'--heading-1-size':'37px'},preferences:{custom:true}})}))
|
||||
vi.mock('@/components/common/MarkdownContent.vue',()=>({default:{}}))
|
||||
vi.mock('@/features/editor/VisualMarkdownEditor.vue',()=>({default:{__scopeId:'data-v-editor'}}))
|
||||
import {preparePdfSnapshot} from './pdfSnapshotService'
|
||||
import {preparePdfSnapshot,preferWoff2FontSource} from './pdfSnapshotService'
|
||||
import {apiClient} from './apiClient'
|
||||
import {renderMarkdown} from '@/utils/markdown'
|
||||
afterEach(()=>vi.clearAllMocks())
|
||||
it('keeps only the WOFF2 source when preparing embedded print fonts',()=>{
|
||||
const css='@font-face { font-family: "Demo"; src: url("data:font/woff2;base64,d09GMg==") format("woff2"), url("demo.woff") format("woff"), url("demo.ttf") format("truetype"); font-style: normal; }'
|
||||
const compact=preferWoff2FontSource(css)
|
||||
expect(compact).toContain('url("data:font/woff2;base64,d09GMg==") format("woff2")')
|
||||
expect(compact).not.toContain('demo.woff"')
|
||||
expect(compact).not.toContain('demo.ttf')
|
||||
})
|
||||
it('preserves actual theme CSS, pseudo elements, root attributes and heading preferences',async()=>{
|
||||
const style=document.createElement('style');style.textContent='[data-theme="paper"] .ProseMirror::before { content:"tape"; transform:rotate(-3deg) }';document.head.append(style)
|
||||
document.documentElement.dataset.theme='paper'
|
||||
|
||||
@@ -38,8 +38,31 @@ async function dataUrl(url: string, signal?:AbortSignal):Promise<string> {
|
||||
const blob=await response.blob()
|
||||
return await new Promise((resolve,reject)=>{const reader=new FileReader();reader.onload=()=>resolve(String(reader.result));reader.onerror=reject;reader.readAsDataURL(blob)})
|
||||
}
|
||||
export function preferWoff2FontSource(css:string):string {
|
||||
if(!/^\s*@font-face\b/i.test(css))return css
|
||||
const declaration=/\bsrc\s*:/i.exec(css)
|
||||
if(!declaration)return css
|
||||
const valueStart=declaration.index+declaration[0].length
|
||||
let quote='',depth=0,valueEnd=-1
|
||||
for(let index=valueStart;index<css.length;index+=1) {
|
||||
const character=css[index]!
|
||||
if(quote) {
|
||||
if(character==='\\')index+=1
|
||||
else if(character===quote)quote=''
|
||||
} else if(character==='"'||character==="'")quote=character
|
||||
else if(character==='(')depth+=1
|
||||
else if(character===')')depth=Math.max(0,depth-1)
|
||||
else if(character===';'&&depth===0){valueEnd=index;break}
|
||||
}
|
||||
if(valueEnd<0)return css
|
||||
const sources=css.slice(valueStart,valueEnd)
|
||||
const woff2=sources.match(/url\(\s*(?:["'][^"']*["']|[^)]*)\s*\)\s*format\(\s*["']?woff2["']?\s*\)/i)
|
||||
return woff2 ? `${css.slice(0,valueStart)} ${woff2[0]}${css.slice(valueEnd)}` : css
|
||||
}
|
||||
async function embedCss(css: string, base: string, signal?:AbortSignal) {
|
||||
// 打印进程完全离线,主题资源必须来自应用同源地址并在此转换为 data URL。
|
||||
// Chromium 支持 WOFF2;丢弃同一字体的 WOFF/TTF 回退,避免重复嵌入三份字体。
|
||||
css=preferWoff2FontSource(css)
|
||||
const matches=[...css.matchAll(/url\(\s*(['"]?)(.*?)\1\s*\)/g)]
|
||||
for(const match of matches) {
|
||||
const url=match[2]!
|
||||
|
||||
Reference in New Issue
Block a user