Print canvas contents

2020-02-06 09:43发布

问题:

var print = document.createElement('button');
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');

canvas.width = 300;
canvas.height = 100;

ctx.fillStyle = '#000';
ctx.font = '15px sans-serif';
ctx.fillText('Fill Text, 18px, sans-serif', 10, 20);

print.innerHTML = 'Print';

document.body.appendChild(print);
document.body.appendChild(canvas);

print.addEventListener('click', function () {
    window.print();
});

http://jsfiddle.net/vpetrychuk/LWup5/.

As you can see text in the canvas displays ok, but after clicking "Print" button (and saving page as PDF) output image becomes ugly.

Any chance to print the canvas contents without blur?

回答1:

You need to make the actual canvas at print size then scale it on screen using CSS rules.

The browser will always use the internal bitmap size first and adjust that to the print or screen. If the bitmap is then of high resolution you will get better result on the print.

But mind you though, you will need to scale every coordinate and size when you print to the canvas. You will also need to prioritize screen versus print as one of them will look worse (if you prioritize print it will not look super on screen and vica verse).

Here is a modified example of your canvas which is now equivalent of 300 DPI (versus default 96 DPI). You can see it looks about the same on screen but will be much sharper when you print it.

/// conversion factor for scale, we want 300 DPI in this example
var dpiFactor = 300 / 96,
    width = 400,
    height = 100;

/// set canvas size representing 300 DPI
canvas.width = width * dpiFactor;
canvas.height = height * dpiFactor;

/// scale all content to fit the 96 DPI display (DPI doesn't really matter here)
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';

/// scale all sizes incl. font size
ctx.font = (15 * dpiFactor).toFixed(0) + 'px sans-serif';

/// scale all positions
ctx.fillText('Fill Text, 18px, sans-serif', 10 * dpiFactor, 20 * dpiFactor);

Simply use wrapper functions to do all the math for you:

function fillText(txt, x, y) {
    ctx.fillText(txt, x * dpiFactor, y * dpiFactor);
}


回答2:

You can try to detect when printing is going to happen (e.g. using Webkit window.matchMedia) but the actual printing is done using a scaling that is not under your control so creating a perfect 1x1 scaled canvas for sharp graphics is impossible.

You can replace the canvas with an high-enough resolution version when you detect printing, but the exact real size of the output is unknown. It's like if the user has a zoomed view of the page... even if canvas has been drawn sharply the browser can zoom it making the result blurry anyway.

Probably for text the best quality option is using a regular text div overimposed on the canvas instead of using fillText (of course this won't work for all use cases).



回答3:

I had in css width: 100% for canvas, which caused wrong scaling

Fixed by changing from 100% to 210mm



回答4:

A better solution is to use the canvas.toDataURL() method and then set the resulting string to the src of an img. When doing this, generate the canvas through Javascript and never actually append it to the body of the page.