Html2Canvas Scale Issue - javascript

I am trying to convert my HTML code to image using Html2Canvas Library. In order to get a high-quality image in the result, I am using scale property with value 2.
html2canvas(element, {
scale: 2
}).then(function(canvas) {
getCanvas = canvas;
var imageData = getCanvas.toDataURL("image/png", 1);
var newData = imageData.replace(/^data:image\/png/, "data:application/octet-stream");
});
Now this newData is sent to php using AJAX and to save I use below code -
define('UPLOAD_DIR', 'assets/output_images/');
$image_parts = explode(";base64,", $_POST['ImageUri']);
$image_type_aux = explode("image/", $image_parts[0]);
$image_base64 = base64_decode($image_parts[1]);
$file_name = uniqid().'.png';
$file = UPLOAD_DIR . $file_name;
$success =file_put_contents($file, $image_base64);
But it always gives me the same image as it was with the scale with value 1 and there is no improvement in the final image.
NOTE: My div dimension is 369 * 520 and generated image size is always 369*520 with scale value 1 or 2 or 3 etc.
Thanks in advance

this just can't be... if you use bitmap image multiply size won't be better quality, but lower. You'll create fake pixels as copy of originals pixels at best.
For a real scale with image(with no quality loss) you've to use scalar image such SVG who won't be in CANVAS who use bitmap image but in the DOM as regular Elements.
Obviously you can use optionals parameters with
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
where width and height will be final width and height in pixels(so no higher quality image just a larger size and a lower quality because pixels are multiplicated with fake pixels cloned form original size).
I hope i'be got your question fine.
edit: notice that to draw in canvas you've to create a image object such new Image('imagename.png'); at least but you can add the alt attributes and other stuff like an id or such. for more example source W3schools.org canvas.drawImage(...)

Related

FabricJS toDataURL Multiplier Not Returning Correct Height/Scaling Correctly

I have a FabricJS canvas that has to be converted to a PDF that matches the exact fractional inches for print production. Per other questions I have received, my printer is a large format printer and actually needs this/is capable to printing to the exact decimal as follows:
Width (Inches): 38.703111
Height (Inches): 25.999987
Since the requirements are a DPI of 300, I obtained the pixels by multiplying the width and height x 300 as follows:
38.703111 inches x 300 = 11610.9333 px
25.999987 inches x 300 = 7799.9961 px
With these measurements in hand, I created a FabricJS canvas that users could edit and then convert to an image (will need to figure out how to convert it to a PDF server side later/suspecting node.js with the pdfkit module).
Since it is not usable to have a 11610 x 7799px Canvas on a page, I set the size of the canvas as follows:
Width: 650px
Height: 436.6571863 (Orginal Height * New Width / Orginal Width = new height)
Here is what my Canvas looks like (HTML) and corresponding JavaScript code to render it:
<canvas id="c" width="650" height="436.6571863" style="border:1px solid"></canvas>
var canvas = new fabric.Canvas('c', {});
Users are able to edit the canvas and then convert it to an image, but this is the problem occurs. I attempt to scale the canvas via FabricJS' toDataURL method using a multiplier of 17.86297431 (Original Width in Pixels / New Width in Pixels and Original Height in Pixels / New Height in Pixels both equal a multiplier of 17.86297431) as follows:
var dataURL = canvas.toDataURL({
format: 'png',
multiplier: 17.86297431
})
document.write('<img src="' + dataURL + '"/>');
However, once it scales, the width of my outputted image appears correct at 11610, but the height is off at 7788 when it should be 7799. This output does not show fractions, but rather just the whole pixels when I inspect the element.
My question is, how can I get my FabricJS Canvas to properly scale up to the pixels (or inches) so I can have my PDF (converted from my PNG) in the correct dimensions? Is this an issue with pixel fractions not being respected/what can I do about this?
You need to set NUM_FRACTION_DIGITS in fabric.js file
By default this value = 2, you can change it and set for example = 9
Now your multiplier = 9,0123456;
But for Fabric JS your multiplier = 9,012;
If your will set NUM_FRACTION_DIGITS = 9 you will get your multiplier = 9,0123456;
Here the proof link: http://fabricjs.com/docs/fabric.Object.html
That is what you need: NUM_FRACTION_DIGITS - Defines the number of fraction digits to use when serializing object values. You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc.
You should not work with float dimensions, that is a bad idea. Why don't you use Math.ceil for your dimensions. This will make no visual difference and this is the approach usually used.
Also you could use the Zoomify alghoritm approach to determine your scale factor. You can start from the original dimensions and divide them by 2 until you get a value smaller than a maximum value defined by you, which you consider acceptable. That way your scale factor will be a power of 2.

