Skip to content

Commit

Permalink
fix: ascii null character regex
Browse files Browse the repository at this point in the history
  • Loading branch information
qq15725 committed Mar 6, 2023
1 parent 0175520 commit 83aee39
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 43 deletions.
1 change: 0 additions & 1 deletion src/converts/dom-to-foreign-object-svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 11 additions & 11 deletions src/css-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,19 @@ export async function replaceCssUrlToDataUrl(
): Promise<string> {
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)
}
}

Expand All @@ -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 {
Expand Down
39 changes: 9 additions & 30 deletions src/embed-web-font.ts
Original file line number Diff line number Diff line change
@@ -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<T extends Element>(
Expand All @@ -24,7 +24,7 @@ export async function embedWebFont<T extends Element>(

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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<string> {
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

Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) }`
}

Expand Down
Binary file added test/fixtures/xml.ascii-null-character.html
Binary file not shown.
Binary file added test/fixtures/xml.ascii-null-character.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

1 comment on commit 83aee39

@vercel
Copy link

@vercel vercel bot commented on 83aee39 Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

modern-screenshot – ./

modern-screenshot.vercel.app
modern-screenshot-qq15725.vercel.app
modern-screenshot-git-main-qq15725.vercel.app

Please sign in to comment.