diff --git a/src/converts/dom-to-foreign-object-svg.ts b/src/converts/dom-to-foreign-object-svg.ts index 35b263e..ef335c2 100644 --- a/src/converts/dom-to-foreign-object-svg.ts +++ b/src/converts/dom-to-foreign-object-svg.ts @@ -83,7 +83,6 @@ function createForeignObjectSvg(clone: Node, context: Context): SVGSVGElement { foreignObject.setAttributeNS(null, 'y', '0%') foreignObject.setAttributeNS(null, 'width', '100%') foreignObject.setAttributeNS(null, 'height', '100%') - foreignObject.setAttributeNS(null, 'externalResourcesRequired', 'true') foreignObject.append(clone) svg.appendChild(foreignObject) return svg diff --git a/src/css-url.ts b/src/css-url.ts index f59b0a7..c273851 100644 --- a/src/css-url.ts +++ b/src/css-url.ts @@ -10,21 +10,19 @@ export async function replaceCssUrlToDataUrl( ): Promise { if (!hasCssUrl(cssText)) return cssText - for (const url of parseCssUrls(cssText)) { + for (const [rawUrl, url] of parseCssUrls(cssText, baseUrl)) { try { const dataUrl = await contextFetch( context, { - url: baseUrl - ? resolveUrl(url, baseUrl) - : url, + url, requestType: isImage ? 'image' : 'text', responseType: 'dataUrl', }, ) - cssText = cssText.replace(toRE(url), `$1${ dataUrl }$3`) + cssText = cssText.replace(toRE(rawUrl), `$1${ dataUrl }$3`) } catch (error) { - consoleWarn('Failed to fetch css data url', url, error) + consoleWarn('Failed to fetch css data url', rawUrl, error) } } @@ -35,15 +33,17 @@ export function hasCssUrl(cssText: string): boolean { return /url\((['"]?)([^'"]+?)\1\)/.test(cssText) } -function parseCssUrls(cssText: string): string[] { - const result: string[] = [] +export const URL_RE = /url\((['"]?)([^'"]+?)\1\)/g - cssText.replace(/url\((['"]?)([^'"]+?)\1\)/g, (raw, quotation, url) => { - result.push(url) +function parseCssUrls(cssText: string, baseUrl: string | null): [string, string][] { + const result: [string, string][] = [] + + cssText.replace(URL_RE, (raw, quotation, url) => { + result.push([url, resolveUrl(url, baseUrl)]) return raw }) - return result.filter((url) => !isDataUrl(url)) + return result.filter(([url]) => !isDataUrl(url)) } function toRE(url: string): RegExp { diff --git a/src/embed-web-font.ts b/src/embed-web-font.ts index 9f4d420..f6d92c3 100644 --- a/src/embed-web-font.ts +++ b/src/embed-web-font.ts @@ -1,6 +1,6 @@ -import { hasCssUrl, replaceCssUrlToDataUrl } from './css-url' +import { URL_RE, hasCssUrl, replaceCssUrlToDataUrl } from './css-url' import { contextFetch } from './fetch' -import { consoleWarn, isCssFontFaceRule } from './utils' +import { consoleWarn, isCssFontFaceRule, resolveUrl } from './utils' import type { Context } from './context' export async function embedWebFont( @@ -24,7 +24,7 @@ export async function embedWebFont( if (font && font.cssText) { const cssText = filterPreferredFormat(font.cssText, context) - svgStyleElement.appendChild(ownerDocument.createTextNode(`\n${ cssText }\n`)) + svgStyleElement.appendChild(ownerDocument.createTextNode(`${ cssText }\n`)) } else { try { const cssRules = await getCssRules(Array.from(ownerDocument.styleSheets), context) @@ -77,15 +77,17 @@ async function getCssRules( .filter(rule => rule.constructor.name === 'CSSImportRule') .map(async rule => { let importIndex = index + 1 - const url = (rule as CSSImportRule).href + const baseUrl = (rule as CSSImportRule).href try { const cssText = await contextFetch(context, { - url, + url: baseUrl, requestType: 'text', responseType: 'text', }) - const embedCssText = await embedFonts(cssText, url, context) - for (const rule of parseCss(embedCssText)) { + const replacedCssText = cssText.replace(URL_RE, (raw, quotation, url) => { + return raw.replace(url, resolveUrl(url, baseUrl)) + }) + for (const rule of parseCss(replacedCssText)) { try { sheet.insertRule( rule, @@ -119,29 +121,6 @@ async function getCssRules( return ret } -const URL_MATCH_RE = /url\([^)]+\)/g -const URL_REPLACE_RE = /url\(["']?([^"')]+)["']?\)/g - -async function embedFonts(cssText: string, baseUrl: string, context: Context): Promise { - await Promise.all( - cssText.match(URL_MATCH_RE)?.map(async location => { - let url = location.replace(URL_REPLACE_RE, '$1') - if (!url.startsWith('https://')) { - url = new URL(url, baseUrl).href - } - return contextFetch(context, { - url, - requestType: 'text', - responseType: 'dataUrl', - }).then(base64 => { - // Side Effect - cssText = cssText.replace(location, `url(${ base64 })`) - }) - }) ?? [], - ) - return cssText -} - const COMMENTS_RE = /(\/\*[\s\S]*?\*\/)/gi const KEYFRAMES_RE = /((@.*?keyframes [\s\S]*?){([\s\S]*?}\s*?)})/gi diff --git a/src/utils.ts b/src/utils.ts index 8cf9837..93679d3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -97,7 +97,7 @@ export function svgToDataUrl(svg: SVGElement) { const xhtml = new XMLSerializer() .serializeToString(svg) // eslint-disable-next-line no-control-regex - .replace(/[\u0000-\u001F\u007F-\u009F]/g, '') + .replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]/g, '') return `data:image/svg+xml;charset=utf-8,${ encodeURIComponent(xhtml) }` } diff --git a/test/fixtures/xml.ascii-null-character.html b/test/fixtures/xml.ascii-null-character.html new file mode 100644 index 0000000..3f0bf30 Binary files /dev/null and b/test/fixtures/xml.ascii-null-character.html differ diff --git a/test/fixtures/xml.ascii-null-character.png b/test/fixtures/xml.ascii-null-character.png new file mode 100644 index 0000000..62eda0d Binary files /dev/null and b/test/fixtures/xml.ascii-null-character.png differ