Automatically Rotate Image Based on Text Content - PHP/JS

I would like to be able to automatically rotate an image based off of its text content so that the text will be displayed properly (vertically). I would prefer the language to be either Javascript or PHP.
Bad Example
Proper Example
For instance, GIMP and PS do it when importing a picture like such:
Q
How can I accurately auto rotate images with JS/PHP so that the text shows up properly (vertically, if you would)?
--NOTE--
I do not want to rotate based off of the "EXIF orientation" data, but rather by the orientation of the text in the image. Apparently the EXIF data only tracks the orientation the picture was taken in respects to the ground.
One possible solution I thought of would be to use OCR to detect characters in an image and test the image in all 4 orientations (rotated 90 degrees 3 times, in addition to original orientation). Whichever position returns the highest matched characters is most likely the proper orientation for the text.
One could use the following library for PHP: https://github.com/thiagoalessio/tesseract-ocr-for-php. In coordination with imagerotate(), one could find out the best orientation for the image based off of the amount of characters returned from OCR.
In Theory
require_once '/path/to/TesseractOCR/TesseractOCR.php';
$filename='path/to/some/image.jpg';
$photo = // create photo from $filename
$results = array();
for ($i=0; $i<4; $i++) {
$new = imagerotate($photo, $i*90, 0);
$new_path = // save the new rotated photo and get path
$tesseract = new TesseractOCR($new_path);
$results[$i] = strlen($tesseract->recognize());
}
/* Highest output is the best orientation for the image in respects to the text in it */
echo "Original Orientation: " . $results[0];
echo "Rotated 90 degrees: " . $results[1];
echo "Rotated 180 degrees: " . $results[2];
echo "Rotated 270 degrees: " . $results[3];
Pros
- Utilizes existing libraries (Tesseract with PHP wrapper, imagerotate php function)
Cons
- Computationally intensive. One image needs to be rotated 3 times & OCR 4 times
The solution you are asking for is not quite the same as your example. At first I thought you wanted a smart function that detected the text, but it is simpler than that with your example.
You need to look at the EXIF data. Fortunately, I have doe this multiple times. To correct an image that has been rotated, you can use the following function, which I wrote for correcting images taken on tablets/phones but will be displayed on computers. The input must be a filename for a JPG image.
function fix_image($filename, $max_dimension = 0)
{
$exif = exif_read_data($filename, 0, true);
$o = $exif[IFD0][Orientation];
// correct the image rotation
$s = imagecreatefromjpeg($filename);
if ($o == 1) { }
if ($o == 3) { $s = imagerotate($s, 180, 0); }
if ($o == 6) { $s = imagerotate($s, 270, 0); }
if ($o == 8) { $s = imagerotate($s, 90, 0); }
// export the image, rotated properly
imagejpeg($s, $filename, 100);
imagedestroy($s);
if ($max_dimension > 0)
{
$i = getimagesize($filename);
// reopen image for resizing
// Use the known orientation to determine if it is portrait or landscape
if ($i[0] > $i[1])
{
// landscape: make the width $max_dimension, adjust the height accordingly
$new_width = $max_dimension;
$new_height = $i[1] * ($max_dimension/$i[0]);
} else {
// portrait: make the height $max_dimension, adjust the width accordingly
$new_height = $max_dimension;
$new_width = $i[0] * ($max_dimension/$i[1]);
}
$s = imagecreatefromjpeg($filename);
$n = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($n, $s, 0, 0, 0, 0, $new_width, $new_height, $i[0], $i[1]);
imagejpeg($n, $filename, 100);
imagedestroy($n);
imagedestroy($s);
}
}
If the exif rotation is not acceptable you will likely need to do some image processing. This will never be 100% accurate. I am not sure the tessaract solution proposed by karns will work very well though since tessaract needs a fair amount of training and you might always encounter fonts you have not trained. Additionally, a comment on how to detect orientation of a scanned document? suggests that tessarct autorotates the image for text detection so you might get similar results on the rotated images.
An alternative is to use opencv via a php wrapper, e.g. https://github.com/mgdm/OpenCV-for-PHP (I have not used the wrapper myself). You can then do a line histogram for example pictures see the accepted answer on word segmentation using opencv. This way you can determine if the picture is horizontally or vertically oriented. Afterwards (and after possible correction of vertically oriented pictures) you could try to determine whether or not the text is upside down, google for detect upside down text one of the results, for example, suggests counting the dots in the upper and lower parts of a line. Again, this will never be 100% accurate.

