webshot/phantom pdf, printing only second half of the page - node.js

I'm working with webshot npm module to create pdf file of my page
This is my page
and
I'm getting this as output in pdf
my settings are
var options = {
renderDelay:10000,
"paperSize": {
"format": "Letter",
"orientation": "portrait",
"border": "1cm"
},
shotSize: {
width: 'all',
height: 'all'
},
shotOffset: {
left: 0
, right: 0
, top: 0
, bottom: 0
}
};
webshot(url, fileName, options, function(err) {
fs.readFile(fileName, function (err,data) {
if (err) {
return console.log(err);
}
fs.unlinkSync(fileName);
fut.return(data);
});
});
this.response.writeHead(200, {'Content-Type': 'application/pdf',"Content-Disposition": "attachment; filename=generated.pdf"});
this.response.end(fut.wait());
For meteor guys this is my server side root
this.route('generatePDF', {
path: '/api/generatePDF',
where: 'server',
action: function() {
var webshot = Meteor.npmRequire('webshot');
var fs = Npm.require('fs');
Future = Npm.require('fibers/future');
var fut = new Future();
var fileName = "generated_"+Random.id()+".pdf";
var userid = (Meteor.isClient) ? Meteor.userId() : this.userId;
console.log(userid);
// var username = Meteor.users.findOne({_id: userid}).username;
var url = "url";
var options = {
renderDelay:10000,
"paperSize": {
"format": "Letter",
"orientation": "portrait",
"border": "1cm"
},
shotSize: {
width: 'all',
height: 'all'
},
shotOffset: {
left: 0
, right: 0
, top: 0
, bottom: 0
}
};
webshot(url, fileName, options, function(err) {
fs.readFile(fileName, function (err,data) {
if (err) {
return console.log(err);
}
fs.unlinkSync(fileName);
fut.return(data);
});
});
this.response.writeHead(200, {'Content-Type': 'application/pdf',"Content-Disposition": "attachment; filename=generated.pdf"});
this.response.end(fut.wait());
}
});
Am I missing anything here? Any help appreciated

You can try to change paperSize to:
"paperSize": {
width: '612px',
height: '792px',
margin: 'XXpx'
},

If anyone else will have trouble with that - I had the same issue. I am not able to tell you exactly why, but the problem was that I was using bootstrap and the wrapper of my page had the "container" class. After removing this class the whole page was rendered - without removing it it just rendered around the half of the page.

Related

My nodejs api mixes the codes of simultaneous api requests by clients

I have a nodejs server file which has an api as shown below to update the profile pictures.
app.post('/updateProfilePic', async(req, res) => {
try {
if (VerifyAPIKey(req.query.key)) {
let userdata = users.find(e => e.Id == req.query.socketId);
if (userdata) {
for (var a = 0; a < users.length; a++) {
const b = a;
if (users[a].IsAuthenticated) {
if (req.query.pub) {
cloudinary.uploader.destroy(req.query.pub, {resource_type: 'image'}, function(err, res) {
// console.log(err, res);
});
}
cloudinary.uploader.upload(req.files.profilePic.tempFilePath, {resource_type: 'image', folder: 'members', eager: [
{width: 25, height: 25, g: 'face', radius: "max", crop: 'fill', format: "png"},
{width: 50, height: 50, g: 'face', radius: "max", crop: 'fill', format: "png"},
{width: 100, height: 100, g: 'face', radius: "max", crop: 'fill', format: "png"},
{width: 250, height: 250, g: 'face', radius: "max", crop: 'fill', format: "png"},
{width: 500, height: 500, g: 'face', crop: 'fill'},
]}, function(err,response) {
if (err) {
console.log(err);
}
if (response) {
const logo = userModel.findOneAndUpdate({
_id: users[b]._id,
},{
PictureUrl: response
}, (err, result) => {
data.status = 200;
data.message = "Your Profile Picture has been updated!"
res.status(200).send(data);
})
}
});
}
}
} else {
data.status = 404;
data.message = "Invalid User!";
res.status(200).send(data);
}
} else {
res.json('Unauthorized request!');
}
} catch(err) {
res.status(400).send(err.message);
}
})
The VerifyAPIKey function is given below
function VerifyAPIKey(key) {
var a = users.find(e=> e.API_KEY == key);
console.log(a)
fs.appendFile('./data/apiRequests.txt', JSON.stringify(a) + "\r\n", function (err) {
if (err) throw err;
});
return Boolean(a);
}
The userdata is in a format as shown below
{
Id: 'FjWs0GZ4MkE_GCmKAAAD',
Ip: '::1',
API_KEY: '590c3789-e807-431b-bfdb-e20b6649e553',
HOST: undefined,
IsAuthenticated: false
}
The problem is the current code causes the response data from cloudinary to mix up between simultaneous requests. I have tested it with two simultaneous requests. Out of the two cloudinary responses whichever comes first is sent back as response to the user who invoked the api later than the two. And the user who invoked the api first get's an error saying cannot set headers after they are sent.
I have tried searching for solution but haven't found any. Can someone please help?
How does data got initiated? data does not seem to be thread-safe and defined outside your async flow. You may want to start from there and make sure data is thread-safe.

