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

Create svg without document #23

Open
jassler opened this issue Aug 27, 2022 · 2 comments
Open

Create svg without document #23

jassler opened this issue Aug 27, 2022 · 2 comments

Comments

@jassler
Copy link

jassler commented Aug 27, 2022

The chess-image-generator package uses node-canvas to create an image of a chess position. Unfortunately I'm a little unhappy with the results, as the chess pieces, which are stored as pngs, become a little blurry on the canvas (see for example here: https://bucket-lichess.vercel.app/api/board?fen=8/6p1/2prppp1/3k4/5PP1/2R5/PP5P/6K1&squares=d8,d6).

Is it possible to use the svgcanvas library to create an svg and convert it to a png image (all on the server)?

I tried to do the following, however it resulted in a document is not defined error which I am not sure how to solve.

const ChessImageGenerator = require('chess-image-generator')
import { createCanvas } from 'canvas'
import { Context } from 'svgcanvas'
const sharp = require('sharp')

// ig <=> ChessImageGenerator object
async function generateBuffer(ig) {
  if (!ig.ready) {
    throw new Error("Load a position first");
  }

  const canvas = createCanvas(ig.size, ig.size)
  const context2D = canvas.getContext('2d')

  // ERROR: document not defined
  const ctx = new Context({
    width: ig.size,
    height: ig.size,
    ctx: context2D,
    document: undefined,
  })
  // create chess pattern and add chess pieces
  for (let i = 0; i < 8; i += 1) {
    for (let j = 0; j < 8; j += 1) {
      const coords = cols[col(j)] + row(i);

      if ((i + j) % 2 === 0) {
        ctx.beginPath();
        ctx.rect(
          ((ig.size / 8) * (7 - j + 1) - ig.size / 8),
          ((ig.size / 8) * i) + ig.padding[0],
          ig.size / 8,
          ig.size / 8
        );
        ctx.fillStyle = ig.dark;
        ctx.fill();
      }
      // ...
      // add chess pieces that are stored as svg files
      // ...
    }
  }

  return sharp(ctx.getSerializedSvg()).png().toBuffer()
}
@cmdcolin
Copy link

cmdcolin commented Aug 28, 2022

(i just watch this repo, not a dev, and currently using canvas2svg) i've done something like this under canvas2svg

import { JSDOM } from 'jsdom'
const { document } = new JSDOM(`...`).window
global.document = document


I copied it from https://github.com/jsdom/jsdom mindlessly and even left the ... in the production code lol...still works. you may also be able to pass the document object directly to the constructor. in my code, I also have a convert to png but i use a command line tool to do it (rsvg-convert), i didn't evaluate other options to do it though, so there might be others

@jassler
Copy link
Author

jassler commented Sep 12, 2022

Thank you for your suggestion, this was immensely helpful! Also apologies for taking so much time to respond - I shouldn't have opened an issue right before going on vacation.

Unfortunately now I get a DOMMatrix is not defined error message at the same place. I've had a look at the DOMMatrix npm package, but it doesn't quite look like it's solving the problem I have. Or did I get something wrong?

For my next.js application, I've added the following code to pages/api/board.js and called localhost:3000/api/board.

import { createCanvas } from 'canvas'
import { Context } from 'svgcanvas'
import { JSDOM } from 'jsdom'
const sharp = require('sharp')
const { document } = new JSDOM(`...`).window
global.document = document

export default function handler(req, res) {
  const canvas = createCanvas(80, 80)
  const context2D = canvas.getContext('2d')

  // code fails here. constructor needs DOMMatrix
  const ctx = new Context({
    width: 80,
    height: 80,
    ctx: context2D
  })
  for (let i = 0; i < 80; i += 10) {
    for (let j = 0; j < 80; j += 10) {
      ctx.beginPath()
      ctx.rect(i, j, 10, 10)
      ctx.fillStyle = ((i + j) % 10 === 0) ? '#FFFFFF' : '#000000'
      ctx.fill()
    }
  }

  return sharp(ctx.getSerializedSvg()).png().toBuffer()
}

Would you be able to give a complete example?

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