Given a canvas object, how can I get a scaled down "version" of it as an image/Base64-encoded byte array?

I am using html2canvas to take "screenshots" of the current browser window. I would like to save the screenshot as Base64 encoded data which can later be used to create an html img. For example, I might want to save the String that canvas.toDataURL() returns in the browser's local storage, and retrieve it later.
The following code works fine:
html2canvas(document.body, {
onrendered: function(canvas) {
localStorage.setItem('screenshot', JSON.stringify(canvas.toDataURL()));
}
});
I can later retrieve it and create an image from it, for example using Angular:
<img data-ng-src="{{imgData}}"/>
What I want to do though, is have a scaled down version of the screenshot. I can simply scale the resulting image using CSS, but I want to save storage space. In other words, I want the String encoding to represent an image that is, for example, 200 pixels wide instead of (say) 1000 pixels wide. I do not care about the quality of the image.
This is the closest I've come:
saveScreenshot = function(name, scaleFactor) {
html2canvas(document.body, {
onrendered: function(canvas) {
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.scale(scaleFactor, scaleFactor);
ctx.drawImage(img, 0, 0);
//canvas.width *= scaleFactor;
//canvas.height *= scaleFactor;
localStorage.setItem('screenshot', JSON.stringify(canvas.toDataURL()));
}
img.src = canvas.toDataURL();
}
});
}
This almost works - the "picture" is scaled correctly, but the resulting image is still the same size as the original, with the rest of the space apparently filled in with transparent pixels.
If I uncomment the 2 lines above which set the canvas' height and width, then the image is the right size, but it's all blank.
I feel like I'm close - what am I missing?
.toDataURL will always capture the entire canvas, so to get a smaller image you must create a second smaller canvas:
var scaledCanvas=document.createElement('canvas');
var scaledContext=scaledCanvas.getContext('2d');
scaledCanvas.width=canvas.width*scaleFactor;
scaledCanvas.height=canvas.height*scaleFactor;
Then scale the second canvas and draw the main canvas onto the second canvas
scaledContext.scale(scaleFactor,scaleFactor);
scaledContext.drawImage(canvas,0,0);
And finally export that second canvas as a dataURL.
var dataURL = scaledCanvas.toDataURL();
Warning: untested code above...but it should put you on the right track!

resize image in canvas keeping resolution

I'm using a canvas to resize client-side an image before uploading it on the server.
maxWidth = 500;
maxHeight = 500;
//manage resizing
if (image.width >= image.height) {
var ratio = 1 / (image.width / maxWidth);
}
else {
var ratio = 1 / (image.height / maxHeight);
}
...
canvas.width = image.width * ratio;
canvas.height = image.height * ratio;
var resCtx = canvas.getContext('2d');
resCtx.drawImage(image, 0, 0, image.width * ratio, image.height * ratio);
This works, since on the server is saved the resized image, but there are two points i would like to improve:
image weight in KB is important to me; i want a very light weight image; even if it is resized, the canvas' image is still too heavy; i can see that the image saved on the server has a resolution of 96 DPI and 32-bit of color-depth even if the original image has a resolution of 72 DPI and 24-bit of color-depth; why? Can i set the canvas' image resolution?
the resized image does not look very nice, because the resizing proces introduce distortion... I've tried the custom algorithm by GameAlchemist in this post:
HTML5 Canvas Resize (Downscale) Image High Quality?
getting a very nice result, but then the resized image was more heavy in KB than the original one! Is there a good algorithm in order to get quality resized images keeping them lightweight?
DPI does not matter at all with images. An image at 1k x 1k will be just the same if 892478427 DPI or as 1 DPI. DPI is arbitrary in this context so ignore that part (it is only used as information for DTP programs so they know the relative size compared to document size). Images are only measured in pixels.
Canvas is RGBA based (32-bits, 24-bit colors + 8 bit alpha channel) so it's optimal form for exporting images will be in this format (ie. PNG files). You can however export 24-bits images without the alpha channel by requesting JPEG format as export format.
Compression is basically signal processing. As in all forms of (lossy) compression you try to remove frequencies in the signal in order to reduce the size. The more high frequencies there is the harder it is to compress the image (or sound or anything else signal based).
In images high frequency manifests as small details (thin lines and so forth) and as noise. To reduce the size of a compressed image you need to remove high frequencies. You can do this by using interpolation as a low-pass filter. A blur is a form of low-pass filter as well and they work in the same way in principle.
So in order to reduce image size you can apply a slight blur on the image before compressing it.
The JPEG format support quality settings which can reduce the size as well although this using a different approach than blurring:
// 0.5 = quality, lower = lower quality. Range is [0.0, 1.0]
var dataUri = canvas.toDataURL('image/jpeg', 0.5);
To blur an image you can use a simple and fast method of scaling it to half the size and then back up (with smoothing enabled). This will use interpolation as a low-pass filter by averaging the pixels.
Or you can use a "real" blur algorithm such as Gaussian blur as well as box blur, but only with a small amount.

