From fc791067e615d531f442047a92ad26707c5b0b7e Mon Sep 17 00:00:00 2001 From: hzsrc Date: Tue, 10 Sep 2024 15:25:15 +0800 Subject: [PATCH] fix: url(...) in css file lose efficacy --- package.json | 2 +- src/index.ts | 20 +++++++++--------- src/util.ts | 59 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 1868843a..047488b9 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "svg" ], "scripts": { - "lint": "eslint 'src/**/*.{js,ts}?(x)' --fix", + "lint": "eslint src/**/*.{js,ts} --fix", "clean": "rimraf dist es lib", "build:esm": "tsc --module esnext --target es2017 --outDir ./es", "build:cjs": "tsc --module commonjs --target es5 --outDir ./lib", diff --git a/src/index.ts b/src/index.ts index c5b87ca8..323d6694 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import { Options } from './types' import { cloneNode } from './clone-node' -import { embedImages } from './embed-images' +//import { embedImages } from './embed-images' import { applyStyle } from './apply-style' -import { embedWebFonts, getWebFontCSS } from './embed-webfonts' +//import { embedWebFonts, getWebFontCSS } from './embed-webfonts' import { getImageSize, getPixelRatio, @@ -19,8 +19,8 @@ export async function toSvg( ): Promise { const { width, height } = getImageSize(node, options) const clonedNode = (await cloneNode(node, options, true)) as HTMLElement - await embedWebFonts(clonedNode, options) - await embedImages(clonedNode, options) + //await embedWebFonts(clonedNode, options) + //await embedImages(clonedNode, options) applyStyle(clonedNode, options) const datauri = await nodeToDataURL(clonedNode, width, height, options.usePageCss) return datauri @@ -143,9 +143,9 @@ export async function toBlob( return blob } -export async function getFontEmbedCSS( - node: T, - options: Options = {}, -): Promise { - return getWebFontCSS(node, options) -} +// export async function getFontEmbedCSS( +// node: T, +// options: Options = {}, +// ): Promise { +// return getWebFontCSS(node, options) +// } diff --git a/src/util.ts b/src/util.ts index f080fa6e..98bae887 100644 --- a/src/util.ts +++ b/src/util.ts @@ -240,6 +240,8 @@ export async function svgToDataURL(svg: SVGElement): Promise { .then(() => new XMLSerializer().serializeToString(svg)) .then(encodeURIComponent) .then((html) => `data:image/svg+xml;charset=utf-8,${html}`) + // @ts-ignore + //.then(s => $('body').prepend(svg) && s) } export async function nodeToDataURL( @@ -275,9 +277,8 @@ export async function nodeToDataURL( return svgToDataURL(svg) } -export const isInstanceOfElement = < - T extends typeof Element | typeof HTMLElement | typeof SVGImageElement, ->( +export const isInstanceOfElement = ( node: Element | HTMLElement | SVGImageElement, instance: T, ): node is T['prototype'] => { @@ -304,6 +305,7 @@ export function getStyles() { promises.push( fetch(href) .then((r) => r.text()) + .then(tx => transRelPath(href, tx)) .catch(() => ''), ) } else { @@ -314,3 +316,54 @@ export function getStyles() { return arr.join('\n\n') }) } + +function transRelPath(cssPath: string, cssText: string): Promise { + const quotReg = /^\s*(['"])(.+?)\1/ + const map: { [url: string]: string } = {} + //css中的图片路径是相对于css文件的,要改为相对于当前html文件 + let css = cssText.replace(/url\(\s*(.+?)\s*\)/ig, (m, path) => { + path = path.replace(quotReg, '$2') + const sUrl = toRelative(cssPath, path) + const ret = 'url(' + sUrl + ')' + map[sUrl] = ret + return ret + }) + //css中的图片在svg中访问失败,需要转换为dataUrl + const promises = Object.keys(map).map(url => { + return urlToDataUrl(url).then(dataUrl => { + let p: number + while ((p = css.indexOf(map[url])) > -1) { + css = css.substring(0, p) + 'url(' + dataUrl + ')' + css.substring(p + map[url].length) + } + }) + }) + return Promise.all(promises).then(_ => css) +} + +function toRelative(compareTo: string, path: string): string { + if (path[0] === '/' || path.match(/^data:|:\/\//i)) return path + const pos = compareTo.lastIndexOf('/') + if (pos > -1) { + const dir = compareTo.substring(0, pos) + const arr = (dir + '/' + path).split('/').filter(i => i !== '.') + for (let i = arr.length - 1; i > 0; i--) { + if (arr[i] === '..' && arr[i - 1] !== '..') { + arr.splice(i - 1, 2) + i-- + } + } + return arr.join('/') + } + return path +} + +function urlToDataUrl(url: string): Promise { + return fetch(url) + .then(response => response.blob()) // 将响应转换为Blob + .then(blob => new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => resolve('' + reader.result) + reader.onerror = error => resolve('') + reader.readAsDataURL(blob) // 转换Blob为DataURL + })) +}