Adding ability to render webpage to a PNG

The driver has a #render method which takes a destination file path and
an options hash for setting the dimensions of the browser's viewport
This commit is contained in:
Mike Nicholaides 2011-05-26 15:05:16 -04:00
parent 8cfb09ee4c
commit 17873240b1
13 changed files with 168 additions and 4 deletions

3
.gitignore vendored
View File

@ -12,3 +12,6 @@ moc_*.cpp
.bundle
pkg
src/webkit_server
.DS_Store
tmp
.rvmrc

View File

@ -2,4 +2,4 @@ source "http://rubygems.org"
gem "rspec", :require => false
gem "capybara"
gem "sinatra", :require => false
gem "mini_magick", :require => false

View File

@ -19,6 +19,8 @@ GEM
rake (>= 0.8.7)
json_pure (1.5.1)
mime-types (1.16)
mini_magick (3.2.1)
subexec (~> 0.0.4)
nokogiri (1.4.4)
rack (1.2.1)
rack-test (0.5.7)
@ -41,6 +43,7 @@ GEM
sinatra (1.1.2)
rack (~> 1.1)
tilt (~> 1.2)
subexec (0.0.4)
tilt (1.2.2)
xpath (0.1.3)
nokogiri (~> 1.3)
@ -50,5 +53,6 @@ PLATFORMS
DEPENDENCIES
capybara
mini_magick
rspec
sinatra

View File

@ -84,6 +84,14 @@ class Capybara::Driver::Webkit
false
end
def render(path, options={})
options[:width] ||= 1000
options[:height] ||= 10
browser.render path, options[:width], options[:height]
end
private
def url(path)

View File

@ -64,6 +64,10 @@ class Capybara::Driver::Webkit
command('Execute', script)
end
def render(path, width, height)
command "Render", path, width, height
end
private
def start_server

View File