Upload images - Nodejs Paperclip and S3

I want to upload an image to S3 and save to user record with NodeJS, just like the Rails Paperclip gem.
I believe this should be the process, but again I'm quite confused about how this package should work:
receive an image and resize by paperclip
save or update to S3
save file to user in DB
I have a Rails Postgres database, and users can upload an image, stored in S3, and reformatted with Paperclip gem. Here's how it is stored:
irb(main):003:0> user.avatar
=> #<Paperclip::Attachment:0x000055b3e043aa50 #name=:avatar,
#name_string="avatar", #instance=#<User id: 1, email:
"example#gmail.com", created_at: "2016-06-11 22:52:36",
updated_at: "2019-06-16 17:17:16", first_name: "Clarissa",
last_name: "Jones", avatar_file_name: "two_people_talking.gif",
avatar_content_type: "image/gif", avatar_file_size: 373197,
avatar_updated_at: "2019-06-16 17:17:12", #options={:convert_options=>{},
:default_style=>:original, :default_url=>":style/missing.png",
:escape_url=>true, :restricted_characters=>/[&$+,\/:;=?#<>\[\]\
{\}\|\\\^~%# ]/, :filename_cleaner=>nil,
:hash_data=>":class/:attachment/:id/:style/:updated_at",
:hash_digest=>"SHA1", :interpolator=>Paperclip::Interpolations,
:only_process=>[],
:path=>"/:class/:attachment/:id_partition/:style/:filename",
:preserve_files=>false, :processors=>[:thumbnail],
:source_file_options=>{:all=>"-auto-orient"}, :storage=>:s3,
:styles=>{:large=>"500x500#", :medium=>"200x200#",
:thumb=>"100x100#"}, :url=>":s3_path_url",
:url_generator=>Paperclip::UrlGenerator,
:use_default_time_zone=>true, :use_timestamp=>true, :whiny=>true,
:validate_media_type=>true, :adapter_options=>
{:hash_digest=>Digest::MD5},
:check_validity_before_processing=>true, :s3_host_name=>"s3-us-
west-2.amazonaws.com", :s3_protocol=>"https", :s3_credentials=>
{:bucket=>"example", :access_key_id=>"REDACTED",
:secret_access_key=>"REDACTED",
:s3_region=>"us-west-2"}}, #post_processing=true,
#queued_for_delete=[], #queued_for_write={}, #errors={},
#dirty=false, #interpolator=Paperclip::Interpolations,
#url_generator=#<Paperclip::UrlGenerator:0x000055b3e043a8e8
#attachment=#<Paperclip::Attachment:0x000055b3e043aa50 ...>>,
#source_file_options={:all=>"-auto-orient"}, #whiny=true,
#s3_options={}, #s3_permissions={:default=>:"public-read"},
#s3_protocol="https", #s3_metadata={}, #s3_headers={},
#s3_storage_class={:default=>nil},
#s3_server_side_encryption=false, #http_proxy=nil,
#use_accelerate_endpoint=nil>
user.avatar(:thumb) returns:
https://s3-us-west-2.amazonaws.com/example/users/avatars/000/000/001/thumb/two_people_talking.gif?1560705432
Now, I'm trying to allow the user to upload a new/change image through a react-native app, and the backend is Nodejs, which is relatively new to me.
I'm so confused about how to implement this, especially because the examples are all referencing Mongoose, which I'm not using.
Just to show how I'd successfully update the user, here is how to update first_name of the user:
users.updateUserPhoto = (req, res) => {
let id = req.decoded.id
let first_name = req.body.first_name
models.Users.update(
first_name: first_name,
{
where: {
id: req.decoded.id
}
},
).then(response => {
res.status(200).json({ status: 200, data: { response } });
})
.catch(error => {
res.status(500).json({ status: 500, err: error });
})
}
Here is the package I found node-paperclip-s3, and here's what I'm trying to do:
'use strict'
let users = {};
const { Users } = require('../models');
let models = require("../models/index");
let Sequelize = require('sequelize');
let Paperclip = require('node-paperclip');
let Op = Sequelize.Op;
let sequelizeDB = require('../modules/Sequelize');
users.updateUserPhoto = (req, res) => {
let id = req.decoded.id
let avatar = req.body.avatar <- this is a file path
models.Users.plugin(Paperclip.plugins, {
avatar: {
styles: [
{ original: true },
{ large: { width: 500, height: 500 } },
{ medium: { width: 200, height: 200 } },
{ thumb: { width: 100, height: 100 } }
],
prefix: '/users/{{attachment}}/{{id}}/{{filename}}',
name_format: '{{style}}.{{extension}}',
storage: 's3',
s3: {
bucket: process.env.S3_BUCKET_NAME,
region: 'us-west-2',
key: process.env.AWS_ACCESS_KEY_ID,
secret: process.env.AWS_SECRET_ACCESS_KEY,
}
}
})
models.Users.update(
avatar,
{
where: {
id: req.decoded.id
}
},
).then(response => {
res.status(200).json({ status: 200, data: { response } });
})
.catch(error => {
res.status(500).json({ status: 500, err: error });
})
}
I've also tried something like this:
models.Users.update(Paperclip.plugins, {
avatar: {
styles: [
{ original: true },
{ large: { width: 500, height: 500 } },
{ medium: { width: 200, height: 200 } },
{ thumb: { width: 100, height: 100 } }
],
prefix: '/users/{{attachment}}/{{id}}/{{filename}}',
name_format: '{{style}}.{{extension}}',
storage: 's3',
s3: {
bucket: process.env.S3_BUCKET_NAME,
region: 'us-west-2',
key: process.env.AWS_ACCESS_KEY_ID,
secret: process.env.AWS_SECRET_ACCESS_KEY,
}
},
{
where: {
id: req.decoded.id
}
},
).then(response => {
res.status(200).json({ status: 200, data: { response } });
})
.catch(error => {
res.status(500).json({ status: 500, err: error });
})
})
I've tried:
let new_avatar = (Paperclip.plugins, {
avatar: {
styles: [
{ original: true },
{ large: { width: 500, height: 500 } },
{ medium: { width: 200, height: 200 } },
{ thumb: { width: 100, height: 100 } }
],
prefix: `/users/avatars/{{attachment}}/{{id}}/{{filename}}`,
name_format: '{{style}}.{{extension}}',
storage: 's3',
s3: {
bucket: process.env.S3_BUCKET_NAME,
region: 'us-west-2',
key: process.env.AWS_ACCESS_KEY_ID,
secret: process.env.AWS_SECRET_ACCESS_KEY,
}
},
})
let data = {
avatar: new_avatar
}
models.Users.update(
data,
{
where: {
id: req.decoded.id
}
},
).then(response => {
res.status(200).json({ status: 200, data: { response } });
})
.catch(error => {
res.status(500).json({ status: 500, err: error });
})
From the example in the link above, I don't understand how it is saving to S3, or how it's updating the database in the same way the Rails gem is creating that record.
Question : how to save resized images + original in the exact same way that the Rails paperclip gem is saving to S3 AND the user record in the database.
I originally had this open for a 400 point bounty, and am more than happy to still offer 400 points to anyone who can help me solve this. Thanks!!
The below code is for nodeJs.
I have added an api to save an image from frontend to AWS S3.
I have added comments within code for better understanding.
var express = require("express");
var router = express.Router();
var aws = require('aws-sdk');
aws.config.update({
secretAccessKey: config.AwsS3SecretAccessKey,
accessKeyId: config.AwsS3AccessKeyId,
region: config.AwsS3Region
});
router
.route("/uploadImage")
.post(function (req, res) {
//req.files.imageFile contains the file from client, modify it as per you requirement
var file = getDesiredFileFromPaperclip(req.files.imageFile);
const fileName = new Date().getTime() + file.name;
//before uploading, we need to create an instance of client file
file.mv(fileName, (movErr, movedFile) => {
if (movErr) {
console.log(movErr);
res.send(400);
return;
}
//read file data
fs.readFile(fileName, (err, data) => {
if (err) {
console.error(err)
res.send(400);
}
else {
//as we have byte data of file, delete the file instance
try {
fs.unlink(fileName);
} catch (error) {
console.error(error);
}
//now, configure aws
var s3 = new aws.S3();
const params = {
Bucket: config.AwsS3BucketName, // pass your bucket name
Key: fileName, // file will be saved as bucket_name/file.ext
Body: data
}
//upload file
s3.upload(params, function (s3Err, awsFileData) {
if (s3Err) {
console.error(s3Err)
res.send(400);
} else {
console.log(`File uploaded successfully at ${awsFileData.Location}`)
//update uploaded file data in database using 'models.Users.update'
//send response to client/frontend
var obj = {};
obj.status = { "code": "200", "message": "Yipee!! Its Done" };
obj.result = { url: awsFileData.Location };
res.status(200).send(obj);
}
});
}
});
});
});
This is old school, non - fancy solution.Please try it out and let me know.

