phantomjs screen capture for pdf - canvas scaling - javascript

i use phantomjs to make a pdf out of the content on my site. And it looks great with canvas as the exception.
in my print.css / media css i have the canvas to 100%
canvas {
height : 100%!important;
width : 100%!important;
}
the canvas / content of canvas (which is a chart generated by chart.js) looks stretched / out of focus.
From reading various post i noticed that setting sizing through css is a no go, and i instead have to have it in-tag or set it through javascript directly on the canvas element.
however, thats not really an option since its already scaled/sized perfectly on the site. If i dont have the 100% height width, the canvas dont get jagged, but its WAY too big for my pdf format.
Whats the right way to go about fixing this, getting a crisp canvas for my screen capture which is sized for A4 / Standard pdf format?
Thanks in advance.

When dealing with a canvas the css 'height' and 'width' element determine the size of the literal DOM element <canvas> NOT the size of the display within. In order to set this try something like this:
// grab canvas attribute
var canvas = document.getElementById('canvas');
// size the display appropriately
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// don't forget to account for the window size changing later on
window.addEventListener('resize', function(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}, false);

Related

How can i get high resolution image from smaller canvas?

I am using one canvas in my web app and it's actual height and width are 500px. I am showing this canvas on screen as 500px square but i want image exported from this canvas as 1600px square. I have tried below code with no luck.
canvas.width = 1600;
canvas.style.width = 500;
Any help will be appreciated.
You can have the canvas display at 500px while still having a resolution of 1600px. Display size and resolution are independent. For resolution you set the canvas width and height properties. For display size you set the canvas style width and height properties.
// create a canvas or get it from the page
var canvas = document.createElement("canvas");
// set the resolution (number of pixels)
canvas.width = canvas.height = 1600;
// set the display size
canvas.style.width = canvas.style.height = "500px";
// get the rendering context
var ctx = canvas.getContext("2d");
To get the rendering to match the display size you need to scale up all rendering. You can do this by setting the transform scale to the canvas resolution divided by the display size
var scale = 1600 / 500; // get the scale that matches display size
ctx.setTransform(scale,0,0,scale,0,0);
Now when you render to the canvas you use the screen size coordinates.
ctx.fillRect(0,0,500,500); // fill all of the canvas.
ctx.fillStyle = "red"; // draw a red circle 100 display pixels in size.
ctx.beginPath();
ctx.arc(250,250,100,0,Math.PI * 2);
ctx.fill();
When you then save the canvas, what ever method you use as long as it is not screen capture the saved canvas will be 1600 by 1600 and all the rendering will be correctly positions and proportional
HTML
<canvas width="1600px" height="1600px" > </canvas>
CSS
canvas{
position :absolute;
transform:scale(0.3125);
left:-500px; //adjust
top:-350px; //adjust
}
Use transform:scale() to adjust size of your canvas
Now 1600 * 1600 will be the actual size of your canvas, so you can directly export images from your canvas
But in view it show as 500px * 500px beacuse it's scaled down, it dose not affect the image quality while exporting
Honest answer: you can't.
If you did, then you'd have found a way to losslessly compress data with less than 1/9th of the original size, and without any encoding, which is unarguably impossible.
What you can do is scale it up in a way that it at least doesn't get blurry. To do that, you need the final image to be an integer multiple of the previous canvas, so the browser won't apply anti-aliasing. Or if you want to use your own copying formula with putImageData that would get rid of anti-aliasing, you'll still get various incongruences and it would be very slow
In your case, the closest you could get is 1500x1500 ( 3*500x3*500 ). If your point was to process an image, you're not in luck, but if you just want to display something good enough, you can resort to various other tricks such as centering the canvas and using properties like box-shadow to make it clear that it's separate from the rest of the screen

Canvas height changed by EaselJS. How to avoid this?