Hand-create images in javascript and draw with respect to the alpha channel?

I'm currently trying to create a page with dynamically generated images, which are not shapes, drawn into a canvas to create an animation.
The first thing I tried was the following:
//create plenty of those:
var imageArray = ctx.createImageData(0,0,16,8);
//fill them with RGBA values...
//then draw them
ctx.putImageData(imageArray,x,y);
The problem is that the images are overlapping and that putImageData simply... puts the data in the context, with no respect to the alpha channel as specified in the w3c:
pixels in the canvas are replaced wholesale, with no composition, alpha blending, no shadows, etc.
So I thought, well how can I use Images and not ImageDatas?
I tried to find a way to actually put the ImageData object back into an image but it appears it can only be put in a canvas context. So, as a last resort, I tried to use the toDataURL() method of a 16x8 canvas(the size of my images) and to stick the result as src of my ~600 images.
The result was beautiful, but was eating up 100% of my CPU...(which it did not with putImageData, ~5% cpu) My guess is that for some unknown reason the image is re-loaded from the image/png data URI each time it is drawn... but that would be plain weird... no? It also seems to take a lot more RAM than my previous technique.
So, as a result, I have no idea how to achieve my goal.
How can I dynamically create alpha-channelled images in javascript and then draw them at an appreciable speed on a canvas?
Is the only real alternative using a Java applet?
Thanks for your time.
Not knowing, what you really want to accomplish:
Did you have a look at the drawImage-method of the rendering-context?
Basically, it does the composition (as specified by the globalCompositeOperation-property) for you -- and it allows you to pass in a canvas element as the source.
So could probably do something along the lines of:
var offScreenContext = document.getCSSCanvasContext( "2d", "synthImage", width, height);
var pixelBuffer = offScreenContext.createImageData( tileWidth, tileHeight );
// do your image synthesis and put the updated buffer back into the context:
offScreenContext.putImageData( pixelBuffer, 0, 0, tileOriginX, tileOriginY, tileWidth, tileHeight );
// assuming 'ctx' is the context of the canvas that actually gets drawn on screen
ctx.drawImage(
offScreenContext.canvas, // => the synthesized image
tileOriginX, tileOriginY, tileWidth, tileHeight, // => frame of offScreenContext that get's drawn
originX, originY, tileWidth, tileHeight // => frame of ctx to draw in
);
Assuming that you have an animation you want to loop over, this has the added benefit of only having to generate the frames once into some kind of sprite-map so that in subsequent iterations you'll only ever need to call ctx.drawImage() -- at the expense of an increased memory footprint of course...
Why don't you use SVG?
If you have to use canvas, maybe you could implement drawing an image on a canvas yourself?
var red = oldred*(1-alpha)+imagered*alpha
...and so on...
getCSSCanvasContext seems to be WebKit only, but you could also create an offscreen canvas like this:
var canvas = document.createElement('canvas')
canvas.setAttribute('width',300);//use whatever you like for width and height
canvas.setAttribute('height',200);
Which you can then draw to and draw onto another canvas with the drawImage method.

Categories