I'm trying to generate images (jpg or png) from HTML and I've already tried PhantomJS (via jonnyw/php-phantomjs in php) and wkhtmltoimage but they both have the same problem when generating the image. Any border radius, images, or fonts all have really bad jagged edges and aren't crisp at all.
At first I thought it was no fonts being loaded but my font-icons work fine, they're just really poor quality. I have 100 quality set and I get the same results when using Phantomjs or wkhtmltoimage on any website.
Does anyone know what could be causing this?
UPDATE
UPDATE 2
Here's the code used from jonnyw/php-phantomjs:
$client = Client::getInstance();
$client->isLazy();
$client->getEngine()->setPath('phantomjs');
$client->getEngine()->debug(true);
$width = 560;
$height = 670;
$top = 1;
$left = 1;
$request = $client->getMessageFactory()->createCaptureRequest('https://myurltoscreengrab.com', 'GET');
$request->setOutputFile('uploads/stats/test.png');
$request->setFormat('png');
$request->setViewportSize($width, $height);
$request->setCaptureDimensions($width, $height, $top, $left);
$response = $client->getMessageFactory()->createResponse();
// Send the request
$client->send($request, $response);
JS Being Used
/**
* Set up page and script parameters
*/
var page = require('webpage').create(),
system = require('system'),
response = {},
debug = [],
logs = [],
procedure = {},
resources = 0,
timeout;
/**
* Global variables
*/
/**
* Define width & height of capture
*/
var rectTop = 1,
rectLeft = 1,
rectWidth = 530,
rectHeight = 670;
if(rectWidth && rectHeight) {
debug.push(new Date().toISOString().slice(0, -5) + ' [INFO] PhantomJS - Set capture clipping size ~ top: ' + rectTop + ' left: ' + rectLeft + ' ' + rectWidth + 'x' + rectHeight);
page.clipRect = {
top: rectTop,
left: rectLeft,
width: rectWidth,
height: rectHeight
};
}
/**
* Define paper size.
*/
/**
* Define viewport size.
*/
var viewportWidth = 530,
viewportHeight = 670;
if(viewportWidth && viewportHeight) {
debug.push(new Date().toISOString().slice(0, -5) + ' [INFO] PhantomJS - Set viewport size ~ width: ' + viewportWidth + ' height: ' + viewportHeight);
page.viewportSize = {
width: viewportWidth,
height: viewportHeight
};
}
/**
* Define custom headers.
*/
page.customHeaders = {};
/**
* Page settings
*/
page.settings.resourceTimeout = 5000;
/**
* On resource timeout
*/
page.onResourceTimeout = function (error) {
response = error;
response.status = error.errorCode;
};
/**
* On resource requested
*/
page.onResourceRequested = function (req) {
resources++;
window.clearTimeout(timeout);
};
/**
* On resource received
*/
page.onResourceReceived = function (res) {
var resource = res; // To be removed in version 5.0
if(!response.status) {
response = resource;
}
if(!res.stage || res.stage === 'end') {
resources--;
if (resources === 0) {
timeout = window.setTimeout(function() {
procedure.execute('success');
}, 300);
}
}
};
/**
* Handle page errors
*/
page.onError = function (msg, trace) {
var error = {
message: msg,
trace: []
};
trace.forEach(function(t) {
error.trace.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : ''));
});
logs.push(error);
};
/**
* Handle global errors
*/
phantom.onError = function(msg, trace) {
var stack = [];
trace.forEach(function(t) {
stack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : ''));
});
response.status = 500;
response.content = msg;
response.console = stack;
system.stdout.write(JSON.stringify(response, undefined, 4));
phantom.exit(1);
};
/**
* Open page
*/
page.open ('https://boxstat.co/widgets/image/stats/2898784/18/500/FFFFFF-EEEEEE-fafafa-333333-85bd4d-ffffff-e4f8cf-71b42f-fddfc1-bd6610-fad3c9-c85639-fac9c9-c52e2e', 'GET', '', function (status) {
page.evaluate(function() {
var styles = {};
for(var property in styles) {
document.body.style[property] = styles[property];
}
});
window.setTimeout(function () {
procedure.execute(status);
}, 4800);
});
/**
* Execute procedure
*/
procedure.execute = function (status) {
if (status === 'success') {
try {
page.render('uploads/stats/test.png', {
format: 'png',
quality: 100,
});
response.content = page.evaluate(function () {
return document.getElementsByTagName('html')[0].innerHTML
});
} catch(e) {
response.status = 500;
response.content = e.message;
}
}
response.console = logs;
system.stderr.write(debug.join('\\n') + '\\n');
system.stdout.write(JSON.stringify(response, undefined, 4));
phantom.exit();
};
Add viewportSize and zoomFactor in your phantomjs like:
And/or add:
Try setting the zoom factor using a higher DPI for paper in relation to screen DPI:
Must be set after page size is defined.
I also found 2 functions that attempt to deal with this sort of problem...
Function 1
Usage
Function 2
Usage
I hope this helps you. Now let me tell you what I would do. I've been making browser automation scripts for a while now and PhantomJS is, IMO, not so good. Consider using NightmareJS. It's MUCH faster than Phantom and easier to use.