I'm using Easel JS to develop Canvas based Solitaire game.
TODO: Set height for canvas and draw all elements inside of that.
Issue: EaselJS change the canvas height automatically.
References:
How I set the height:
var gameBoard = document.getElementById("board");
gameBoard.style.height = window.innerHeight+"px";
gameBoard.height = gameBoard.offsetHeight;
gameBoard.style.height = "auto";
Without knowing what your goals are, it's hard to tell what the problem is exactly... but setting the CSS "canvas.style.height" property to "auto" seems to be the problem. The width and height properties (not CSS) are still what you set them to originally (1300 x 501).
Remove this line and you will be left with a canvas that is the size of the window when the page is loaded.
gameBoard.style.height = "auto";
If you want your canvas to have as many pixels as there are in the window, you will need to listen for the "resize" event and apply the values back to your canvas.
window.addEventListener("resize", function(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});

How do we draw a resized image on an html5 canvas?

I have an image to be drawn on an html5 canvas. I want to resize this image to a certain height and width before being drawn onto the canvas. My code currently consists of:
img = new Image();
img.src = "vivo.jpg";
// calcuate new height and width
img.height = newheight;
img.width = newwidth;
$("#cnvs").attr({
"height" : newheight,
"width" : newwidth
});
context.drawImage(img, 0, 0);
The code only works if I include the width and height parameters in the context.drawImage() function itself. But what I don't understand is, why the image isn't resized already before the context.drawImage() function accesses it.
I have also printed alert statements to check "img.height" and "img.width" and it is indeed set to the values of newheight, newwidth. Why would the context.drawImage() access the old values of height and width?
Edit - Additionally, when I display the image on the webpage (not on the canvas, but just the webpage), the resized image is shown. But the canvas does not show the resized image. I don't understand why
The reason why it isn't working is because you are changing the properties of the <img> HTML node. These properties are layouting instructions how the node is displayed in the document (which are obsolete, by the way. Use CSS for layouting). The width and height of the image material displayed in the image-element is independent from this.

Print canvas contents

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?
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);
}
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).
I had in css width: 100% for canvas, which caused wrong scaling
Fixed by changing from 100% to 210mm
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.
Daniel's answer works great. Printing images instead of canvases is better. You don't need to write any JS hacks.

HTML5 canvas stroke() thick and fuzzy

I'm trying to allow the user to draw a rectangle on the canvas (like a selection box). I'm getting some ridiculous results, but then I noticed that even just trying the code from my reference here, I get huge fuzzy lines and don't know why.
it's hosted at dylanstestserver.com/drawcss. the javascript is inline so you can check it out. I am using jQuery to simplify getting the mouse coordinates.
The blurry problem will happen if you use css to set the canvas height and width instead of setting height and width in the canvas element.
<style>
canvas { height: 800px; width: 1200px; } WRONG WAY -- BLURRY LINES
</style>
<canvas height="800" width="1200"></canvas> RIGHT WAY -- SHARP LINES
For some reason, your canvas is stretched. Because you have its css property width set to 100%, it is stretching it from some sort of native size. It's the difference between using the css width property and the width attribute on the <canvas> tag. You might want to try using a bit of javascript to make it fill the viewport (see jQuery .width()):
jQuery(document).ready(function(){
var canvas = document.getElementById('drawing');
canvas.width(($(window).width()).height($(window).height());
var context = canvas.getContext('2d');
//...
The way I do it is to set the canvas element to a width and height in the css, and then set the canvas.width = canvas.clientWidth and canvas.height = canvas.clientHeight
var canvas = document.getElementById("myCanvas");
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
var context = canvas.getContext("2d");
You haven't indicated canvas size in pixels, so it is scaled up. It is 300x150 here. Try setting the width, height
On retina displays you also need to scale (in addition to the accepted answer):
var context = canvas.getContext('2d');
context.scale(2,2);
The css sizing issue mentioned in these comments is correct, but another more subtle thing that can cause blurred lines is forgetting to call make a call to context.beginPath() before drawing a line. Without calling this, you will still get a line, but it won't be smoothed which makes the line looks like a series of steps.
I found the reason mine was blurry was that there was a slight discrepancy between the inline width and the CSS width.
I have both inline width/height parameters AND css width/height assigned to my canvas, because I need to keep its physical dimensions static, while increasing its inline dimensions for retina screens.
Check yours are the same if you have a situation like mine.

Categories