Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better image processing support #25

Open
techmunk opened this issue Mar 15, 2022 · 2 comments
Open

Better image processing support #25

techmunk opened this issue Mar 15, 2022 · 2 comments

Comments

@techmunk
Copy link

I have issues using svg-to-png and the underlying pupperteer modules due to a proxy issue. I also did not like the size and overhead that these modules bring with them.

As a result of this, I started to investigate processing the generated SVG with regular image processing tools like imagemagick, or the npm package sharp, but found these had subtle issues with processing the SVG.

I was able to resolve these issues with the following custom command, which allowed me to use sharp in the following manner:

import sharp from 'sharp'
const svg = receiptline.transform(...)
const png = await sharp(svg).png().toBuffer()

Not only is this significantly quicker than puppeteer, it is also comes with a much smaller dependency size.

My custom command is as below:

const custom = {
  ...receiptline.commands.svg,
  ...{
    close: function() {
      return receiptline.commands.svg.close.bind(this)()
        .replace('>', '><rect width="100%" height="100%" fill="white"/>')
        .replace('<filter id="receiptlineinvert" x="0" y="0" width="100%" height="100%">', '<filter id="receiptlineinvert" x="-1%" y="-15%" width="102%" height="150%">')
    },
    text: function (text, encoding) {
      let p = this.textPosition;
      const attr = Object.keys(this.textAttributes).reduce((a, key) => a   ` ${key}="${this.textAttributes[key]}"`, '');
      this.textElement  = `<text${attr}>`
      for (const character of Array.from(text)) {
        const q = this.measureText(character, encoding) * this.textScale;
        const r = (p   q / 2) * this.charWidth / this.textScale;
        p  = q;
        this.textElement  = `<tspan x="${r}">${character.replace(/[ &<>]/g, r => ({' ': '&#xa0;', '&': '&amp;', '<': '&lt;', '>': '&gt;'}[r]))}</tspan>`;
      }
      this.textElement  = `</text>`
      this.textPosition  = this.measureText(text, encoding) * this.textScale;
      return '';
    },
  }
}

Most of these changes are transparent to the output, and will work fine in a browser, as well as with generic image processing tools. The only exception to this for which I have been unable to find a workaround for is the receiptlineinvert filter. Whilst what I have provided above seems to work well in an image processor or sharp, in chrome there is just a bit too much "padding" around the element.

Is there some way to incorporate these changes so that generic image processing tools can be used to process the svg?

@receiptline
Copy link
Owner

Thank you for using receiptline!

I have tried your custom command.
The processing speed was very fast and the text positioning was improved.
But in some cases, multibyte characters were out of place.

As you know, SVG implementations have compatibility issues.
So may not be positioned as expected.
In order for image processing tools to do the expected conversion, the must be used.

Currently, the most compatible SVG implementations are web browsers.
So we use puppeteer, although it is larger in size and overhead.

I think your custom command is a good solution under limited conditions.

Thank you!

@receiptline
Copy link
Owner

Your source code was a hint for us to support Thai characters.
Thank you!

And we will soon release a version that supports "sharp".

There are some limitations.
"sharp" minimizes the area of "invert" character decoration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants