我正在尝试从HTML生成图像(jpg或png),我已经尝试过PhantomJS(通过php中的jonnyw / php-phantomjs)和wkhtmltoimage,但是在生成图像时它们都有相同的问题。任何边框半径,图像或字体都有非常糟糕的锯齿状边缘,并且根本不脆。
起初我以为没有加载字体,但我的字体图标工作正常,它们的质量非常差。我有100个质量设置,当在任何网站上使用Phantomjs或wkhtmltoimage时,我得到相同的结果。
有谁知道造成这种情况的原因是什么?
更新
更新2
这是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正在使用
/**
* 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();
};
答案 0 :(得分:1)
在phantomjs中添加viewportSize和zoomFactor,如:
await page.property('viewportSize', { height: 1600, width: 3600 });
await page.property('zoomFactor', 4);
和/或添加:
<script>
window.devicePixelRatio = 4;
</script>
尝试使用与纸张DPI相关的纸张的较高DPI设置缩放系数:
page.zoomFactor = 300 / 96; // or use / 72
必须在定义页面大小后设置。
我还发现了两个试图解决这类问题的函数......
功能1
var makeHighResScreenshot = function(srcEl, destIMG, dpi) {
var scaleFactor = Math.floor(dpi / 96);
// Save original size of element
var originalWidth = srcEl.offsetWidth;
var originalHeight = srcEl.offsetHeight;
// Save original document size
var originalBodyWidth = document.body.offsetWidth;
var originalBodyHeight = document.body.offsetHeight;
// Add style: transform: scale() to srcEl
srcEl.style.transform = "scale(" + scaleFactor + ", " + scaleFactor + ")";
srcEl.style.transformOrigin = "left top";
// create wrapper for srcEl to add hardcoded height/width
var srcElWrapper = document.createElement('div');
srcElWrapper.id = srcEl.id + '-wrapper';
srcElWrapper.style.height = originalHeight*scaleFactor + 'px';
srcElWrapper.style.width = originalWidth*scaleFactor + 'px';
// insert wrapper before srcEl in the DOM tree
srcEl.parentNode.insertBefore(srcElWrapper, srcEl);
// move srcEl into wrapper
srcElWrapper.appendChild(srcEl);
// Temporarily remove height/width constraints as necessary
document.body.style.width = originalBodyWidth*scaleFactor +"px";
document.body.style.height = originalBodyHeight*scaleFactor +"px";
window.scrollTo(0, 0); // html2canvas breaks when we're not at the top of the doc, see html2canvas#820
html2canvas(srcElWrapper, {
onrendered: function(canvas) {
destIMG.src = canvas.toDataURL("image/png");
srcElWrapper.style.display = "none";
// Reset height/width constraints
document.body.style.width = originalBodyWidth + "px";
document.body.style.height = originalBodyHeight + "px";
}
});
};
<强>用法强>
var src = document.getElementById("screenshot-source");
var img = document.getElementById("screenshot-img");
makeHighResScreenshot(src, img, 192); // DPI of 192 is 4x resolution (2x normal DPI for both width and height)
功能2
function takeHighResScreenshot(srcEl, destIMG, scaleFactor) {
// Save original size of element
var originalWidth = srcEl.offsetWidth;
var originalHeight = srcEl.offsetHeight;
// Force px size (no %, EMs, etc)
srcEl.style.width = originalWidth + "px";
srcEl.style.height = originalHeight + "px";
// Position the element at the top left of the document because of bugs in html2canvas. The bug exists when supplying a custom canvas, and offsets the rendering on the custom canvas based on the offset of the source element on the page; thus the source element MUST be at 0, 0.
// See html2canvas issues #790, #820, #893, #922
srcEl.style.position = "absolute";
srcEl.style.top = "0";
srcEl.style.left = "0";
// Create scaled canvas
var scaledCanvas = document.createElement("canvas");
scaledCanvas.width = originalWidth * scaleFactor;
scaledCanvas.height = originalHeight * scaleFactor;
scaledCanvas.style.width = originalWidth + "px";
scaledCanvas.style.height = originalHeight + "px";
var scaledContext = scaledCanvas.getContext("2d");
scaledContext.scale(scaleFactor, scaleFactor);
html2canvas(srcEl, { canvas: scaledCanvas })
.then(function(canvas) {
destIMG.src = canvas.toDataURL("image/png");
srcEl.style.display = "none";
});
};
<强>用法强>
var src = document.getElementById("screenshot-src");
var img = document.getElementById("screenshot-img");
takeHighResScreenshot(src, img, 2); // This time we provide desired scale factor directly, no more messing with DPI
我希望这会对你有所帮助。现在让我告诉你我会做什么。我现在已经制作了浏览器自动化脚本一段时间了,而PhantomJS是IMO,不太好。考虑使用NightmareJS。它比Phantom更快,更容易使用。