1
votes

I have an HTML/JavaScript program that uses the HTML5 canvas to generate a pretty graph:

enter image description here

I want to include this graph in a book that I'm producing with LaTeX. Unfortunately, when I print this page to PDF, I get a bitmap—presumably because canvas is a bitmap.

I just checked and apparently if I had developed this with SVG instead of with HTML5 canvas I would be good to go. But I didn't. So is there any way to generate line art from HTML5 Canvas, or should I just bite the bullet and re-implement this as SVG?

(I used canvas instead of svg because I thought that Canvas was preferred, more-modern, and more efficient. Was I mistaken?)

EDIT: Here is my JavaScript for drawing each arrow. I believe that I can readily port this to SVG using svg.js

    /* Draw a Piece at its rotation at the position row,col */
    drawRotatedPiece(p) {
        let x = this.x(p.col);
        let y = this.y(p.row);
        this.ctx.beginPath();
        this.ctx.save();
        this.ctx.translate( x, y);
        this.ctx.fillStyle='white';
        this.ctx.fillRect( -this.radius, -this.radius, this.radius*2, this.radius*2 );
        this.ctx.rotate( p.angle * Math.PI / 180);
        this.ctx.moveTo( 0, -this.radius );
        this.ctx.lineTo( -this.radius, 0 );
        this.ctx.lineTo( +this.radius, 0 );
        this.ctx.closePath( );
        this.ctx.fillStyle='black';
        this.ctx.fill();
        this.ctx.fillRect( -this.radius/2.0, 0, this.radius, this.radius*2/3.0 );
        this.ctx.restore();

    }
1
Pixels are great when the output size is fixed. Start talking DPIs and output size like one does when concerned with print and you start needing images of different sizes to fill a given area. Think about your choice of LaTeX - you're not trying to store images of the pages in a book, you're trying to store the data used to render the images of the text. You should do the same thing with the images - store their definition, not their implementation. Yes, just redraw with SVG.enhzflep
You will somehow have to reproduce the draw calls of the HTML5 canvas API, such as by writing a wrapper class that wraps an HTML5 canvas and translates those calls into calls that write SVG or PDF commands. However, this is not exactly easy to do, depending on how you're using the HTML5 canvas API. You should edit your question to include code that shows how you generated the graphic using the HTML5 canvas API.Peter O.
In any case, your graphic looks simple enough -- just a bunch of stroked and filled paths -- that my note on the Essentials of SVG may help you translate the graphic to SVG.Peter O.
No you don’t need svg.js. Your canvas code is quite simple. It wouldn’t be too much hassle to manually port it to svg. Just bite that bullet. And if you’re not familiar with svg, well this is the perfect chance to learn something new. Basically you just need to learn the "d" attribute of path element.hackape
Last I checked pdf didn't support svg out of the box either. Their vector graphics were not compatible with svg, so instead of converting this to svg, rather learn their vector format (I believe using their GUI might be easier though).Kaiido

1 Answers

2
votes

The following is code that generates SVG that reproduces the rotated piece you have given in your question. The method is drawRotatedPiece(x, y, radius, angle).

// Generate a SVG path command
function xycmd(cmd,x,y){
 return cmd+ x + " " + y;
}

function rectpathxy(x,y,w,h){
   return xycmd("M", x, y) +
          xycmd("L", x+w, y) +
          xycmd("L", x+w, y+h) +
          xycmd("L", x, y+h) +
          "Z";
}

function drawRotatedPiece(x, y, radius, angle){
  trans=[];
  svg="";
  svg+="<path style='stroke:red;fill:white'";
  svg+=" transform='translate("+x+","+y+")'";
  svg+=" d='";
  svg+=rectpathxy(-radius, -radius, radius*2, radius*2);
  svg+="'/>";
  svg+="<path style='stroke:none;fill:black'";
  svg+=" transform='translate("+x+","+y+") rotate("+angle+")'";
  svg+=" d='";
  var w=radius;
  var h=radius*2/3.0;
  svg+= xycmd("M", 0, -radius) +
        xycmd("L", -radius, 0) +
        xycmd("L", +radius, 0) +
        "Z" +
        xycmd("M", x,     y) +
        xycmd("L", x+w,   y) +
        xycmd("L", x+w, y+h) +
        xycmd("L", x,   y+h) +
        "Z";
  svg+="'/>";
  return svg;
}

In case you need to generate any other graphics in SVG format (especially simple graphics like yours that are just a bunch of stroked and filled paths), my note on the Essentials of SVG may help you translate those graphics to SVG.