phantom-html2pdf css not apply

try to convert html to pdf with phantom-html2pdf nodejs.everything working without problem except one. i try to apply CSS to my output but nothing happen at all.
app.get("/ph2p", function(req, res) {
var pdf = require("phantom-html2pdf");
var fs = require("fs");
fs.unlink("./file.pdf", function() {
console.log("pdf file deleted!");
});
pdf.convert(
{
'html': "htmlfile.html",
'css': "style.css",
js: "",
runnings: "",
'paperSize': { format: "A4", orientation: "portrait", border: "1cm" },
deleteOnAction: true,
runningsArgs: ""
},
function(err, result) {
result.toFile("file.pdf", function() {
console.log("pdf file created");
});
}
);
res.end("pdf checking...");
});
anyone have clue what's wrong?

fabricJs custom object does not render from JSON

fabricJS Version 2.2.3
Test jsFiddle
I'm trying to use LabeledRect subclass but my problem is that whenever I try to load it from JSON, it does not render, and I got no error in console. See the fiddle below.
How can I render it properly ? I think my problem is in the fromObject func but I got no idea where.
/**
* fabric.js template for bug reports
*
* Please update the name of the jsfiddle (see Fiddle Options).
* This templates uses latest dev verison of fabric.js (https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js).
*/
// initialize fabric canvas and assign to global windows object for debug
var canvas = window._canvas = new fabric.Canvas('c');
// ADD YOUR CODE HERE
var json = '{"version":"2.2.3","objects":[{"type":"labeledRect","version":"2.2.3","originX":"left","originY":"top","left":0,"top":0,"width":100,"height":50,"fill":"#faa","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0,"label":"1"}]}';
fabric.LabeledRect = fabric.util.createClass(fabric.Rect, {
type: 'labeledRect',
initialize: function(options) {
options || (options = {});
this.callSuper('initialize', options);
this.set('label', options.label || '');
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
label: this.get('label')
});
},
_render: function(ctx) {
this.callSuper('_render', ctx);
ctx.font = '20px Helvetica';
ctx.fillStyle = '#333';
ctx.fillText(this.label, -this.width / 2, -this.height / 2 + 20);
}
});
fabric.LabeledRect.fromObject = function(object, callback) {
fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
delete object.objects;
callback && callback(new fabric.LabeledRect(enlivenedObjects, object));
});
};
fabric.LabeledRect.async = true;
canvas.loadFromJSON(json);
canvas.renderAll();
canvas {
border: 1px solid #999;
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="1000" height="600"></canvas>
fabric.LabeledRect.fromObject = function(object, callback) {
return fabric.Object._fromObject('LabeledRect', object, callback);
};
call fabric.Object._fromObject inside fromObject
DEMO
var canvas = window._canvas = new fabric.Canvas('c');
var json = '{"version":"2.2.3","objects":[{"type":"labeledRect","version":"2.2.3","originX":"left","originY":"top","left":0,"top":0,"width":100,"height":50,"fill":"#faa","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0,"label":"1"}]}';
fabric.LabeledRect = fabric.util.createClass(fabric.Rect, {
type: 'labeledRect',
initialize: function(options) {
options || (options = {});
this.callSuper('initialize', options);
this.set('label', options.label || '');
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
label: this.get('label')
});
},
_render: function(ctx) {
this.callSuper('_render', ctx);
ctx.font = '20px Helvetica';
ctx.fillStyle = '#333';
ctx.fillText(this.label, -this.width / 2, -this.height / 2 + 20);
}
});
fabric.LabeledRect.fromObject = function(object, callback) {
return fabric.Object._fromObject('LabeledRect', object, callback);
};
canvas.loadFromJSON(json,canvas.renderAll.bind(canvas));
canvas {
border: 1px solid #999;
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="1000" height="600"></canvas>

Exporting custom properties from fabricjs objects to SVG

I have an image object where I have added a custom property.
When stringify the object, I can see that the property is there, but when I export the SVG by using toSVG, the custom property is not in the SVG source.
I would like the name property in my example to be exported as a custom property in the SVG image xml data (with the possibility to add multiple properties)
Is this possible?
Please se my fiddle: https://jsfiddle.net/Jonah/ujexg46s/
var svgImage = fabric.util.createClass(fabric.Image, {
initialize: function(element, options) {
this.callSuper("initialize", element, options);
options && this.set("name", options.name);
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
name: this.name
});
}
});
var canvas = new fabric.Canvas('container');
function loadImage() {
var img = new Image();
img.src = imageSrc;
img.onload = function() {
var image = new svgImage(img, {
name: "test",
left: 0,
top: 0
});
canvas.add(image);
}
}
document.getElementById('load-btn').onclick = function() {
loadImage();
};
document.getElementById('export-btn').onclick = function() {
canvas.deactivateAll().renderAll();
console.log(JSON.stringify(canvas));
window.open('data:image/svg+xml;utf8,' + encodeURIComponent(canvas.toSVG()));
};
You can find answer here.
fabric.util.object.extend(fabric.Image.prototype, {
toObject: function(propertiesToInclude) {
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
src: this._originalElement.src || this._originalElement._src,
filters: this.filters.map(function(filterObj) {
return filterObj && filterObj.toObject();
}),
crossOrigin: this.crossOrigin,
alignX: this.alignX,
alignY: this.alignY,
meetOrSlice: this.meetOrSlice,
name: this.name
});
},
});
https://jsfiddle.net/mullainathan/ujexg46s/1/
You can override the existing toSVG function like this.
var circle = new fabric.Circle ({
radius: 40,
left: 50,
top: 50,
fill: 'rgb(0,255,0)',
opacity: 0.5,
id: 'hello'
});
circle.toSVG = (function(toSVG) {
return function(){
var svgString = toSVG.call(this);
var domParser = new DOMParser();
var doc = domParser.parseFromString(svgString, 'image/svg+xml');
var parentG = doc.querySelector('path')
parentG.setAttribute('id', this.id);
return doc.documentElement.outerHTML;
}
})(circle.toSVG)

Resources