@ -0,0 +1,79 @@
require 'spec_helper'
require 'capybara/driver/webkit'
require 'mini_magick'
describe Capybara::Driver::Webkit, "rendering an image" do
before(:all) do
# Set up the tmp directory and file name
tmp_dir = File.join(PROJECT_ROOT, 'tmp')
FileUtils.mkdir_p tmp_dir
@file_name = File.join(tmp_dir, 'render-test.png')
app = lambda do |env|
body = <<-HTML
<html>
<body>
<h1>Hello World</h1>
</body>
</html>
HTML
[200,
{ 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
[body]]
end
@driver = Capybara::Driver::Webkit.new(app, :browser => $webkit_browser)
@driver.visit("/hello/world?success=true")
end
after(:all) { @driver.reset! }
def render(options)
FileUtils.rm_f @file_name
@driver.render @file_name, options
@image = MiniMagick::Image.open @file_name
end
context "with default options" do
before(:all){ render({}) }
it "should be a PNG" do
@image[:format].should == "PNG"
end
it "width default to 1000px (with 15px less for the scrollbar)" do
@image[:width].should == 1000-15
end
it "height should be at least 10px" do
@image[:height].should >= 10
end
end
context "with dimensions set larger than necessary" do
before(:all){ render(:width => 500, :height => 400) }
it "width should match the width given" do
@image[:width].should == 500
end
it "height should match the height given" do
@image[:height].should > 10
end
end
context "with dimensions set smaller than the document's default" do
before(:all){ render(:width => 50, :height => 10) }
it "width should be greater than the width given" do
@image[:width].should > 50
end
it "height should be greater than the height given" do
@image[:height].should > 10
end
end
end

View File

@ -11,6 +11,7 @@
#include "Execute.h"
#include "FrameFocus.h"
#include "Header.h"
#include "Render.h"
#include <QTcpSocket>
#include <iostream>

19
src/Render.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "Render.h"
#include "WebPage.h"
Render::Render(WebPage *page, QObject *parent) : Command(page, parent) {
}
void Render::start(QStringList &arguments) {
QStringList functionArguments(arguments);
QString imagePath = functionArguments.takeFirst();
int width = functionArguments.takeFirst().toInt();
int height = functionArguments.takeFirst().toInt();
QSize size(width, height);
page()->setViewportSize(size);
bool result = page()->render( imagePath );
emit finished(new Response(result));
}

12
src/Render.h Normal file
View File

@ -0,0 +1,12 @@
#include "Command.h"
#include <QStringList>
class WebPage;
class Render : public Command {
Q_OBJECT
public:
Render(WebPage *page, QObject *parent = 0);
virtual void start(QStringList &arguments);
};

View File

@ -106,3 +106,35 @@ QString WebPage::failureString() {
return QString("Unable to load URL: ") + currentFrame()->requestedUrl().toString();
}
/*
* Swiped from Phantom.js, but removed code for rendering to PDFs and GIFs
*
* Takes a QString of the file path to save the image to
* Returns true if it was able to save the file
*/
bool WebPage::render(const QString &fileName) {
QFileInfo fileInfo(fileName);
QDir dir;
dir.mkpath(fileInfo.absolutePath());
QSize viewportSize = this->viewportSize();
QSize pageSize = this->mainFrame()->contentsSize();
if (pageSize.isEmpty()) {
return false;
}
QImage buffer(pageSize, QImage::Format_ARGB32);
buffer.fill(qRgba(255, 255, 255, 0));
QPainter p(&buffer);
p.setRenderHint( QPainter::Antialiasing, true);
p.setRenderHint( QPainter::TextAntialiasing, true);
p.setRenderHint( QPainter::SmoothPixmapTransform, true);
this->setViewportSize(pageSize);
this->mainFrame()->render(&p);
p.end();
this->setViewportSize(viewportSize);
return buffer.save(fileName);
}

View File

@ -10,6 +10,7 @@ class WebPage : public QWebPage {
QString failureString();
QString userAgentForUrl(const QUrl &url ) const;
void setUserAgent(QString userAgent);
bool render(const QString &fileName);
public slots:
bool shouldInterruptJavaScript();

View File

@ -12,4 +12,5 @@ CHECK_COMMAND(Source)
CHECK_COMMAND(Evaluate)
CHECK_COMMAND(Execute)
CHECK_COMMAND(FrameFocus)
CHECK_COMMAND(Header)
CHECK_COMMAND(Header)
CHECK_COMMAND(Render)

View File

@ -1,8 +1,8 @@
TEMPLATE = app
TARGET = webkit_server
DESTDIR = .
HEADERS = WebPage.h Server.h Connection.h Command.h Visit.h Find.h Reset.h Node.h JavascriptInvocation.h Url.h Source.h Evaluate.h Execute.h FrameFocus.h Response.h NetworkAccessManager.h Header.h
SOURCES = main.cpp WebPage.cpp Server.cpp Connection.cpp Command.cpp Visit.cpp Find.cpp Reset.cpp Node.cpp JavascriptInvocation.cpp Url.cpp Source.cpp Evaluate.cpp Execute.cpp FrameFocus.cpp Response.cpp NetworkAccessManager.cpp Header.cpp
HEADERS = WebPage.h Server.h Connection.h Command.h Visit.h Find.h Reset.h Node.h JavascriptInvocation.h Url.h Source.h Evaluate.h Execute.h FrameFocus.h Response.h NetworkAccessManager.h Header.h Render.h
SOURCES = main.cpp WebPage.cpp Server.cpp Connection.cpp Command.cpp Visit.cpp Find.cpp Reset.cpp Node.cpp JavascriptInvocation.cpp Url.cpp Source.cpp Evaluate.cpp Execute.cpp FrameFocus.cpp Response.cpp NetworkAccessManager.cpp Header.cpp Render.cpp
RESOURCES = webkit_server.qrc
QT += network webkit
CONFIG += console