webtoon / psd

Fast zero-dependency PSD parser for the web and Node.js
https://webtoon.github.io/psd
MIT License
1.18k stars 49 forks source link

PNG converted to PSD with webtoon/psd not parseable with `Psd.parse(buffer)` (PSD generated from PNG with imagemagick-wasm)? #100

Open termhare opened 7 months ago

termhare commented 7 months ago

I created this PSD file.psd.zip from this PNG file.png.zip, using https://github.com/dlemstra/magick-wasm . That magick library (for the browser) works on converting PNG to JPG/etc., and I saved the bytes output from the selected browser file to a .psd file on my computer, and that is the contents of file.psd.zip. That psd seems to render on my Mac (and with CMD+space it shows large):

Screenshot 2024-01-23 at 12 17 38 PM

That png itself was generated from another png (I'm playing with magick-wasm), if that matters. But it renders as a PSD locally if I say the bytes to a .psd file, but I get this error in Psd.parse(arrayBuffer):

my-app/node_modules/.pnpm/@webtoon+psd@0.4.0/node_modules/@webtoon/psd/dist/index.js:397
    throw new Le();
          ^

Le
    at wt (my-app/node_modules/.pnpm/@webtoon+psd@0.4.0/node_modules/@webtoon/psd/dist/index.js:397:11)
    at fn (my-app/node_modules/.pnpm/@webtoon+psd@0.4.0/node_modules/@webtoon/psd/dist/index.js:1188:76)
    at Rn (my-app/node_modules/.pnpm/@webtoon+psd@0.4.0/node_modules/@webtoon/psd/dist/index.js:1490:13)
    at Function.parse (my-app/node_modules/.pnpm/@webtoon+psd@0.4.0/node_modules/@webtoon/psd/dist/index.js:1982:15)
    at <anonymous> (my-app/tmp/psd.ts:162482:21)
    at Object.<anonymous> (my-app/tmp/psd.ts:162490:4)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Object.F (my-app/node_modules/.pnpm/@esbuild-kit+cjs-loader@2.4.2/node_modules/@esbuild-kit/cjs-loader/dist/index.js:1:941)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Module._load (node:internal/modules/cjs/loader:1023:12)

Node.js v20.10.0

This is occurring whether or not I'm calling Psd.parse in Node.js or the browser. So it fails to parse the PSD, and so I can't render the PSD in the browser.

Any ideas what is wrong with this PNG, or why this isn't parsing?

I get the same error when trying to parse a JPEG as well, so not sure.

My messy code, with the PSD functionality temp hacked in for now:

useEffect(() => {
  if (!files?.length) {
    return
  }

  const file = files[0]
  setInput(file)
  // https://www.npmjs.com/package/wasm-imagemagick
  file.arrayBuffer().then(async data => {
    const ir = new FileReader()
    ir.onloadend = () => {
      setInputString(ir.result as string)
    }
    ir.readAsDataURL(file)
    if (data.byteLength <= bytes('16mb')) {
      if (outputFormat === 'psd') {
        const canvasElement = canvasRef.current
        if (canvasElement) {
          try {
            console.log(bufferToString(await file.arrayBuffer()))

            function bufferToString(buf: ArrayBuffer) {
              var view = new Uint8Array(buf)
              var array: Array<string> = []
              Array.prototype.forEach.call(view, item => {
                array.push(
                  '0x' +
                    (item as number).toString(16).padStart(2, '0'),
                )
              })
              return `[ ${array.join(',\n  ')} ]`
            }
            const psdFile = Psd.parse(await file.arrayBuffer())
            const context = canvasElement.getContext('2d')
            const compositeBuffer = await psdFile.composite()
            const imageData = new ImageData(
              compositeBuffer as Uint8ClampedArray,
              psdFile.width as number,
              psdFile.height as number,
            )
            setShowCanvas(true)

            canvasElement.width = psdFile.width
            canvasElement.height = psdFile.height

            context?.putImageData(imageData, 0, 0)
          } catch (e) {
            console.log(e)
          }
        }
      } else {
        readWithImageMagickCallback(
          new Uint8Array(data),
          (err, image) => {
            if (err) {
              console.log(err)
              return setInputError(err.message as string)
            } else if (image) {
              writeWithImageMagick(image, outputFormat)
                .then(out => {
                  setInputError(undefined)
                  const path = file.name.split('.')
                  if (path.length > 1) {
                    path.pop()
                  }
                  const blob = new Blob([out])
                  setOutput(blob)
                  setOutputFileName(
                    `${path.join('.')}.${outputFormat.toLowerCase()}`,
                  )
                  // downloadBlob(
                  //   blob,
                  //   `${path.join('.')}.${outputFormat.toLowerCase()}`,
                  // )

                  const or = new FileReader()
                  or.onloadend = () => {
                    setOutputString(or.result as string)
                  }
                  or.readAsDataURL(blob)
                })
                .catch(error => {
                  setInputError(error.message as string)
                })
            }
          },
        )
      }
    } else {
      setInputError(
        `File should be less than 16mb so it doesn't crash the browser.`,
      )
    }
  })
}, [files, outputFormat, canvasRef])
termhare commented 7 months ago

Not sure why the code is not getting sourcemapped to TypeScript (unminified), I see the //#sourceMappingURL=index.js.map in the dist/index.js file, is this fixable? I'll open another issue.

termhare commented 7 months ago

@pastelmind any ideas about why this might not be working in my examples?