Browse files

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
  • Loading branch information...
1 parent 8cfb09e commit 17873240b113eead6bd83c3c8a54f047a61c2877 @nicholaides nicholaides committed May 26, 2011
View
3 .gitignore
@@ -12,3 +12,6 @@ moc_*.cpp
.bundle
pkg
src/webkit_server
+.DS_Store
+tmp
+.rvmrc
View
2 Gemfile
@@ -2,4 +2,4 @@ source "http://rubygems.org"
gem "rspec", :require => false
gem "capybara"
gem "sinatra", :require => false
-
+gem "mini_magick", :require => false
View
4 Gemfile.lock
@@ -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
8 lib/capybara/driver/webkit.rb
@@ -84,6 +84,14 @@ def has_shortcircuit_timeout?
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
4 lib/capybara/driver/webkit/browser.rb
@@ -64,6 +64,10 @@ def execute_script(script)
command('Execute', script)
end
+ def render(path, width, height)
+ command "Render", path, width, height
+ end
+
private
def start_server
View
79 spec/driver_rendering_spec.rb
@@ -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
1 src/Connection.cpp
@@ -11,6 +11,7 @@
#include "Execute.h"
#include "FrameFocus.h"
#include "Header.h"
+#include "Render.h"
#include <QTcpSocket>
#include <iostream>
View
19 src/Render.cpp
@@ -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));
+}
View
12 src/Render.h
@@ -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
32 src/WebPage.cpp
@@ -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
1 src/WebPage.h
@@ -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
3 src/find_command.h
@@ -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
4 src/webkit_server.pro
@@ -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

0 comments on commit 1787324

Please sign in to comment.