Skip to content

Commit

Permalink
feat: supports enable part features, fix more abnormal attributes (#35
Browse files Browse the repository at this point in the history
  • Loading branch information
qq15725 committed Jul 31, 2023
1 parent ea62d1e commit c707dc2
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 26 deletions.
43 changes: 25 additions & 18 deletions src/clone-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ function applyCssStyleWithOptions(
if (styles) for (const name in styles) clonedStyle[name] = styles[name]!
}

const ABNORMAL_ATTRIBUTE_RE = /^[\w-]+$/

export async function cloneNode<T extends Node>(
node: T,
context: Context,
Expand All @@ -87,29 +89,34 @@ export async function cloneNode<T extends Node>(
) {
const cloned = await cloneElement(node, context)

// fix abnormal attribute
cloned.removeAttribute('"')
if (context.isEnable('removeAbnormalAttributes')) {
const names = cloned.getAttributeNames()
for (let len = names.length, i = 0; i < len; i++) {
const name = names[i]
if (!ABNORMAL_ATTRIBUTE_RE.test(name)) {
cloned.removeAttribute(name)
}
}
}

const diffStyle = copyCssStyles(node, cloned, isRoot, context)

if (isRoot) applyCssStyleWithOptions(cloned, context)

const overflow = [
diffStyle.get('overflow-x')?.[0],
diffStyle.get('overflow-y')?.[1],
]

copyPseudoClass(
node,
cloned,
// copy scrollbar
(overflow.includes('scroll'))
|| (
(overflow.includes('auto') || overflow.includes('overlay'))
&& (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth)
),
context,
)
let copyScrollbar = false
if (context.isEnable('copyScrollbar')) {
const overflow = [
diffStyle.get('overflow-x')?.[0],
diffStyle.get('overflow-y')?.[1],
]
copyScrollbar = (overflow.includes('scroll'))
|| (
(overflow.includes('auto') || overflow.includes('overlay'))
&& (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth)
)
}

copyPseudoClass(node, cloned, copyScrollbar, context)

copyInputValue(node, cloned)

Expand Down
7 changes: 7 additions & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ export interface InternalContext<T extends Node> {
* Automatically destroy context
*/
autoDestruct: boolean

/**
* Is enable
*
* @param key
*/
isEnable: (key: string) => boolean
}

export type Context<T extends Node = Node> = InternalContext<T> & Required<Options>
2 changes: 1 addition & 1 deletion src/converts/dom-to-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function domToCanvas<T extends Node>(context: Context<T>): Promise<
export async function domToCanvas(node: any, options?: any) {
const context = await orCreateContext(node, options)
const svg = await domToForeignObjectSvg(context)
const dataUrl = svgToDataUrl(svg)
const dataUrl = svgToDataUrl(svg, context.isEnable('removeControlCharacter'))
if (!context.autoDestruct) {
context.svgStyleElement = createStyleElement(context.ownerDocument)
context.svgDefsElement = context.ownerDocument?.createElementNS(XMLNS, 'defs')
Expand Down
2 changes: 1 addition & 1 deletion src/converts/dom-to-svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ export async function domToSvg(node: any, options?: any) {
svgImage.setAttributeNS(null, 'height', '100%')
svgImage.setAttributeNS(null, 'width', '100%')
svg.appendChild(svgImage)
return svgToDataUrl(svg)
return svgToDataUrl(svg, context.isEnable('removeControlCharacter'))
}
9 changes: 9 additions & 0 deletions src/create-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export async function createContext<T extends Node>(node: T, options?: Options &
const { scale = 1, workerUrl, workerNumber = 1 } = options || {}

const debug = Boolean(options?.debug)
const features = options?.features ?? true

const ownerDocument = node.ownerDocument ?? (IN_BROWSER ? window.document : undefined)
const ownerWindow = node.ownerDocument?.defaultView ?? (IN_BROWSER ? window : undefined)
Expand Down Expand Up @@ -107,6 +108,14 @@ export async function createContext<T extends Node>(node: T, options?: Options &
requests,
drawImageCount: 0,
tasks: [],

features,
isEnable: (key: string): boolean => {
if (typeof features === 'boolean') {
return features
}
return (features as any)[key] ?? true
},
}

context.log.time('wait until load')
Expand Down
28 changes: 28 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,34 @@ export interface Options {
cssText?: string
}

/**
* All enabled features
*
* default: true
*/
features?: boolean | {
/**
* Copy scrollbar css styles
*
* default: true
*/
copyScrollbar?: boolean

/**
* Remove abnormal attributes to cloned node (for normalize XML)
*
* default: true
*/
removeAbnormalAttributes?: boolean

/**
* Remove control characters (for normalize XML)
*
* default: true
*/
removeControlCharacter?: boolean
}

/**
* Canvas `drawImage` interval
* is used to fix errors in decoding images in safari/webkit
Expand Down
16 changes: 10 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,16 @@ export function createSvg(width: number, height: number, ownerDocument?: Documen
return svg
}

export function svgToDataUrl(svg: SVGElement) {
const xhtml = new XMLSerializer()
.serializeToString(svg)
// https://www.w3.org/TR/xml/#charsets
// eslint-disable-next-line no-control-regex
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]/ug, '')
export function svgToDataUrl(svg: SVGElement, removeControlCharacter: boolean) {
let xhtml = new XMLSerializer().serializeToString(svg)

if (removeControlCharacter) {
xhtml = xhtml
// https://www.w3.org/TR/xml/#charsets
// eslint-disable-next-line no-control-regex
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]/ug, '')
}

return `data:image/svg+xml;charset=utf-8,${ encodeURIComponent(xhtml) }`
}

Expand Down

1 comment on commit c707dc2

@vercel
Copy link

@vercel vercel bot commented on c707dc2 Jul 31, 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.