neocotic / convert-svg

Node.js packages for converting SVG into other formats using headless Chromium
MIT License
200 stars 47 forks source link

Issue trying to consume png output on the client #39

Closed jeremytowne closed 6 years ago

jeremytowne commented 6 years ago

Hi, I'm having an issue trying to consume the output of convert on the client. I'm trying to create a blob and download it, but the result seems to be a file that can't be opened. I've also tried to get a url from the blob and set it as an img.src, but also no luck. Maybe I'm not creating the blob correctly? Any help is appreciated!!

Client:

async function saveChart() {
// convertSvgToPng POSTs the data to the /convert route and returns the data received 
    const pngData = await convertSvgToPng(
      document.getElementById(title).outerHTML
    )
    const blob = new Blob([pngData], { type: 'image/png' })
    saveAs(blob, `${title}.png`)
  }

Server:

import { convert } from 'convert-svg-to-png'

router.post(
  '/convert',
  wrap(async (req, res) => {
    const png = await convert(req.body.svg)

    res.set('Content-Type', 'image/png')
    return res.send(png)
  })
)
jeremytowne commented 6 years ago

I ended up getting this to work. On the server I had to get the base64 encoded string for the png data:

router.post(
  '/convert',
  wrap(async (req, res) => {
    const png = await convert(req.body.svg)
    const base64data = Buffer.from(png).toString('base64')
    res.set('Content-Type', 'image/png')
    return res.send(base64data)
  })
)

Then on the client I had to do a little manipulation to get it into the correct form the Blob would accept:

  function fixBinary(bin) {
    const length = bin.length
    const buf = new ArrayBuffer(length)
    const arr = new Uint8Array(buf)
    arr.forEach((binData, index) => {
      arr[index] = bin.charCodeAt(index)
    })
    return buf
  }

  async function saveChart() {
    const pngData = await convertSvgToPng(
      // IE has an issue just getting innerHTML, work around is to get the innerHTML of the parentNode
      document.getElementById(title).parentNode.innerHTML
    )
    const blob = new Blob([fixBinary(atob(pngData))], { type: 'image/png' })
    saveAs(blob, `${title}.png`)
  }
neocotic commented 6 years ago

I didn't see this issue in my inbox somehow, so sorry for delayed response. I'm glad you solved your problem as I've not too much experience with the Blob API so not sure how much help I could have been. I might investigate this further though as I can imagine this being a common use case. Any reason you didn't set the Content-Disposition header on the server response to trigger a browser download of the PNG instead?