Skip to content

Commit

Permalink
feat: add static method Image.fromSvg parsing svg string to image
Browse files Browse the repository at this point in the history
with test case

close #34
  • Loading branch information
bubkoo committed Dec 13, 2019
1 parent de3570e commit ff81d92
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 1 deletion.
104 changes: 104 additions & 0 deletions packages/x6/src/struct/image.test.ts
@@ -0,0 +1,104 @@
import { Image } from './image'

const content =
'<g fill="none" fill-rule="nonzero"><path fill="#EDC92C" d="M15.874 12.317L8.4.27a.477.477 0 0 0-.812 0L.092 12.35a.477.477 0 0 0 .405.73H15.491a.477.477 0 0 0 .383-.763z"/><path fill="#000" d="M7.44 8.097l-.155-2.319c-.03-.452-.043-.776-.043-.973 0-.268.07-.477.21-.627a.723.723 0 0 1 .554-.225c.278 0 .464.096.557.288.094.192.14.469.14.83 0 .213-.01.43-.033.649l-.208 2.387c-.023.284-.071.502-.146.653a.38.38 0 0 1-.368.228c-.174 0-.295-.073-.363-.22-.068-.147-.116-.37-.145-.67zm.537 3.186a.761.761 0 0 1-.515-.191c-.147-.127-.22-.306-.22-.535 0-.2.07-.37.21-.511.14-.14.312-.21.516-.21.203 0 .376.07.52.21.144.14.216.31.216.511 0 .226-.073.403-.218.532a.742.742 0 0 1-.509.194z"/></g>'

describe('Image.fromSvg', () => {
it('should prase svg string to image', () => {
const img = Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="14">${content}</svg>`,
)
expect(img.width).toBe(16)
expect(img.height).toBe(14)

const src = img.toString()
expect(src).toBe(img.src)

const clone = img.valueOf()
expect(clone.src).toBe(img.src)
expect(clone.width).toBe(img.width)
expect(clone.height).toBe(img.height)
console.log(img.src)
})

it('should use specified width and height', () => {
const img = Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="14">${content}</svg>`,
32,
32,
)
expect(img.width).toBe(32)
expect(img.height).toBe(32)
})

it('should prase width and height from viewbox', () => {
const img = Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 14">${content}</svg>`,
)
expect(img.width).toBe(16)
expect(img.height).toBe(14)
})

it('should prase width from viewbox', () => {
const img = Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 14" height="14">${content}</svg>`,
)
expect(img.width).toBe(16)
expect(img.height).toBe(14)
})

it('should prase height from viewbox', () => {
const img = Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 14" width="16">${content}</svg>`,
)
expect(img.width).toBe(16)
expect(img.height).toBe(14)
})

it('should throw error when can not prase width', () => {
const fn1 = () => {
Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" width="x">${content}</svg>`,
)
}

expect(fn1).toThrowError()

const fn2 = () => {
Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 x 14">${content}</svg>`,
)
}

expect(fn2).toThrowError()
})

it('should throw error when can not prase height', () => {
const fn1 = () => {
Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" height="x">${content}</svg>`,
)
}

expect(fn1).toThrowError()

const fn2 = () => {
Image.fromSvg(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 x">${content}</svg>`,
)
}

expect(fn2).toThrowError()
})

// it.skip('should work width old browser', () => {
// const btoa = window.btoa
// window.btoa = null
// const img = Image.fromSvg(
// `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 14" height="14">${content}</svg>`,
// )
// expect(img.width).toBe(16)
// expect(img.height).toBe(14)
// window.btoa = btoa
// })
})
86 changes: 85 additions & 1 deletion packages/x6/src/struct/image.ts
Expand Up @@ -13,7 +13,7 @@ export class Image {
/**
* Integer that specifies the height of the image.
*/
public height: number
public height: number,
) {}

toString() {
Expand All @@ -36,3 +36,87 @@ export namespace Image {
height: number
}
}

export namespace Image {
function parseViewBox(svg: string) {
const matches = svg.match(/<svg[^>]*viewBox\s*=\s*(["']?)(.+?)\1[^>]*>/i)
if (matches && matches[2]) {
return matches[2].replace(/\s+/, ' ').split(' ')
}
return null
}

function getNumber(str: string) {
const ret = parseFloat(str)
return isNaN(ret) ? null : ret
}

export function fromSvg(
svg: string,
width?: number | null,
height?: number | null,
) {
let viewBox: string[] | null = null

const getNumberFromViewBox = (index: number) => {
if (viewBox == null) {
viewBox = parseViewBox(svg)
}
if (viewBox != null) {
return getNumber(viewBox[index])
}
return null
}

const getNumberFromMatches = (reg: RegExp) => {
const matches = svg.match(reg)
if (matches && matches[2]) {
return getNumber(matches[2])
}
return null
}

let w = width
if (w == null) {
w = getNumberFromMatches(/<svg[^>]*width\s*=\s*(["']?)(.+?)\1[^>]*>/i)
}

if (w == null) {
w = getNumberFromViewBox(2)
}

if (w == null) {
throw new Error('Can not parse width from svg string')
}

let h = height
if (h == null) {
h = getNumberFromMatches(/<svg[^>]*height\s*=\s*(["']?)(.+?)\1[^>]*>/i)
}

if (h == null) {
h = getNumberFromViewBox(3)
}

if (h == null) {
throw new Error('Can not parse height from svg string')
}

const decoded = encodeURIComponent(svg)
.replace(/'/g, '%27')
.replace(/"/g, '%22')

const header = 'data:image/svg+xml'
const dataUrl = `${header},${decoded}`

// let dataUrl
// if (window.btoa != null) {
// const base64 = btoa(decoded)
// dataUrl = `${header};base64,${base64}`
// } else {
// dataUrl = `${header},${decoded}`
// }

return new Image(dataUrl, w, h)
}
}

0 comments on commit ff81d92

Please sign in to comment.