JSPDF with autoTable addImage - image

I'm currently working on creating pdf files with jspdf and the AutoTable plugin.
My plan is to create a table like this:
I have the images as local urls, and I'm trying to add them to the pdf using the new Image and adding them as .src to the image.
When I directly run the jspdf.addImage function with the image, the images display correctly.
But I'm struggling to get the correct scaling to work. So I wanted to use the width and height properties from the image, but for this to work you need to wait for the image to load.
I tried to use the onload function, but it stops rendering the table in general, because it skips over the function and continues with the next table before the images load.
If you have any suggestions of how to get something like this to work it would be greatly appreciated. The images all have variable resolution and need to be scaled down to properly fit in the table. I'll paste my working code (but without the height and width scaling) below.
doc.autoTable({
head: [{comments: 'Photos'}],
body: body,
styles: {
lineColor: [0, 0, 0],
lineWidth: 0.4,
},
headStyles: {
fillColor: [191, 191, 191],
textColor: [0, 0, 0],
},
didDrawCell: function (data) {
if (data.section === 'body') {
console.log(data);
const image = new Image();
// image.onload = function() {
// console.log(this);
// const width = this.width;
// const height = this.height;
// console.log({width, height})
// doc.addImage(
// this,
// 'JPEG',
// data.cell.x + 5,
// data.cell.y + 2,
// )
// }
image.src = QuestionPhotos.link(photosObject[data.cell.raw])
doc.addImage(
image,
'JPEG',
data.cell.x + 5,
data.cell.y + 2,
)
}
}
The commented out part of this code was my other attempt where I would add the image after it was loaded, but this made the image not appear at all in the pdf.

I have been able to solve most of my question using the following functions:
The scaling I was able to fix by storing the width and height together with my images. I then set my height to be fixed (in my case 80mm) and calculated the width like this: photo.height / photo.width * 80.
I tried the other functions Ihsan mentioned, but without any luck.
The didDrawCell is used for inserting the images after the cell has been drawn. willDrawCell draws the image behind the table. didParseCell will insert the images in the top corner of the page instead of in the table itself.
I also initially used the didParseCell to increase the size of the cells to be the correct size for the images, but later on changed this to adding the style when creating the body of the table.
body.push([{content:'', styles: {minCellHeight: height + 10}}]);
I kept track of the row index to know at what rows to insert the image and so that the sizing is correct.

Related

CSRF token error when generating data from canvas

I'm using a package called laravel-admin.
I created a custom signature field with popular SignaturePad package, the data got converted into image successfully. However when I tried to submit the form I've got an error saying CSRF token miss match.
Here's my code for rendering SignaturePad class:
public function render()
{
$this->script = <<<EOT
var canvas = document.getElementById('signature-pad');
// Adjust canvas coordinate space taking into account pixel ratio,
// to make it look crisp on mobile devices.
// This also causes canvas to be cleared.
function resizeCanvas() {
// When zoomed out to less than 100%, for some very strange reason,
// some browsers report devicePixelRatio as less than 1
// and only part of the canvas is cleared then.
var ratio = Math.max(window.devicePixelRatio || 1, 1);
canvas.width = canvas.offsetWidth * ratio;
canvas.height = canvas.offsetHeight * ratio;
canvas.getContext("2d").scale(ratio, ratio);
}
window.onresize = resizeCanvas;
resizeCanvas();
var signaturePad = new SignaturePad(canvas, {
backgroundColor: 'rgb(255, 255, 255)' // necessary for saving image as JPEG; can be removed is only saving as PNG or SVG
});
$('button[type="submit"]').click(function() {
console.log(signaturePad.toDataURL());
$('input[type=hidden]').val(signaturePad.toDataURL());
});
EOT;
return parent::render();
}
If I remove the line below then I won't get it, but obviously my data is not sent. The way it works is I converts the value of the image and put it to the hidden input field to be sent. Can anyone help?
$('button[type="submit"]').click(function() {
console.log(signaturePad.toDataURL());
$('input[type=hidden]').val(signaturePad.toDataURL());
});
Apparently I forgot that laravel gives hidden input containing CSRF token that I am changing all together with jQuery code
$('button[type="submit"]').click(function() {
console.log(signaturePad.toDataURL());
$('input[type=hidden]').val(signaturePad.toDataURL());
});
Thanks everyone

what are the major differences between image asset and bitmap factory plugin

I'm trying to resize my image size before uploading it to server. I got to know that one can resize the image by keeping its aspect ratio using image asset. Though the image size was reduced the quality is very poor, and one more option i found is bitmap factory.
So, by using bitmap does the image quality is better than using image asset. kindly clarify this. Since the plugin is giving lot of errors currently. i don't want to waste time solving those if i don't get better quality images.
When it comes to resizing the image, both image-asset module and bitmap factory plugin does the same job.
Apart from resizing, the bitmap factory plugin also allows you to draw objects / write text over image. I'm not sure how it makes you a difference with image quality, but if you check the code it is almost the same for resizing.
I tried with both image resizing, and i don't know why i see some
better quality image from bitmap than image asset
. you can check yourself with this below code.
For Bitmap
let w = imageSourceModule.fromFile(img).width;
let h = imageSourceModule.fromFile(img).height;
var bmp = BitmapFactory.create(w, h);
const asset_1 = new ImageAsset(img);
imageSourceModule.fromAsset(asset_1)
.then(img_1 => {
bmp.dispose(function (b) {
b.insert(BitmapFactory.makeMutable(img_1));
// ## Max dimension. Respects aspect ratio.
var b2 = b.resizeMax(250);
var thumb_image = b2.toImageSource();
console.log("-----thumb_image------");
console.log(thumb_image);
if (thumb_image) {
console.log("bit map File successfully deleted....!");
thumb_image.saveToFile(pathDest_1, "jpg");
}
});
})
For Image Asset
const asset = new ImageAsset(img);
asset.options = {
width: 250,
height: 250,
keepAspectRatio: true,
autoScaleFactor: true,
};
imageSourceModule.fromAsset(asset)
.then(img => {
img.saveToFile(pathDest, "jpg");
})

FineUploader scale thumbnails by one dimension (i.e. maxHeight or maxWidth rather than maxSize)

The library takes a maxSize parameter for scaling, which applies to the longest of both dimensions. It seems that the work-around solution to scale by one dimension would be to manually run scaleImage() in an onSubmitted callback by calculating what the maxSize should be based on the original image size to get a result with the desired height or width, but this has its own hurdles:
It makes sense that using addFiles() inside of a onSubmitted callback would trigger another onSubmitted event; but if I use addFiles() to add the thumbnail, the thumbnail shows up in the UI list, and this triggers another onSubmitted causing another thumbnail to be generated, which keeps going in a loop.
I need to generate a thumbnail retrained by (a maxHeight of 240 pixels and a maxWidth of 320 pixels) and upload the thumbnail to a separate S3 bucket when uploadStoredFiles() is called, without triggering another onSubmitted event and without showing the thumbnail as a "duplicate" entry in the UI file list. What is the best way to do this in Fine-Uploader?
Some sample code:
function makeThumbnail() {
// FIXME to avoid duplicate, put this in the compression success
var thumbnailPromise = uploader.scaleImage(id, {
maxSize: 123,
quality: 45,
customResizer: !qq.ios() && function (resizeInfo) {
return new Promise(function (resolve, reject) {
pica.resizeCanvas(resizeInfo.sourceCanvas, resizeInfo.targetCanvas, {}, resolve)
});
}
});
thumbnailPromise.then(
function (blob) {
console.log(URL.createObjectURL(blob));
uploader.addFiles(blob);
},
function (err) {
}
);
}
Before you pass a scaled Blob into addFiles, simply add a custom property to the Blob object, something like blob.myScaledImage = true. Then, when handling an onSubmitted callback, retrieve the associated Blob using the getFile API method. If the Blob contains your custom property, don't re-scale it.

How do I create a group containing multiple groups of images in fabricjs

I need to dynamically add groups of multiple groups of images to the canvas, these "multi-group" will react differently depending on which of he contained groups has been dragged over by another group. I need these multi-groups to be able to be selected, rotated and dragged around the screen.
Ive chosen to use FabricJS and create images using fabric.Image.fromURL, manipulate, then add them to the canvas in a callback function. To add multiple images to a group, you nest more images in cascading callback functions as shown in this tutorial
I cant find a way to get a reference to a group of images created this way so I can add another group of images. Declaring a variable before any calls to fabric.Image.fromURL and trying to assign a reference to a created image doesnt work. The only way Ive got something working is a horrible desparate hack of adding such a group to the canvas then setting it as the active object then creating a new group and adding that active object to the group, this is soooo wrong and I dont think it will work for 3 groups. Heres my last coding attempt;
fabric.Image.fromURL('images/icon_station.png', function(img) {
var img1 = img;
fabric.Image.fromURL('images/icon_curve.png', function(img) {
img.set({left: 64,top: 64,angle: -90});
var img2 = img;
fabric.Image.fromURL('images/icon_curve.png', function(img) {
var img3 = img;
var tile_loop = new fabric.Group([img1, img2, img3]);
tile_loop.set({left: 0, top: 0, height: 64, width: 64});
canvas.add(tile_loop).setActiveObject(tile_loop);
});
});
});
fabric.Image.fromURL('images/icon_station.png', function(img) {
img.set({left: 64,top: 64});
var img1 = img;
fabric.Image.fromURL('images/icon_straight.png', function(img) {
img.set({left: 64,top: 64});
var img2 = img;
fabric.Image.fromURL('images/icon_curve.png', function(img) {
img.set({left: 64,top: 64});
var img3 = img;
var tile_loop = new fabric.Group([img1,img2,img3,canvas.getActiveObject()]);
tile_loop.set({left: 128, top: 128, height: 128, width: 128});
canvas.add(tile_loop);
});
});
});
The only other way Ive seen is to load images into html tags and create them in fabric from the tags, like this
var imgElement = document.getElementById('my-img');
var imgInstance = new fabric.Image(imgElement, {left: 100,top: 100});
canvas.add(imgInstance);
but that seems like a last resort work around, I would prefer to load images up directly in fabricjs.
Ive tried to do this in Phaser but it doesnt do groups of groups, Fabric has all the other stuff I need so I would like to stay with it, its a shame it does these weird ways of handling images.
Any help is much appreciated, if all else fails Ill use the last resort, it would be great to find a clever correct way.
I created an example in JSFiddle;
http://jsfiddle.net/d7bjbpfb/

KineticJS : get image array id

Here is the problem :
I have a canvas, and four (would be more in future, but 4 for testing...anyway, doesn't matter) images that can be "poped" into the canvas by clicking on it.
Each image can be present multiple times in the canvas.
So far, poping is working fine, images are draggable... But I can't add some resize or zIndex function as I can only select the last image add to the canvas.
In a ideal world, I would like, by clicking/dragging an image, put it on top of the canvas, and kinda "select" it, so that I can connect the resize functions to the image.
But with the array of images, I can't manage to identify properly the item dragged, and can't use (or don't manage to use) the selectors.
Thank you.
EDIT : some code
var imgCpt = 0;
var image = [];
function addDetails(img) {
imgCpt++;
var imageObj = new Image();
imageObj.onload = function() {
image[imgCpt] = new Kinetic.Image({
x: 0,
y: 0,
image: imageObj,
draggable: true,
id:image[imgCpt]
});
image[imgCpt].setX((stage.getWidth()/2) - (image[imgCpt].getWidth()/2));
image[imgCpt].setY((stage.getHeight()/2) - (image[imgCpt].getHeight()/2));
eval(image[imgCpt]).on('click', function() {
alert(eval(imgCpt));
});
layer.add(image[imgCpt]);
stage.add(layer);
};
imageObj.src = 'uploads/'+img;
}
I've already tried different solutions : multiple layer, and acting on it instead of acting on image, working with shapes filled with image instead of image, but it's always the same problem : I can't get the id of the concerned element (instead of the id of the last insert element)
This version works with array, but I tried yersterday to build the image id with eval(); without more success.
Thank you for your help
EDIT² : sorry to insist, but I would really be glad to have some assistance on this point, even if I think it's more JS related than pure KineticJS related.
Thank you.
Ok Guys, just solved the problem :
eval("image["+imgCpt+"].on('click', function() {alert("+imgCpt+");});");
Instead of :
eval(image[imgCpt]).on('click', function() {
alert(eval(imgCpt));
});
Now time to set a true action behind the click.
Thank you for helping ;)

Resources