From cdc7a744e34d5928a408cac448d6e7b4880599f1 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Fri, 28 Dec 2012 18:06:44 +0200 Subject: [PATCH 01/31] Different quote styles --- tests/cases/background/position.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cases/background/position.html b/tests/cases/background/position.html index f48e3bb..2d53bb7 100644 --- a/tests/cases/background/position.html +++ b/tests/cases/background/position.html @@ -41,8 +41,8 @@
-
-
+
+
From 3bbcfe36e04453f41e5eaf74582126d4cf9f2836 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Fri, 28 Dec 2012 19:41:14 +0200 Subject: [PATCH 02/31] Test result outputs --- .gitignore | 3 +- .../chrome-23.0.1271.97-windows-vista.json | 1 + ...e-23.0.1271.97-windows-vista.json.baseline | 1 + tests/results/chrome.json | 1 + tests/results/firefox-12.0-windows-vista.json | 1 + tests/results/firefox.json | 1 + tests/results/iexplorer-9-windows-vista.json | 1 + tests/results/iexplorer.json | 1 + tests/selenium.js | 329 +++++++++++------- 9 files changed, 208 insertions(+), 131 deletions(-) create mode 100644 tests/results/chrome-23.0.1271.97-windows-vista.json create mode 100644 tests/results/chrome-23.0.1271.97-windows-vista.json.baseline create mode 100644 tests/results/chrome.json create mode 100644 tests/results/firefox-12.0-windows-vista.json create mode 100644 tests/results/firefox.json create mode 100644 tests/results/iexplorer-9-windows-vista.json create mode 100644 tests/results/iexplorer.json diff --git a/.gitignore b/.gitignore index 2ad031c..4a2b018 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ node_modules/ .envrc server.js *.sublime-workspace -chromedriver.log \ No newline at end of file +chromedriver.log +.baseline \ No newline at end of file diff --git a/tests/results/chrome-23.0.1271.97-windows-vista.json b/tests/results/chrome-23.0.1271.97-windows-vista.json new file mode 100644 index 0000000..8c8cf53 --- /dev/null +++ b/tests/results/chrome-23.0.1271.97-windows-vista.json @@ -0,0 +1 @@ +{"tests":{"cases/background/encoded.html":98,"cases/background/linear-gradient.html":82.27},"date":"2012-12-28T16:48:04.248Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/chrome-23.0.1271.97-windows-vista.json.baseline b/tests/results/chrome-23.0.1271.97-windows-vista.json.baseline new file mode 100644 index 0000000..478cb85 --- /dev/null +++ b/tests/results/chrome-23.0.1271.97-windows-vista.json.baseline @@ -0,0 +1 @@ +{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":82.27},"date":"2012-12-28T17:20:56.098Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/chrome.json b/tests/results/chrome.json new file mode 100644 index 0000000..01acad7 --- /dev/null +++ b/tests/results/chrome.json @@ -0,0 +1 @@ +{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":82.27},"date":"2012-12-28T16:48:04.248Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/firefox-12.0-windows-vista.json b/tests/results/firefox-12.0-windows-vista.json new file mode 100644 index 0000000..ffe0de2 --- /dev/null +++ b/tests/results/firefox-12.0-windows-vista.json @@ -0,0 +1 @@ +{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":92.64},"date":"2012-12-28T16:48:10.772Z","version":"12.0"} \ No newline at end of file diff --git a/tests/results/firefox.json b/tests/results/firefox.json new file mode 100644 index 0000000..5b0f7bb --- /dev/null +++ b/tests/results/firefox.json @@ -0,0 +1 @@ +{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":85.64},"date":"2012-12-28T16:48:10.772Z","version":"12.0"} \ No newline at end of file diff --git a/tests/results/iexplorer-9-windows-vista.json b/tests/results/iexplorer-9-windows-vista.json new file mode 100644 index 0000000..526cd67 --- /dev/null +++ b/tests/results/iexplorer-9-windows-vista.json @@ -0,0 +1 @@ +{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":100},"date":"2012-12-28T16:48:04.855Z","version":"9"} \ No newline at end of file diff --git a/tests/results/iexplorer.json b/tests/results/iexplorer.json new file mode 100644 index 0000000..526cd67 --- /dev/null +++ b/tests/results/iexplorer.json @@ -0,0 +1 @@ +{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":100},"date":"2012-12-28T16:48:04.855Z","version":"9"} \ No newline at end of file diff --git a/tests/selenium.js b/tests/selenium.js index 5e9b5fe..b8e4074 100644 --- a/tests/selenium.js +++ b/tests/selenium.js @@ -1,154 +1,223 @@ -var webdriver = require("webdriver.js").webdriver, -http = require("http"), -url = require("url"), -path = require("path"), -base64_arraybuffer = require('base64-arraybuffer'), -PNG = require('png-js'), -fs = require("fs"); +(function(){ + "use strict;" + var webdriver = require("webdriver.js").webdriver, + http = require("http"), + url = require("url"), + path = require("path"), + base64_arraybuffer = require('base64-arraybuffer'), + PNG = require('png-js'), + fs = require("fs"); + function createServer(port) { + return http.createServer(function(request, response) { + var uri = url.parse(request.url).pathname, + filename = path.join(process.cwd(), "../" + uri); -function createServer(port) { - return http.createServer(function(request, response) { - var uri = url.parse(request.url).pathname, - filename = path.join(process.cwd(), "../" + uri); - - fs.exists(filename, function(exists) { - if(!exists) { - response.writeHead(404, { - "Content-Type": "text/plain" - }); - response.write("404 Not Found\n"); - response.end(); - return; - } - - if (fs.statSync(filename).isDirectory()) filename += '/index.html'; - - fs.readFile(filename, "binary", function(err, file) { - if(err) { - response.writeHead(500, { + fs.exists(filename, function(exists) { + if(!exists) { + response.writeHead(404, { "Content-Type": "text/plain" }); - response.write(err + "\n"); + response.write("404 Not Found\n"); response.end(); return; } - response.writeHead(200); - response.write(file, "binary"); - response.end(); + if (fs.statSync(filename).isDirectory()) filename += '/index.html'; + + fs.readFile(filename, "binary", function(err, file) { + if(err) { + response.writeHead(500, { + "Content-Type": "text/plain" + }); + response.write(err + "\n"); + response.end(); + return; + } + + response.writeHead(200); + response.write(file, "binary"); + response.end(); + }); }); - }); - }).listen(port); -} + }).listen(port); + } -function walkDir(dir, done) { - var results = []; - fs.readdir(dir, function(err, list) { - if (err) return done(err); - var i = 0; - (function next() { - var file = list[i++]; - if (!file) return done(null, results); - file = dir + '/' + file; - fs.stat(file, function(err, stat) { - if (stat && stat.isDirectory()) { - walkDir(file, function(err, res) { - results = results.concat(res); + function walkDir(dir, done) { + var results = []; + fs.readdir(dir, function(err, list) { + if (err) return done(err); + var i = 0; + (function next() { + var file = list[i++]; + if (!file) return done(null, results); + file = dir + '/' + file; + fs.stat(file, function(err, stat) { + if (stat && stat.isDirectory()) { + walkDir(file, function(err, res) { + results = results.concat(res); + next(); + }); + } else { + results.push(file); next(); - }); - } else { - results.push(file); - next(); - } - }); - })(); - }); -}; - -function getPixelArray(base64, func) { - var arraybuffer = base64_arraybuffer.decode(base64); - (new PNG(arraybuffer)).decode(func); -} - - -function testPage(browser, url, done) { - - browser.url(url) - .$(".html2canvas", 5000, function(){ - this.execute(function(){ - var canvas = $('.html2canvas')[0]; - return canvas.toDataURL("image/png").substring(22); - },[], function(dataurl) { - getPixelArray(dataurl, function(h2cPixels) { - browser.screenshot(function(base64){ - getPixelArray(base64, function(screenPixels) { - var len = h2cPixels.length, index = 0, diff = 0; - for (; index < len; index++) { - if (screenPixels[index] - h2cPixels[index] !== 0) { - diff++; - } - } - done(100 - (Math.round((diff/h2cPixels.length) * 10000) / 100)); - }); - }) - }); + } + }); + })(); }); - }); -} - -function runBrowsers(pages){ - - var port = 5555, - stats = {}, - browsers = ["chrome", "firefox", "internet explorer"], - browsersDone = 0, - server = createServer(port), - numPages = pages.length; - - var browserDone = function() { - if (++browsersDone >= browsers.length) { - server.close(); - console.log(stats); - } }; - browsers.forEach(function(browserName){ - var browser = new webdriver({ - browser: browserName - }), - browserType; + function getPixelArray(base64, func) { + var arraybuffer = base64_arraybuffer.decode(base64); + (new PNG(arraybuffer)).decode(func); + } - browser.status(function(browserInfo){ - browserType = [browserName, browser.version, browserInfo.os.name.replace(/\s+/g, "-").toLowerCase()].join("-"); - var date = new Date(), - obj = { - tests: {}, - date: date.toISOString() + + function testPage(browser, url, done) { + + browser.url(url) + .$(".html2canvas", 5000, function(){ + this.execute(function(){ + var canvas = $('.html2canvas')[0]; + return canvas.toDataURL("image/png").substring(22); + },[], function(dataurl) { + getPixelArray(dataurl, function(h2cPixels) { + browser.screenshot(function(base64){ + getPixelArray(base64, function(screenPixels) { + var len = h2cPixels.length, index = 0, diff = 0; + for (; index < len; index++) { + if (screenPixels[index] - h2cPixels[index] !== 0) { + diff++; + } + } + done(100 - (Math.round((diff/h2cPixels.length) * 10000) / 100)); + }); + }) + }); + }); + }); + } + + var writeResultFile = function(filename, json, append) { + fs.writeFile(filename + (append || ""), json); + }; + + var openResultFile = function(stats, browser) { + var tests = stats[browser].tests, + filename = "results/" + browser + ".json", + write = writeResultFile.bind(null, filename, JSON.stringify(stats[browser])); + + fs.exists(filename, function(exists) { + if(exists) { + fs.readFile(filename, "binary", parseResultFile.bind(null, tests, browser, write)); + } else { + write(); + } + }); + }; + + var setColor = function(color, text) { + return color + text.amount + "% " + text.test; + }; + + var parseResultFile = function(tests, browser, createResultFile, err, file) { + if (err) throw err; + var data = JSON.parse(file), + improved = [], + colors = { + red: "\x1b[1;31m", + green: "\x1b[0;32m" + }, + regressed = []; + + Object.keys(tests).forEach(function(test){ + var testResult = tests[test], + dataResult = data.tests[test], + dataObject = { + amount: testResult - dataResult, + test: test }; - stats[browserType] = obj; - stats[browserName] = obj; - processPage(0); + + if (testResult > dataResult) { + improved.push(dataObject); + } else if (testResult < dataResult) { + regressed.push(dataObject); + } + }); - function processPage(index) { - var page = pages[index++]; - testPage(browser, "http://localhost:" + port + "/tests/" + page + "?selenium", function(result) { - if (numPages > index) { - processPage(index); - } else { - browser.close(browserDone); - } - stats[browserType].tests[page] = result; + if (improved.length > 0 || regressed.length > 0) { + if (regressed.length === 0) { + createResultFile(".baseline"); + } + + console.log((regressed.length > 0) ? colors.red : colors.green, browser); + + improved.map(setColor.bind(null, colors.green)).concat(regressed.map(setColor.bind(null, colors.red))).forEach(function(item) { + console.log(" *", item); }); } + }; + + function handleResults(stats) { + Object.keys(stats).forEach(openResultFile.bind(null, stats)); + } + + function runBrowsers(pages){ + + var port = 5555, + stats = {}, + browsers = ["chrome", "firefox", "internet explorer"], + browsersDone = 0, + server = createServer(port), + numPages = pages.length; + + var browserDone = function() { + if (++browsersDone >= browsers.length) { + server.close(); + handleResults(stats); + } + }; + + browsers.forEach(function(browserName){ + var browser = new webdriver({ + browser: browserName + }), + browserType; + browserName = browserName.replace("internet explorer", "iexplorer"); + browser.status(function(browserInfo){ + browserType = [browserName, browser.version, browserInfo.os.name.replace(/\s+/g, "-").toLowerCase()].join("-"); + var date = new Date(), + obj = { + tests: {}, + date: date.toISOString(), + version: browser.version + }; + stats[browserType] = obj; + stats[browserName] = obj; + processPage(0); + }); + + function processPage(index) { + var page = pages[index++]; + testPage(browser, "http://localhost:" + port + "/tests/" + page + "?selenium", function(result) { + if (numPages > index) { + processPage(index); + } else { + browser.close(browserDone); + } + stats[browserType].tests[page] = result; + }); + } + + }); + } + + walkDir("cases", function(err, results) { + if (err) throw err; + runBrowsers(results.slice(0, 2)); }); -} - -walkDir("cases", function(err, results) { - if (err) throw err; - runBrowsers(results); -}); +})(); From 118b42eb7ef747de2805be0b28ef3a5a0997a8da Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Fri, 28 Dec 2012 19:42:59 +0200 Subject: [PATCH 03/31] remove .baseline files --- .gitignore | 2 +- tests/results/chrome-23.0.1271.97-windows-vista.json.baseline | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 tests/results/chrome-23.0.1271.97-windows-vista.json.baseline diff --git a/.gitignore b/.gitignore index 4a2b018..7ce3c52 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,4 @@ node_modules/ server.js *.sublime-workspace chromedriver.log -.baseline \ No newline at end of file +*.baseline \ No newline at end of file diff --git a/tests/results/chrome-23.0.1271.97-windows-vista.json.baseline b/tests/results/chrome-23.0.1271.97-windows-vista.json.baseline deleted file mode 100644 index 478cb85..0000000 --- a/tests/results/chrome-23.0.1271.97-windows-vista.json.baseline +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":82.27},"date":"2012-12-28T17:20:56.098Z","version":"23.0.1271.97"} \ No newline at end of file From f0ce6917fa0c19cbf614953212b4b21cd8dc299c Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Fri, 28 Dec 2012 19:46:23 +0200 Subject: [PATCH 04/31] updated license formatting --- LICENSE | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/LICENSE b/LICENSE index d60aa56..a73ffc9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,22 @@ -/* - The MIT License +Copyright (c) 2012 Niklas von Hertzen - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From 07e80df3993d1384c7de518dba430b0cd070e7c4 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 14:33:30 +0200 Subject: [PATCH 05/31] Fixed lint error --- src/Parse.js | 4 ++-- tests/results/chrome-23.0.1271.97-windows-vista.json | 1 - tests/results/chrome.json | 1 - tests/results/firefox-12.0-windows-vista.json | 1 - tests/results/firefox.json | 1 - tests/results/iexplorer-9-windows-vista.json | 1 - tests/results/iexplorer.json | 1 - 7 files changed, 2 insertions(+), 8 deletions(-) delete mode 100644 tests/results/chrome-23.0.1271.97-windows-vista.json delete mode 100644 tests/results/chrome.json delete mode 100644 tests/results/firefox-12.0-windows-vista.json delete mode 100644 tests/results/firefox.json delete mode 100644 tests/results/iexplorer-9-windows-vista.json delete mode 100644 tests/results/iexplorer.json diff --git a/src/Parse.js b/src/Parse.js index 9eaca5a..2b273a2 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -274,8 +274,8 @@ _html2canvas.Parse = function (images, options) { text_align = text_align.replace(["-webkit-auto"],["auto"]); - renderList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(text_align) && noLetterSpacing(letter_spacing)) - ? textNode.nodeValue.split(/(\b| )/) + renderList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(text_align) && noLetterSpacing(letter_spacing)) ? + textNode.nodeValue.split(/(\b| )/) : textNode.nodeValue.split(""); metrics = setTextVariables(ctx, el, text_decoration, color); diff --git a/tests/results/chrome-23.0.1271.97-windows-vista.json b/tests/results/chrome-23.0.1271.97-windows-vista.json deleted file mode 100644 index 8c8cf53..0000000 --- a/tests/results/chrome-23.0.1271.97-windows-vista.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"cases/background/encoded.html":98,"cases/background/linear-gradient.html":82.27},"date":"2012-12-28T16:48:04.248Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/chrome.json b/tests/results/chrome.json deleted file mode 100644 index 01acad7..0000000 --- a/tests/results/chrome.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":82.27},"date":"2012-12-28T16:48:04.248Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/firefox-12.0-windows-vista.json b/tests/results/firefox-12.0-windows-vista.json deleted file mode 100644 index ffe0de2..0000000 --- a/tests/results/firefox-12.0-windows-vista.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":92.64},"date":"2012-12-28T16:48:10.772Z","version":"12.0"} \ No newline at end of file diff --git a/tests/results/firefox.json b/tests/results/firefox.json deleted file mode 100644 index 5b0f7bb..0000000 --- a/tests/results/firefox.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":85.64},"date":"2012-12-28T16:48:10.772Z","version":"12.0"} \ No newline at end of file diff --git a/tests/results/iexplorer-9-windows-vista.json b/tests/results/iexplorer-9-windows-vista.json deleted file mode 100644 index 526cd67..0000000 --- a/tests/results/iexplorer-9-windows-vista.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":100},"date":"2012-12-28T16:48:04.855Z","version":"9"} \ No newline at end of file diff --git a/tests/results/iexplorer.json b/tests/results/iexplorer.json deleted file mode 100644 index 526cd67..0000000 --- a/tests/results/iexplorer.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"cases/background/encoded.html":100,"cases/background/linear-gradient.html":100},"date":"2012-12-28T16:48:04.855Z","version":"9"} \ No newline at end of file From 52033a5d7297ee9abaaaab7b99a36751b87a0480 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 14:34:01 +0200 Subject: [PATCH 06/31] Fixed jquery path --- tests/qunit/index.html | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/qunit/index.html b/tests/qunit/index.html index 5aa5b38..4545f06 100644 --- a/tests/qunit/index.html +++ b/tests/qunit/index.html @@ -9,7 +9,7 @@ - + @@ -53,9 +53,9 @@
-
-
-
+
+
+
@@ -64,8 +64,8 @@
-
-
+
+
@@ -78,16 +78,16 @@
-
-
-
-
+
+
+
+
-
-
-
+
+
+
- - - -
- -
-
DIV #1 -
position: relative; -
-
DIV #2 -
position: absolute; -
z-index: 2; -
-
- -
- -
-
DIV #3 -
position: relative; -
z-index: 1; -
-
DIV #4 -
position: absolute; -
z-index: 10; -
-
- -

DIV #5
position:relative;
- -

DIV #6
position:static;
- - - diff --git a/tests/cases/border/dashed.html b/tests/cases/border/dashed.html new file mode 100644 index 0000000..74e95de --- /dev/null +++ b/tests/cases/border/dashed.html @@ -0,0 +1,47 @@ + + + + Borders tests + + + + + +
 
+
 
+
 
+
 
+ + diff --git a/tests/cases/border/dotted.html b/tests/cases/border/dotted.html new file mode 100644 index 0000000..77f1c35 --- /dev/null +++ b/tests/cases/border/dotted.html @@ -0,0 +1,47 @@ + + + + Borders tests + + + + + +
 
+
 
+
 
+
 
+ + diff --git a/tests/cases/border/double.html b/tests/cases/border/double.html new file mode 100644 index 0000000..daa3852 --- /dev/null +++ b/tests/cases/border/double.html @@ -0,0 +1,47 @@ + + + + Borders tests + + + + + +
 
+
 
+
 
+
 
+ + diff --git a/tests/cases/border/solid.html b/tests/cases/border/solid.html new file mode 100644 index 0000000..faab8a7 --- /dev/null +++ b/tests/cases/border/solid.html @@ -0,0 +1,47 @@ + + + + Borders tests + + + + + +
 
+
 
+
 
+
 
+ + From 630bed968ef75fdc539dfcb560011de2f9c5a12b Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 15:01:48 +0200 Subject: [PATCH 08/31] baseline results --- tests/results/chrome-23.0.1271.97-windows-vista.json | 1 + tests/results/chrome.json | 1 + tests/results/firefox-12.0-windows-vista.json | 1 + tests/results/firefox.json | 1 + tests/results/iexplorer-9-windows-vista.json | 1 + tests/results/iexplorer.json | 1 + 6 files changed, 6 insertions(+) create mode 100644 tests/results/chrome-23.0.1271.97-windows-vista.json create mode 100644 tests/results/chrome.json create mode 100644 tests/results/firefox-12.0-windows-vista.json create mode 100644 tests/results/firefox.json create mode 100644 tests/results/iexplorer-9-windows-vista.json create mode 100644 tests/results/iexplorer.json diff --git a/tests/results/chrome-23.0.1271.97-windows-vista.json b/tests/results/chrome-23.0.1271.97-windows-vista.json new file mode 100644 index 0000000..a815c3c --- /dev/null +++ b/tests/results/chrome-23.0.1271.97-windows-vista.json @@ -0,0 +1 @@ +{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":82.27,"tests/cases/background/multi.html":96.6,"tests/cases/background/position.html":90.4,"tests/cases/background/radial-gradient.html":57.9,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":96.45,"tests/cases/border/dotted.html":97.41,"tests/cases/border/double.html":97.96,"tests/cases/border/solid.html":99.97,"tests/cases/forms.html":95.96,"tests/cases/images/canvas.html":99.86,"tests/cases/images/cross-origin.html":97.99,"tests/cases/images/empty.html":99.86,"tests/cases/images/images.html":83.72,"tests/cases/images/svg.html":99.92,"tests/cases/list/decimal-leading-zero.html":99.63,"tests/cases/list/decimal.html":99.64,"tests/cases/list/lower-alpha.html":99.65,"tests/cases/list/upper-roman.html":99.45,"tests/cases/overflow.html":96.85,"tests/cases/text/linethrough.html":97.14,"tests/cases/text/text.html":95.42,"tests/cases/text/underline-lineheight.html":97.06,"tests/cases/text/underline.html":97.65,"tests/cases/visibility.html":99.19,"tests/cases/zindex/z-index1.html":97.09,"tests/cases/zindex/z-index2.html":95.94,"tests/cases/zindex/z-index3.html":98.98},"date":"2012-12-29T12:57:31.769Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/chrome.json b/tests/results/chrome.json new file mode 100644 index 0000000..a815c3c --- /dev/null +++ b/tests/results/chrome.json @@ -0,0 +1 @@ +{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":82.27,"tests/cases/background/multi.html":96.6,"tests/cases/background/position.html":90.4,"tests/cases/background/radial-gradient.html":57.9,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":96.45,"tests/cases/border/dotted.html":97.41,"tests/cases/border/double.html":97.96,"tests/cases/border/solid.html":99.97,"tests/cases/forms.html":95.96,"tests/cases/images/canvas.html":99.86,"tests/cases/images/cross-origin.html":97.99,"tests/cases/images/empty.html":99.86,"tests/cases/images/images.html":83.72,"tests/cases/images/svg.html":99.92,"tests/cases/list/decimal-leading-zero.html":99.63,"tests/cases/list/decimal.html":99.64,"tests/cases/list/lower-alpha.html":99.65,"tests/cases/list/upper-roman.html":99.45,"tests/cases/overflow.html":96.85,"tests/cases/text/linethrough.html":97.14,"tests/cases/text/text.html":95.42,"tests/cases/text/underline-lineheight.html":97.06,"tests/cases/text/underline.html":97.65,"tests/cases/visibility.html":99.19,"tests/cases/zindex/z-index1.html":97.09,"tests/cases/zindex/z-index2.html":95.94,"tests/cases/zindex/z-index3.html":98.98},"date":"2012-12-29T12:57:31.769Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/firefox-12.0-windows-vista.json b/tests/results/firefox-12.0-windows-vista.json new file mode 100644 index 0000000..68a96db --- /dev/null +++ b/tests/results/firefox-12.0-windows-vista.json @@ -0,0 +1 @@ +{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":85.64,"tests/cases/background/multi.html":96.45,"tests/cases/background/position.html":90.23,"tests/cases/background/radial-gradient.html":54.87,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":98.38,"tests/cases/border/dotted.html":96.46,"tests/cases/border/double.html":97.87,"tests/cases/border/solid.html":99.97,"tests/cases/forms.html":94.55,"tests/cases/images/canvas.html":100,"tests/cases/images/cross-origin.html":97.58,"tests/cases/images/empty.html":99.87,"tests/cases/images/images.html":96.93,"tests/cases/images/svg.html":96.79,"tests/cases/list/decimal-leading-zero.html":99.72,"tests/cases/list/decimal.html":99.73,"tests/cases/list/lower-alpha.html":99.73,"tests/cases/list/upper-roman.html":99.61,"tests/cases/overflow.html":97.49,"tests/cases/text/linethrough.html":94.12,"tests/cases/text/text.html":94.41,"tests/cases/text/underline-lineheight.html":92.35,"tests/cases/text/underline.html":93.5,"tests/cases/visibility.html":98.81,"tests/cases/zindex/z-index1.html":99.38,"tests/cases/zindex/z-index2.html":98.16,"tests/cases/zindex/z-index3.html":98.55},"date":"2012-12-29T12:57:40.717Z","version":"12.0"} \ No newline at end of file diff --git a/tests/results/firefox.json b/tests/results/firefox.json new file mode 100644 index 0000000..68a96db --- /dev/null +++ b/tests/results/firefox.json @@ -0,0 +1 @@ +{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":85.64,"tests/cases/background/multi.html":96.45,"tests/cases/background/position.html":90.23,"tests/cases/background/radial-gradient.html":54.87,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":98.38,"tests/cases/border/dotted.html":96.46,"tests/cases/border/double.html":97.87,"tests/cases/border/solid.html":99.97,"tests/cases/forms.html":94.55,"tests/cases/images/canvas.html":100,"tests/cases/images/cross-origin.html":97.58,"tests/cases/images/empty.html":99.87,"tests/cases/images/images.html":96.93,"tests/cases/images/svg.html":96.79,"tests/cases/list/decimal-leading-zero.html":99.72,"tests/cases/list/decimal.html":99.73,"tests/cases/list/lower-alpha.html":99.73,"tests/cases/list/upper-roman.html":99.61,"tests/cases/overflow.html":97.49,"tests/cases/text/linethrough.html":94.12,"tests/cases/text/text.html":94.41,"tests/cases/text/underline-lineheight.html":92.35,"tests/cases/text/underline.html":93.5,"tests/cases/visibility.html":98.81,"tests/cases/zindex/z-index1.html":99.38,"tests/cases/zindex/z-index2.html":98.16,"tests/cases/zindex/z-index3.html":98.55},"date":"2012-12-29T12:57:40.717Z","version":"12.0"} \ No newline at end of file diff --git a/tests/results/iexplorer-9-windows-vista.json b/tests/results/iexplorer-9-windows-vista.json new file mode 100644 index 0000000..9cdb8cf --- /dev/null +++ b/tests/results/iexplorer-9-windows-vista.json @@ -0,0 +1 @@ +{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":100,"tests/cases/background/multi.html":96.89,"tests/cases/background/position.html":90.41,"tests/cases/background/radial-gradient.html":94.02,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":97.7,"tests/cases/border/dotted.html":95.93,"tests/cases/border/double.html":97.95,"tests/cases/border/solid.html":99.98,"tests/cases/forms.html":95.02,"tests/cases/images/canvas.html":100,"tests/cases/images/cross-origin.html":99.35,"tests/cases/images/empty.html":99.85,"tests/cases/images/images.html":55.09,"tests/cases/images/svg.html":99.93,"tests/cases/list/decimal-leading-zero.html":35.040000000000006,"tests/cases/list/decimal.html":35.06,"tests/cases/list/lower-alpha.html":35.05,"tests/cases/list/upper-roman.html":35.11,"tests/cases/overflow.html":96.51,"tests/cases/text/linethrough.html":45.74,"tests/cases/text/text.html":79.85,"tests/cases/text/underline-lineheight.html":51.38,"tests/cases/text/underline.html":45.69,"tests/cases/visibility.html":99.39,"tests/cases/zindex/z-index1.html":99.54,"tests/cases/zindex/z-index2.html":97.81,"tests/cases/zindex/z-index3.html":98.68},"date":"2012-12-29T12:57:33.655Z","version":"9"} \ No newline at end of file diff --git a/tests/results/iexplorer.json b/tests/results/iexplorer.json new file mode 100644 index 0000000..9cdb8cf --- /dev/null +++ b/tests/results/iexplorer.json @@ -0,0 +1 @@ +{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":100,"tests/cases/background/multi.html":96.89,"tests/cases/background/position.html":90.41,"tests/cases/background/radial-gradient.html":94.02,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":97.7,"tests/cases/border/dotted.html":95.93,"tests/cases/border/double.html":97.95,"tests/cases/border/solid.html":99.98,"tests/cases/forms.html":95.02,"tests/cases/images/canvas.html":100,"tests/cases/images/cross-origin.html":99.35,"tests/cases/images/empty.html":99.85,"tests/cases/images/images.html":55.09,"tests/cases/images/svg.html":99.93,"tests/cases/list/decimal-leading-zero.html":35.040000000000006,"tests/cases/list/decimal.html":35.06,"tests/cases/list/lower-alpha.html":35.05,"tests/cases/list/upper-roman.html":35.11,"tests/cases/overflow.html":96.51,"tests/cases/text/linethrough.html":45.74,"tests/cases/text/text.html":79.85,"tests/cases/text/underline-lineheight.html":51.38,"tests/cases/text/underline.html":45.69,"tests/cases/visibility.html":99.39,"tests/cases/zindex/z-index1.html":99.54,"tests/cases/zindex/z-index2.html":97.81,"tests/cases/zindex/z-index3.html":98.68},"date":"2012-12-29T12:57:33.655Z","version":"9"} \ No newline at end of file From 05f3af4901cf487aa70346e5be050d040768024f Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 15:02:05 +0200 Subject: [PATCH 09/31] webdriver grunt task --- grunt.js | 16 ++++++++++-- tests/selenium.js | 62 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/grunt.js b/grunt.js index 81cfa9e..75ac5fb 100644 --- a/grunt.js +++ b/grunt.js @@ -13,7 +13,7 @@ module.exports = function(grunt) { post: '})(window,document);' }, lint: { - files: ['grunt.js', 'build/<%= pkg.name %>.js'] + files: ['build/<%= pkg.name %>.js'] }, qunit: { files: ['tests/qunit/index.html'] @@ -55,7 +55,19 @@ module.exports = function(grunt) { uglify: {} }); + var selenium = require("./tests/selenium.js"); + grunt.registerTask('webdriver', 'Browser render tests', function(arg1) { + + var done = this.async(); + + if (arguments.length === 0) { + selenium.tests(); + } else if (arg1 === "baseline") { + selenium.baseline(); + } + }); + // Default task. - grunt.registerTask('default', 'concat lint qunit min'); + grunt.registerTask('default', 'concat lint qunit webdriver min'); }; diff --git a/tests/selenium.js b/tests/selenium.js index b8e4074..2d7bf39 100644 --- a/tests/selenium.js +++ b/tests/selenium.js @@ -11,7 +11,7 @@ function createServer(port) { return http.createServer(function(request, response) { var uri = url.parse(request.url).pathname, - filename = path.join(process.cwd(), "../" + uri); + filename = path.join(process.cwd(), uri); fs.exists(filename, function(exists) { if(!exists) { @@ -73,9 +73,15 @@ (new PNG(arraybuffer)).decode(func); } + function getBaselineFiles() { + return fs.readdirSync("tests/results/").filter(function(name) { + return /\.baseline$/.test(name); + }).map(function(item) { + return "tests/results/" + item; + }); + } function testPage(browser, url, done) { - browser.url(url) .$(".html2canvas", 5000, function(){ this.execute(function(){ @@ -105,7 +111,7 @@ var openResultFile = function(stats, browser) { var tests = stats[browser].tests, - filename = "results/" + browser + ".json", + filename = "tests/results/" + browser + ".json", write = writeResultFile.bind(null, filename, JSON.stringify(stats[browser])); fs.exists(filename, function(exists) { @@ -118,44 +124,53 @@ }; var setColor = function(color, text) { - return color + text.amount + "% " + text.test; + return [color, " * ", ((isNaN(text.amount)) ? "NEW" : text.amount + "%"), " ", text.test].join(""); }; var parseResultFile = function(tests, browser, createResultFile, err, file) { if (err) throw err; var data = JSON.parse(file), improved = [], + regressed = [], + newItems = [], colors = { red: "\x1b[1;31m", + blue: "\x1b[1;36m", + violet: "\x1b[0;35m", green: "\x1b[0;32m" - }, - regressed = []; + }; Object.keys(tests).forEach(function(test){ var testResult = tests[test], dataResult = data.tests[test], dataObject = { - amount: testResult - dataResult, + amount: (Math.abs(testResult - dataResult) < 0.02) ? 0 : testResult - dataResult, test: test }; - if (testResult > dataResult) { + if (dataObject.amount > 0) { improved.push(dataObject); - } else if (testResult < dataResult) { + } else if (dataObject.amount < 0) { regressed.push(dataObject); + } else if (dataResult === undefined) { + newItems.push(dataObject); } }); - if (improved.length > 0 || regressed.length > 0) { + if (newItems.length > 0 || improved.length > 0 || regressed.length > 0) { if (regressed.length === 0) { createResultFile(".baseline"); } + console.log(colors.violet, "********************"); console.log((regressed.length > 0) ? colors.red : colors.green, browser); - improved.map(setColor.bind(null, colors.green)).concat(regressed.map(setColor.bind(null, colors.red))).forEach(function(item) { - console.log(" *", item); + improved.map(setColor.bind(null, colors.green)) + .concat(regressed.map(setColor.bind(null, colors.red))) + .concat(newItems.map(setColor.bind(null, colors.blue))) + .forEach(function(item) { + console.log(item); }); } @@ -202,7 +217,7 @@ function processPage(index) { var page = pages[index++]; - testPage(browser, "http://localhost:" + port + "/tests/" + page + "?selenium", function(result) { + testPage(browser, "http://localhost:" + port + "/" + page + "?selenium", function(result) { if (numPages > index) { processPage(index); } else { @@ -215,9 +230,20 @@ }); } - walkDir("cases", function(err, results) { - if (err) throw err; - runBrowsers(results.slice(0, 2)); - }); + exports.tests = function() { + getBaselineFiles().forEach(fs.unlinkSync.bind(fs)); + walkDir("tests/cases", function(err, results) { + if (err) throw err; + runBrowsers(results); + }); + }; -})(); + exports.baseline = function() { + getBaselineFiles().forEach(function(file) { + var newName = file.substring(0, file.length - 9); + fs.renameSync(file, newName); + console.log(newName, "created"); + }); + }; + +})(); \ No newline at end of file From e7b4dd17b9397d2ae9a294ef810fe195d8e23830 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 15:31:24 +0200 Subject: [PATCH 10/31] updated readme --- readme.md | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index 17a0317..dcc9f68 100644 --- a/readme.md +++ b/readme.md @@ -7,13 +7,12 @@ html2canvas ###How does it work?### -The script renders the current page as a canvas image, by reading the DOM and the different styles applied to the elements. However, as many elements are displayed differently on different browsers and operating systems (such as form elements such as radio buttons or checkboxes) as well as +The script renders the current page as a canvas image, by reading the DOM and the different styles applied to the elements. -It does not require any rendering from the server, as the whole image is created on the clients browser. However, for browsers without canvas support alternatives such as flashcanvas or ExplorerCanvas are necessary to create the image. +It does not require any rendering from the server, as the whole image is created on the clients browser. However, as it is heavily dependent on the browser, this library is *not suitable* to be used on for example on node.js. +It doesn't magically circumvent and browser content policy restrictions either, so rendering cross origin content will require a proxy to get the content to the same origin. -Additionally, to render iframe content or images situated outside of the same origin policy a proxy will be necessary to load the content to the users browser. - -The script is still in a very experimental state, so I don't recommend using it in a production environment nor start building applications with it yet, as there will be still major changes made. However, please do test it out and report your findings, especially if something should be working, but is displaying it incorrectly. +The script is still in a **very experimental state**, so I don't recommend using it in a production environment nor start building applications with it yet, as there will be still major changes made. ###Browser compatibility### @@ -21,8 +20,8 @@ The script should work fine on the following browsers: * Firefox 3.5+ * Google Chrome -* Newer versions of Opera (exactly how new is yet to be determined) -* >=IE9 (Older versions compatible with the use of flashcanvas) +* Opera 12+ +* IE9+ Note that the compatibility will most likely be increased in future builds, as many of the current restrictions have at least partial work arounds, which can be used with older browser versions. @@ -46,6 +45,36 @@ To access the created canvas, provide the `onrendered` event in the options whic } }); +### Building ### + +The library uses grunt for building. Alternatively, you can download ready builds from the downloads page. + +Run the full build process (including lint, qunit and webdriver tests): + + $ grunt + +Skip lint tests and simply build from source: + + $ grunt concat + $ grunt min + +### Running tests ### + +The library has two sets of tests. The first set is a number of qunit tests that check that different values parsed by browsers are correctly converted in html2canvas. To run these tests with grunt you'll need phantomjs. + +The other set of tests run Firefox, Chrome and Internet Explorer with webdriver. They capture an actual screenshot from the test pages and compare the image to the screenshot created by html2canvas and calculate the percentage differences. These tests generally aren't expected to provide 100% matches, but while commiting changes, these should generally not go decrease from the baseline values. + +Run qunit tests: + + $ grunt test + +Run webdriver tests: + + $ grunt webdriver + +Commiting improvements in baseline values: + + $ grunt webdriver:baseline ### Examples ### @@ -53,6 +82,11 @@ For more information and examples, please visit the webdriver + v0.34 - 26.6.2012 * Removed (last?) jQuery dependencies (niklasvh) From d1ee6e9d64efa5a5a941bddd3df898a34f880ef3 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 16:02:46 +0200 Subject: [PATCH 11/31] removed baseline files --- readme.md | 2 +- tests/results/.gitignore | 1 + tests/results/chrome-23.0.1271.97-windows-vista.json | 1 - tests/results/chrome.json | 1 - tests/results/firefox-12.0-windows-vista.json | 1 - tests/results/firefox.json | 1 - tests/results/iexplorer-9-windows-vista.json | 1 - tests/results/iexplorer.json | 1 - 8 files changed, 2 insertions(+), 7 deletions(-) create mode 100644 tests/results/.gitignore delete mode 100644 tests/results/chrome-23.0.1271.97-windows-vista.json delete mode 100644 tests/results/chrome.json delete mode 100644 tests/results/firefox-12.0-windows-vista.json delete mode 100644 tests/results/firefox.json delete mode 100644 tests/results/iexplorer-9-windows-vista.json delete mode 100644 tests/results/iexplorer.json diff --git a/readme.md b/readme.md index dcc9f68..b484d05 100644 --- a/readme.md +++ b/readme.md @@ -53,7 +53,7 @@ Run the full build process (including lint, qunit and webdriver tests): $ grunt -Skip lint tests and simply build from source: +Skip lint and tests and simply build from source: $ grunt concat $ grunt min diff --git a/tests/results/.gitignore b/tests/results/.gitignore new file mode 100644 index 0000000..b722e9e --- /dev/null +++ b/tests/results/.gitignore @@ -0,0 +1 @@ +!.gitignore \ No newline at end of file diff --git a/tests/results/chrome-23.0.1271.97-windows-vista.json b/tests/results/chrome-23.0.1271.97-windows-vista.json deleted file mode 100644 index a815c3c..0000000 --- a/tests/results/chrome-23.0.1271.97-windows-vista.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":82.27,"tests/cases/background/multi.html":96.6,"tests/cases/background/position.html":90.4,"tests/cases/background/radial-gradient.html":57.9,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":96.45,"tests/cases/border/dotted.html":97.41,"tests/cases/border/double.html":97.96,"tests/cases/border/solid.html":99.97,"tests/cases/forms.html":95.96,"tests/cases/images/canvas.html":99.86,"tests/cases/images/cross-origin.html":97.99,"tests/cases/images/empty.html":99.86,"tests/cases/images/images.html":83.72,"tests/cases/images/svg.html":99.92,"tests/cases/list/decimal-leading-zero.html":99.63,"tests/cases/list/decimal.html":99.64,"tests/cases/list/lower-alpha.html":99.65,"tests/cases/list/upper-roman.html":99.45,"tests/cases/overflow.html":96.85,"tests/cases/text/linethrough.html":97.14,"tests/cases/text/text.html":95.42,"tests/cases/text/underline-lineheight.html":97.06,"tests/cases/text/underline.html":97.65,"tests/cases/visibility.html":99.19,"tests/cases/zindex/z-index1.html":97.09,"tests/cases/zindex/z-index2.html":95.94,"tests/cases/zindex/z-index3.html":98.98},"date":"2012-12-29T12:57:31.769Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/chrome.json b/tests/results/chrome.json deleted file mode 100644 index a815c3c..0000000 --- a/tests/results/chrome.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":82.27,"tests/cases/background/multi.html":96.6,"tests/cases/background/position.html":90.4,"tests/cases/background/radial-gradient.html":57.9,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":96.45,"tests/cases/border/dotted.html":97.41,"tests/cases/border/double.html":97.96,"tests/cases/border/solid.html":99.97,"tests/cases/forms.html":95.96,"tests/cases/images/canvas.html":99.86,"tests/cases/images/cross-origin.html":97.99,"tests/cases/images/empty.html":99.86,"tests/cases/images/images.html":83.72,"tests/cases/images/svg.html":99.92,"tests/cases/list/decimal-leading-zero.html":99.63,"tests/cases/list/decimal.html":99.64,"tests/cases/list/lower-alpha.html":99.65,"tests/cases/list/upper-roman.html":99.45,"tests/cases/overflow.html":96.85,"tests/cases/text/linethrough.html":97.14,"tests/cases/text/text.html":95.42,"tests/cases/text/underline-lineheight.html":97.06,"tests/cases/text/underline.html":97.65,"tests/cases/visibility.html":99.19,"tests/cases/zindex/z-index1.html":97.09,"tests/cases/zindex/z-index2.html":95.94,"tests/cases/zindex/z-index3.html":98.98},"date":"2012-12-29T12:57:31.769Z","version":"23.0.1271.97"} \ No newline at end of file diff --git a/tests/results/firefox-12.0-windows-vista.json b/tests/results/firefox-12.0-windows-vista.json deleted file mode 100644 index 68a96db..0000000 --- a/tests/results/firefox-12.0-windows-vista.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":85.64,"tests/cases/background/multi.html":96.45,"tests/cases/background/position.html":90.23,"tests/cases/background/radial-gradient.html":54.87,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":98.38,"tests/cases/border/dotted.html":96.46,"tests/cases/border/double.html":97.87,"tests/cases/border/solid.html":99.97,"tests/cases/forms.html":94.55,"tests/cases/images/canvas.html":100,"tests/cases/images/cross-origin.html":97.58,"tests/cases/images/empty.html":99.87,"tests/cases/images/images.html":96.93,"tests/cases/images/svg.html":96.79,"tests/cases/list/decimal-leading-zero.html":99.72,"tests/cases/list/decimal.html":99.73,"tests/cases/list/lower-alpha.html":99.73,"tests/cases/list/upper-roman.html":99.61,"tests/cases/overflow.html":97.49,"tests/cases/text/linethrough.html":94.12,"tests/cases/text/text.html":94.41,"tests/cases/text/underline-lineheight.html":92.35,"tests/cases/text/underline.html":93.5,"tests/cases/visibility.html":98.81,"tests/cases/zindex/z-index1.html":99.38,"tests/cases/zindex/z-index2.html":98.16,"tests/cases/zindex/z-index3.html":98.55},"date":"2012-12-29T12:57:40.717Z","version":"12.0"} \ No newline at end of file diff --git a/tests/results/firefox.json b/tests/results/firefox.json deleted file mode 100644 index 68a96db..0000000 --- a/tests/results/firefox.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":85.64,"tests/cases/background/multi.html":96.45,"tests/cases/background/position.html":90.23,"tests/cases/background/radial-gradient.html":54.87,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":98.38,"tests/cases/border/dotted.html":96.46,"tests/cases/border/double.html":97.87,"tests/cases/border/solid.html":99.97,"tests/cases/forms.html":94.55,"tests/cases/images/canvas.html":100,"tests/cases/images/cross-origin.html":97.58,"tests/cases/images/empty.html":99.87,"tests/cases/images/images.html":96.93,"tests/cases/images/svg.html":96.79,"tests/cases/list/decimal-leading-zero.html":99.72,"tests/cases/list/decimal.html":99.73,"tests/cases/list/lower-alpha.html":99.73,"tests/cases/list/upper-roman.html":99.61,"tests/cases/overflow.html":97.49,"tests/cases/text/linethrough.html":94.12,"tests/cases/text/text.html":94.41,"tests/cases/text/underline-lineheight.html":92.35,"tests/cases/text/underline.html":93.5,"tests/cases/visibility.html":98.81,"tests/cases/zindex/z-index1.html":99.38,"tests/cases/zindex/z-index2.html":98.16,"tests/cases/zindex/z-index3.html":98.55},"date":"2012-12-29T12:57:40.717Z","version":"12.0"} \ No newline at end of file diff --git a/tests/results/iexplorer-9-windows-vista.json b/tests/results/iexplorer-9-windows-vista.json deleted file mode 100644 index 9cdb8cf..0000000 --- a/tests/results/iexplorer-9-windows-vista.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":100,"tests/cases/background/multi.html":96.89,"tests/cases/background/position.html":90.41,"tests/cases/background/radial-gradient.html":94.02,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":97.7,"tests/cases/border/dotted.html":95.93,"tests/cases/border/double.html":97.95,"tests/cases/border/solid.html":99.98,"tests/cases/forms.html":95.02,"tests/cases/images/canvas.html":100,"tests/cases/images/cross-origin.html":99.35,"tests/cases/images/empty.html":99.85,"tests/cases/images/images.html":55.09,"tests/cases/images/svg.html":99.93,"tests/cases/list/decimal-leading-zero.html":35.040000000000006,"tests/cases/list/decimal.html":35.06,"tests/cases/list/lower-alpha.html":35.05,"tests/cases/list/upper-roman.html":35.11,"tests/cases/overflow.html":96.51,"tests/cases/text/linethrough.html":45.74,"tests/cases/text/text.html":79.85,"tests/cases/text/underline-lineheight.html":51.38,"tests/cases/text/underline.html":45.69,"tests/cases/visibility.html":99.39,"tests/cases/zindex/z-index1.html":99.54,"tests/cases/zindex/z-index2.html":97.81,"tests/cases/zindex/z-index3.html":98.68},"date":"2012-12-29T12:57:33.655Z","version":"9"} \ No newline at end of file diff --git a/tests/results/iexplorer.json b/tests/results/iexplorer.json deleted file mode 100644 index 9cdb8cf..0000000 --- a/tests/results/iexplorer.json +++ /dev/null @@ -1 +0,0 @@ -{"tests":{"tests/cases/background/encoded.html":100,"tests/cases/background/linear-gradient.html":100,"tests/cases/background/multi.html":96.89,"tests/cases/background/position.html":90.41,"tests/cases/background/radial-gradient.html":94.02,"tests/cases/background/repeat.html":100,"tests/cases/border/dashed.html":97.7,"tests/cases/border/dotted.html":95.93,"tests/cases/border/double.html":97.95,"tests/cases/border/solid.html":99.98,"tests/cases/forms.html":95.02,"tests/cases/images/canvas.html":100,"tests/cases/images/cross-origin.html":99.35,"tests/cases/images/empty.html":99.85,"tests/cases/images/images.html":55.09,"tests/cases/images/svg.html":99.93,"tests/cases/list/decimal-leading-zero.html":35.040000000000006,"tests/cases/list/decimal.html":35.06,"tests/cases/list/lower-alpha.html":35.05,"tests/cases/list/upper-roman.html":35.11,"tests/cases/overflow.html":96.51,"tests/cases/text/linethrough.html":45.74,"tests/cases/text/text.html":79.85,"tests/cases/text/underline-lineheight.html":51.38,"tests/cases/text/underline.html":45.69,"tests/cases/visibility.html":99.39,"tests/cases/zindex/z-index1.html":99.54,"tests/cases/zindex/z-index2.html":97.81,"tests/cases/zindex/z-index3.html":98.68},"date":"2012-12-29T12:57:33.655Z","version":"9"} \ No newline at end of file From a82234873e06009a3f8b8070c609a8e5ccea8c3a Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 16:04:50 +0200 Subject: [PATCH 12/31] updated .gitignore --- tests/results/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/results/.gitignore b/tests/results/.gitignore index b722e9e..c96a04f 100644 --- a/tests/results/.gitignore +++ b/tests/results/.gitignore @@ -1 +1,2 @@ +* !.gitignore \ No newline at end of file From 5f1fedf8f0364062e65360ce104df4accdcb4940 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 16:31:57 +0200 Subject: [PATCH 13/31] added npm dependencies --- package.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f3ba81..91a07a2 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,14 @@ "author": { "name":"Niklas von Hertzen (@niklasvh)" }, + "dependencies": { + "base64-arraybuffer": ">= 0.1.0", + "png-js": ">= 0.1.1", + "webdriver.js": ">= 0.1.0" + }, "homepage": "http://html2canvas.hertzen.com", - "licenses": [{"type": "MIT"}] + "licenses": [{ + "type": "MIT" + }] } \ No newline at end of file From 816ff6d3c5099d4fc23b55ad546aabba01ac73bf Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 16:34:56 +0200 Subject: [PATCH 14/31] updated testing instructions --- .gitignore | 1 + readme.md | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7ce3c52..227e65e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /tests/cache/ /tests/flashcanvas.html /lib/ +/bin/ /build/ image.jpg /.project diff --git a/readme.md b/readme.md index b484d05..e5f50d9 100644 --- a/readme.md +++ b/readme.md @@ -62,7 +62,11 @@ Skip lint and tests and simply build from source: The library has two sets of tests. The first set is a number of qunit tests that check that different values parsed by browsers are correctly converted in html2canvas. To run these tests with grunt you'll need phantomjs. -The other set of tests run Firefox, Chrome and Internet Explorer with webdriver. They capture an actual screenshot from the test pages and compare the image to the screenshot created by html2canvas and calculate the percentage differences. These tests generally aren't expected to provide 100% matches, but while commiting changes, these should generally not go decrease from the baseline values. +The other set of tests run Firefox, Chrome and Internet Explorer with webdriver. The selenium standalone server (runs on Java) is required for these tests and can be downloaded from here. They capture an actual screenshot from the test pages and compare the image to the screenshot created by html2canvas and calculate the percentage differences. These tests generally aren't expected to provide 100% matches, but while commiting changes, these should generally not go decrease from the baseline values. + +If you didn't download `html2canvas` from `npm`, start by downloading the dependencies: + + $ npm update Run qunit tests: @@ -70,6 +74,7 @@ Run qunit tests: Run webdriver tests: + $ java -jar /path/to/selenium-server-standalone-2.xx.x.jar $ grunt webdriver Commiting improvements in baseline values: From c3e9636e4f9e30df0ad38e5bcb7c8e8a7982ff0e Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 17:43:15 +0200 Subject: [PATCH 15/31] Moved renderer logic to renderer.js --- src/Renderer.js | 17 +++++++++++++---- src/Util.js | 19 +++++-------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Renderer.js b/src/Renderer.js index 1703cc8..25abb00 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -1,5 +1,5 @@ _html2canvas.Renderer = function(parseQueue, options){ - var queue = []; + var queue = [], renderer; function sortZ(zStack){ var subStacks = [], @@ -47,11 +47,20 @@ _html2canvas.Renderer = function(parseQueue, options){ } - sortZ(parseQueue.zIndex); - if ( typeof options._renderer._create !== "function" ) { + + if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { + renderer = _html2canvas.Renderer[options.renderer](options); + } else if (typeof options.renderer === "function") { + renderer = options.renderer(options); + } else { + throw new Error("Unknown renderer"); + } + + if ( typeof renderer._create !== "function" ) { throw new Error("Invalid renderer defined"); } - return options._renderer._create( parseQueue, options, document, queue, _html2canvas ); + + return renderer._create( parseQueue, options, document, queue, _html2canvas ); }; diff --git a/src/Util.js b/src/Util.js index f17a70d..939936d 100644 --- a/src/Util.js +++ b/src/Util.js @@ -1,5 +1,4 @@ -html2canvas = function( elements, opts ) { - +window.html2canvas = function(elements, opts) { var queue, canvas, options = { @@ -8,7 +7,7 @@ html2canvas = function( elements, opts ) { elements: elements, // preload options - proxy: "http://html2canvas.appspot.com/", + proxy: "", timeout: 0, // no timeout useCORS: false, // try to load images as CORS (where available), before falling back to proxy allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true @@ -27,18 +26,10 @@ html2canvas = function( elements, opts ) { height: null, taintTest: true, // do a taint test with all images before applying to canvas renderer: "Canvas" - }, renderer; + }; options = _html2canvas.Util.Extend(opts, options); - if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { - options._renderer = _html2canvas.Renderer[options.renderer]( options ); - } else if (typeof options.renderer === "function") { - options._renderer = options.renderer( options ); - } else { - throw("Unknown renderer"); - } - _html2canvas.logging = options.logging; options.complete = function( images ) { @@ -83,7 +74,7 @@ html2canvas = function( elements, opts ) { }; }; -html2canvas.log = h2clog; // for renderers -html2canvas.Renderer = { +window.html2canvas.log = h2clog; // for renderers +window.html2canvas.Renderer = { Canvas: undefined // We are assuming this will be used }; \ No newline at end of file From c72a02bf64537069c20e8624d3d4b8bdc2937f0f Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 21:06:40 +0200 Subject: [PATCH 16/31] refactoring --- src/Core.js | 358 +++++++++++++++++++++++------------------------- src/Renderer.js | 104 +++++++------- 2 files changed, 217 insertions(+), 245 deletions(-) diff --git a/src/Core.js b/src/Core.js index a5b4356..f50a6f5 100644 --- a/src/Core.js +++ b/src/Core.js @@ -7,216 +7,196 @@ html2canvas; function h2clog(a) { - if (_html2canvas.logging && window.console && window.console.log) { - window.console.log(a); - } + if (_html2canvas.logging && window.console && window.console.log) { + window.console.log(a); + } } _html2canvas.Util = {}; _html2canvas.Util.backgroundImage = function (src) { - if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) { - return src; - } - - if (src.toLowerCase().substr( 0, 5 ) === 'url("') { - src = src.substr( 5 ); - src = src.substr( 0, src.length - 2 ); - } else { - src = src.substr( 4 ); - src = src.substr( 0, src.length - 1 ); - } - + if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) { return src; + } + + if (src.toLowerCase().substr( 0, 5 ) === 'url("') { + src = src.substr( 5 ); + src = src.substr( 0, src.length - 2 ); + } else { + src = src.substr( 4 ); + src = src.substr( 0, src.length - 1 ); + } + + return src; }; _html2canvas.Util.Bounds = function getBounds (el) { - var clientRect, - bounds = {}; + var clientRect, + bounds = {}; - if (el.getBoundingClientRect){ - clientRect = el.getBoundingClientRect(); + if (el.getBoundingClientRect){ + clientRect = el.getBoundingClientRect(); - // TODO add scroll position to bounds, so no scrolling of window necessary - bounds.top = clientRect.top; - bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); - bounds.left = clientRect.left; + // TODO add scroll position to bounds, so no scrolling of window necessary + bounds.top = clientRect.top; + bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); + bounds.left = clientRect.left; - // older IE doesn't have width/height, but top/bottom instead - bounds.width = clientRect.width || (clientRect.right - clientRect.left); - bounds.height = clientRect.height || (clientRect.bottom - clientRect.top); + // older IE doesn't have width/height, but top/bottom instead + bounds.width = clientRect.width || (clientRect.right - clientRect.left); + bounds.height = clientRect.height || (clientRect.bottom - clientRect.top); - return bounds; + return bounds; - } + } }; _html2canvas.Util.getCSS = function (el, attribute) { - // return $(el).css(attribute); + // return $(el).css(attribute); - var val; + var val; - function toPX( attribute, val ) { - var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ], - left, - style = el.style; + function toPX( attribute, val ) { + var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ], + left, + style = el.style; - // Check if we are not dealing with pixels, (Opera has issues with this) - // Ported from jQuery css.js - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + // Check if we are not dealing with pixels, (Opera has issues with this) + // Ported from jQuery css.js + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels - if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) { + if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) { - // Remember the original values - left = style.left; + // Remember the original values + left = style.left; - // Put in the new values to get a computed value out - if ( rsLeft ) { - el.runtimeStyle.left = el.currentStyle.left; - } - style.left = attribute === "fontSize" ? "1em" : (val || 0); - val = style.pixelLeft + "px"; + // Put in the new values to get a computed value out + if ( rsLeft ) { + el.runtimeStyle.left = el.currentStyle.left; + } + style.left = attribute === "fontSize" ? "1em" : (val || 0); + val = style.pixelLeft + "px"; - // Revert the changed values - style.left = left; - if ( rsLeft ) { - el.runtimeStyle.left = rsLeft; - } - - } - - if (!/^(thin|medium|thick)$/i.test( val )) { - return Math.round(parseFloat( val )) + "px"; - } - - return val; + // Revert the changed values + style.left = left; + if ( rsLeft ) { + el.runtimeStyle.left = rsLeft; + } } - - if ( window.getComputedStyle ) { - if ( previousElement !== el ) { - computedCSS = document.defaultView.getComputedStyle(el, null); - } - val = computedCSS[ attribute ]; - - if ( attribute === "backgroundPosition" ) { - - val = (val.split(",")[0] || "0 0").split(" "); - - val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ]; - val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always - val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ]; - } else if ( /border(Top|Bottom)(Left|Right)Radius/.test( attribute) ) { - var arr = val.split(" "); - if ( arr.length <= 1 ) { - arr[ 1 ] = arr[ 0 ]; - } - arr[ 0 ] = parseInt( arr[ 0 ], 10 ); - arr[ 1 ] = parseInt( arr[ 1 ], 10 ); - val = arr; - } - - } else if ( el.currentStyle ) { - // IE 9> - if (attribute === "backgroundPosition") { - // Older IE uses -x and -y - val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ]; - } else { - - val = toPX( attribute, el.currentStyle[ attribute ] ); - - if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) { - switch (val) { - case "thin": - val = "1px"; - break; - case "medium": - val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around - break; - case "thick": - val = "5px"; - break; - } - } - } - - - + if (!/^(thin|medium|thick)$/i.test( val )) { + return Math.round(parseFloat( val )) + "px"; } - - - return val; + } -//return $(el).css(attribute); + if ( window.getComputedStyle ) { + if ( previousElement !== el ) { + computedCSS = document.defaultView.getComputedStyle(el, null); + } + val = computedCSS[ attribute ]; + if ( attribute === "backgroundPosition" ) { + val = (val.split(",")[0] || "0 0").split(" "); + + val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ]; + val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always + val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ]; + } else if ( /border(Top|Bottom)(Left|Right)Radius/.test( attribute) ) { + var arr = val.split(" "); + if ( arr.length <= 1 ) { + arr[ 1 ] = arr[ 0 ]; + } + arr[ 0 ] = parseInt( arr[ 0 ], 10 ); + arr[ 1 ] = parseInt( arr[ 1 ], 10 ); + val = arr; + } + + } else if ( el.currentStyle ) { + // IE 9> + if (attribute === "backgroundPosition") { + // Older IE uses -x and -y + val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ]; + } else { + + val = toPX( attribute, el.currentStyle[ attribute ] ); + + if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) { + switch (val) { + case "thin": + val = "1px"; + break; + case "medium": + val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around + break; + case "thick": + val = "5px"; + break; + } + } + } + } + + return val; }; _html2canvas.Util.BackgroundPosition = function ( el, bounds, image ) { - // TODO add support for multi image backgrounds + // TODO add support for multi image backgrounds - var bgposition = _html2canvas.Util.getCSS( el, "backgroundPosition" ) , - topPos, - left, - percentage, - val; + var bgposition = _html2canvas.Util.getCSS( el, "backgroundPosition" ) , + topPos, + left, + percentage, + val; - if (bgposition.length === 1){ - val = bgposition; + if (bgposition.length === 1){ + val = bgposition; - bgposition = []; + bgposition = []; - bgposition[0] = val; - bgposition[1] = val; - } + bgposition[0] = val; + bgposition[1] = val; + } + if (bgposition[0].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[0])/100); + left = ((bounds.width * percentage)-(image.width*percentage)); + } else { + left = parseInt(bgposition[0],10); + } + if (bgposition[1].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[1])/100); + topPos = ((bounds.height * percentage)-(image.height*percentage)); + } else { + topPos = parseInt(bgposition[1],10); + } - if (bgposition[0].toString().indexOf("%") !== -1){ - percentage = (parseFloat(bgposition[0])/100); - left = ((bounds.width * percentage)-(image.width*percentage)); - - }else{ - left = parseInt(bgposition[0],10); - } - - if (bgposition[1].toString().indexOf("%") !== -1){ - - percentage = (parseFloat(bgposition[1])/100); - topPos = ((bounds.height * percentage)-(image.height*percentage)); - }else{ - topPos = parseInt(bgposition[1],10); - } - - - - - return { - top: topPos, - left: left - }; - + return { + top: topPos, + left: left + }; }; _html2canvas.Util.Extend = function (options, defaults) { - for (var key in options) { - if (options.hasOwnProperty(key)) { - defaults[key] = options[key]; - } + for (var key in options) { + if (options.hasOwnProperty(key)) { + defaults[key] = options[key]; } - return defaults; + } + return defaults; }; @@ -229,43 +209,43 @@ _html2canvas.Util.Extend = function (options, defaults) { _html2canvas.Util.Children = function( elem ) { - var children; - try { + var children; + try { - children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ? - elem.contentDocument || elem.contentWindow.document : (function( array ){ - var ret = []; + children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ? + elem.contentDocument || elem.contentWindow.document : (function( array ){ + var ret = []; - if ( array !== null ) { + if ( array !== null ) { - (function( first, second ) { - var i = first.length, - j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - })( ret, array ); + (function( first, second ) { + var i = first.length, + j = 0; + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; } - return ret; - })( elem.childNodes ); + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } - } catch (ex) { - h2clog("html2canvas.Util.Children failed with exception: " + ex.message); - children = []; - } - return children; + first.length = i; + + return first; + })( ret, array ); + + } + + return ret; + })( elem.childNodes ); + + } catch (ex) { + h2clog("html2canvas.Util.Children failed with exception: " + ex.message); + children = []; + } + return children; }; diff --git a/src/Renderer.js b/src/Renderer.js index 25abb00..edf4d90 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -1,66 +1,58 @@ _html2canvas.Renderer = function(parseQueue, options){ - var queue = [], renderer; - function sortZ(zStack){ - var subStacks = [], - stackValues = [], - zStackChildren = zStack.children, - s, - i, - stackLen, - zValue, - zLen, - stackChild, - b, - subStackLen; + function createRenderQueue(parseQueue) { + var queue = []; + var sortZ = function(zStack){ + var subStacks = [], + stackValues = []; - for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){ - - stackChild = zStackChildren[s]; - - if (stackChild.children && stackChild.children.length > 0){ - subStacks.push(stackChild); - stackValues.push(stackChild.zindex); - }else{ - queue.push(stackChild); - } - - } - - stackValues.sort(function(a, b) { - return a - b; - }); - - for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){ - zValue = stackValues[i]; - for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){ - - if (subStacks[b].zindex === zValue){ - stackChild = subStacks.splice(b, 1); - sortZ(stackChild[0]); - break; - + zStack.children.forEach(function(stackChild) { + if (stackChild.children && stackChild.children.length > 0){ + subStacks.push(stackChild); + stackValues.push(stackChild.zindex); + } else { + queue.push(stackChild); } - } + }); + + stackValues.sort(function(a, b) { + return a - b; + }); + + stackValues.forEach(function(zValue) { + var index; + + subStacks.some(function(stack, i){ + index = i; + return (stack.zindex === zValue); + }); + sortZ(subStacks.splice(index, 1)[0]); + + }); + }; + + sortZ(parseQueue.zIndex); + + return queue; + } + + function getRenderer(rendererName) { + var renderer; + + if (typeof options.renderer === "string" && _html2canvas.Renderer[rendererName] !== undefined) { + renderer = _html2canvas.Renderer[rendererName](options); + } else if (typeof rendererName === "function") { + renderer = rendererName(options); + } else { + throw new Error("Unknown renderer"); } + if ( typeof renderer._create !== "function" ) { + throw new Error("Invalid renderer defined"); + } + return renderer; } - sortZ(parseQueue.zIndex); - - if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { - renderer = _html2canvas.Renderer[options.renderer](options); - } else if (typeof options.renderer === "function") { - renderer = options.renderer(options); - } else { - throw new Error("Unknown renderer"); - } - - if ( typeof renderer._create !== "function" ) { - throw new Error("Invalid renderer defined"); - } - - return renderer._create( parseQueue, options, document, queue, _html2canvas ); - + return getRenderer(options.renderer)._create(parseQueue, options, document, createRenderQueue(parseQueue), _html2canvas); }; From 516edbceeae977646f0b8aa1f6497e5a0a169dcd Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 23:05:49 +0200 Subject: [PATCH 17/31] Improve background-repeat accuracy for non int positions --- src/Parse.js | 34 ++++++++++------------- tests/assets/jquery.plugin.html2canvas.js | 2 +- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 2b273a2..0ed2b96 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -655,32 +655,26 @@ _html2canvas.Parse = function (images, options) { } function renderImage (ctx) { - ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments,1)); + var args = Array.prototype.slice.call(arguments,1); + ctx.drawImage.apply(ctx, args); numDraws+=1; } function renderBackgroundRepeat (ctx, image, x, y, width, height, elx, ely){ - var sourceX = 0, - sourceY=0; - if (elx-x>0){ - sourceX = elx-x; - } - - if (ely-y>0){ - sourceY = ely-y; - } + var sourceX = (elx - x > 0) ? elx-x :0, + sourceY= (ely - y > 0) ? ely-y : 0; renderImage( ctx, image, - sourceX, // source X - sourceY, // source Y - width-sourceX, // source Width - height-sourceY, // source Height - x+sourceX, // destination X - y+sourceY, // destination Y - width-sourceX, // destination width - height-sourceY // destination height + Math.floor(sourceX), // source X + Math.floor(sourceY), // source Y + Math.ceil(width-sourceX), // source Width + Math.ceil(height-sourceY), // source Height + Math.ceil(x+sourceX), // destination X + Math.ceil(y+sourceY), // destination Y + Math.ceil(width-sourceX), // destination width + Math.ceil(height-sourceY) // destination height ); } @@ -695,7 +689,7 @@ _html2canvas.Parse = function (images, options) { for(bgy=(y + bgp.top);bgy < h + y;){ height = (Math.floor(bgy + image.height) > h + y) ? (h+y) - bgy : image.height; renderBackgroundRepeat(ctx, image, x+bgp.left, bgy,width, height, x, y); - bgy = Math.floor(bgy + image.height); + bgy = Math.round(bgy + image.height); } } @@ -709,7 +703,7 @@ _html2canvas.Parse = function (images, options) { for (bgx=(x + bgp.left); bgx < w + x;) { width = (Math.floor(bgx + image.width) > w + x) ? (w + x) - bgx : image.width; renderBackgroundRepeat(ctx, image, bgx,(y + bgp.top), width, height, x, y); - bgx = Math.floor(bgx + image.width); + bgx = Math.round(bgx + image.width); } } diff --git a/tests/assets/jquery.plugin.html2canvas.js b/tests/assets/jquery.plugin.html2canvas.js index fd446ba..eb764e8 100644 --- a/tests/assets/jquery.plugin.html2canvas.js +++ b/tests/assets/jquery.plugin.html2canvas.js @@ -3,7 +3,7 @@ */ (function( $ ){ $.fn.html2canvas = function(options) { - if (options && options.profile && window.console && window.console.profile) { + if (options && options.profile && window.console && window.console.profile && window.location.search !== "?selenium2") { console.profile(); } var date = new Date(), From aa5b3d41c4729104565b5fb9b25358ba5efe39d0 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 29 Dec 2012 23:35:52 +0200 Subject: [PATCH 18/31] refactoring --- src/Parse.js | 71 +++++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 0ed2b96..0835d83 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -275,7 +275,7 @@ _html2canvas.Parse = function (images, options) { text_align = text_align.replace(["-webkit-auto"],["auto"]); renderList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(text_align) && noLetterSpacing(letter_spacing)) ? - textNode.nodeValue.split(/(\b| )/) + textNode.nodeValue.split(/(\b| )/) : textNode.nodeValue.split(""); metrics = setTextVariables(ctx, el, text_decoration, color); @@ -608,35 +608,30 @@ _html2canvas.Parse = function (images, options) { } return borders; - } function renderFormValue (el, bounds, stack){ var valueWrap = doc.createElement('valuewrap'), - cssArr = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], - i, + cssPropertyArray = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], textValue, - textNode, - arrLen, - style; - - for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){ - style = cssArr[i]; + textNode; + cssPropertyArray.forEach(function(property) { try { - valueWrap.style[style] = getCSS(el, style); - } catch( e ) { + valueWrap.style[property] = getCSS(el, property); + } catch(e) { // Older IE has issues with "border" h2clog("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); } - } + }); valueWrap.style.borderColor = "black"; valueWrap.style.borderStyle = "solid"; valueWrap.style.display = "block"; valueWrap.style.position = "absolute"; + if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){ valueWrap.style.lineHeight = getCSS(el, "height"); } @@ -655,8 +650,7 @@ _html2canvas.Parse = function (images, options) { } function renderImage (ctx) { - var args = Array.prototype.slice.call(arguments,1); - ctx.drawImage.apply(ctx, args); + ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments, 1)); numDraws+=1; } @@ -679,36 +673,33 @@ _html2canvas.Parse = function (images, options) { } - function renderBackgroundRepeatY (ctx, image, bgp, x, y, w, h){ + function renderBackgroundRepeatY (ctx, image, backgroundPosition, x, y, w, h){ var height, width = Math.min(image.width, w), bgy; - bgp.top -= Math.ceil(bgp.top / image.height) * image.height; + backgroundPosition.top -= Math.ceil(backgroundPosition.top / image.height) * image.height; - for(bgy=(y + bgp.top);bgy < h + y;){ + for (bgy = y + backgroundPosition.top; bgy < h + y; bgy = Math.round(bgy + image.height)){ height = (Math.floor(bgy + image.height) > h + y) ? (h+y) - bgy : image.height; - renderBackgroundRepeat(ctx, image, x+bgp.left, bgy,width, height, x, y); - bgy = Math.round(bgy + image.height); + renderBackgroundRepeat(ctx, image, x + backgroundPosition.left, bgy,width, height, x, y); } } - function renderBackgroundRepeatX(ctx, image, bgp, x, y, w, h){ + function renderBackgroundRepeatX(ctx, image, backgroundPosition, x, y, w, h){ var height = Math.min(image.height, h), width, bgx; - bgp.left -= Math.ceil(bgp.left / image.width) * image.width; + backgroundPosition.left -= Math.ceil(backgroundPosition.left / image.width) * image.width; - for (bgx=(x + bgp.left); bgx < w + x;) { + for (bgx = x + backgroundPosition.left; bgx < w + x; bgx = Math.round(bgx + image.width)) { width = (Math.floor(bgx + image.width) > w + x) ? (w + x) - bgx : image.width; - renderBackgroundRepeat(ctx, image, bgx,(y + bgp.top), width, height, x, y); - bgx = Math.round(bgx + image.width); + renderBackgroundRepeat(ctx, image, bgx,(y + backgroundPosition.top), width, height, x, y); } } function renderBackground(el,bounds,ctx){ - // TODO add support for multi background-images var background_image = getCSS(el, "backgroundImage"), background_repeat = getCSS(el, "backgroundRepeat").split(",")[0], @@ -822,7 +813,6 @@ _html2canvas.Parse = function (images, options) { function renderElement(el, parentStack){ - var bounds = _html2canvas.Util.Bounds(el), x = bounds.left, y = bounds.top, @@ -855,8 +845,6 @@ _html2canvas.Parse = function (images, options) { zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex ); - - stack = { ctx: h2cRenderContext( docDim.width || w , docDim.height || h ), zIndex: zindex, @@ -864,8 +852,6 @@ _html2canvas.Parse = function (images, options) { cssPosition: cssPosition }; - - // TODO correct overflow for absolute content residing under a static position if (parentStack.clip){ @@ -886,13 +872,10 @@ _html2canvas.Parse = function (images, options) { borders = renderBorders(el, ctx, bounds, false); stack.borders = borders; - - if (ignoreElementsRegExp.test(el.nodeName) && options.iframeDefault !== "transparent"){ bgcolor = (options.iframeDefault === "default") ? "#efefef" : options.iframeDefault; } - bgbounds = { left: x + borders[3].width, top: y + borders[0].width, @@ -900,12 +883,10 @@ _html2canvas.Parse = function (images, options) { height: h - (borders[0].width + borders[2].width) }; - if (stack.clip){ bgbounds = clipBounds(bgbounds, stack.clip); } - if (bgbounds.height > 0 && bgbounds.width > 0){ renderRect( ctx, @@ -991,30 +972,24 @@ _html2canvas.Parse = function (images, options) { return zindex.children[stackLength - 1]; } - + function isElementVisible(element) { + return (getCSS(element, 'display') !== "none" && getCSS(element, 'visibility') !== "hidden" && !element.hasAttribute("data-html2canvas-ignore")); + } function parseElement (el, stack) { - // skip hidden elements and their children - if (getCSS(el, 'display') !== "none" && getCSS(el, 'visibility') !== "hidden" && !el.hasAttribute("data-html2canvas-ignore")) { + if (isElementVisible(el)) { stack = renderElement(el, stack) || stack; ctx = stack.ctx; if (!ignoreElementsRegExp.test(el.nodeName)) { - var elementChildren = _html2canvas.Util.Children(el), - i, - node, - childrenLen; - - for (i = 0, childrenLen = elementChildren.length; i < childrenLen; i+=1) { - node = elementChildren[i]; + _html2canvas.Util.Children(el).forEach(function(node) { if (node.nodeType === 1) { parseElement(node, stack); } else if (node.nodeType === 3) { renderText(el, node, stack); } - } - + }); } } } From 0c2572b5ceddfb42edf250f3d8fd1f0929f16941 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 00:27:38 +0200 Subject: [PATCH 19/31] refactoring --- src/Parse.js | 115 ++++++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 0835d83..8b2c3cb 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -45,27 +45,25 @@ _html2canvas.Parse = function (images, options) { rangeHeight, stack, ctx, - docDim, i, children, childrenLen; - function docSize(){ - - return { - width: Math.max( - Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), - Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), - Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) - ), - height: Math.max( - Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), - Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), - Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) - ) - }; + function documentWidth () { + return Math.max( + Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), + Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), + Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) + ); + } + function documentHeight () { + return Math.max( + Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), + Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), + Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) + ); } images = images || {}; @@ -810,7 +808,33 @@ _html2canvas.Parse = function (images, options) { } } + function createStack(element, parentStack, bounds) { + var stack = { + ctx: h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height), + zIndex: setZ(getCSS(element, "zIndex"), (parentStack) ? parentStack.zIndex : null), + opacity: getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1), + cssPosition: getCSS(element, "position"), + clip: (parentStack && parentStack.clip) ? _html2canvas.Util.Extend( {}, parentStack.clip ) : null + }; + + return stack; + } + + function getBackgroundBounds(borders, bounds, clip) { + var backgroundBounds = { + left: bounds.left + borders[3].width, + top: bounds.top + borders[0].width, + width: bounds.width - (borders[1].width + borders[3].width), + height: bounds.height - (borders[0].width + borders[2].width) + }; + + if (clip) { + backgroundBounds = clipBounds(backgroundBounds, clip); + } + + return backgroundBounds; + } function renderElement(el, parentStack){ var bounds = _html2canvas.Util.Bounds(el), @@ -820,49 +844,28 @@ _html2canvas.Parse = function (images, options) { h = bounds.height, image, bgcolor = getCSS(el, "backgroundColor"), - cssPosition = getCSS(el, "position"), zindex, - opacity = getCSS(el, "opacity"), stack, stackLength, borders, ctx, - bgbounds, + backgroundBounds, imgSrc, paddingLeft, paddingTop, paddingRight, paddingBottom; - if (!parentStack){ - docDim = docSize(); - parentStack = { - opacity: 1 - }; - }else{ - docDim = {}; - } - - zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex ); - - stack = { - ctx: h2cRenderContext( docDim.width || w , docDim.height || h ), - zIndex: zindex, - opacity: opacity * parentStack.opacity, - cssPosition: cssPosition - }; + stack = createStack(el, parentStack, bounds); + zindex = stack.zIndex; // TODO correct overflow for absolute content residing under a static position - if (parentStack.clip){ - stack.clip = _html2canvas.Util.Extend( {}, parentStack.clip ); - } - if (options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(el, "overflow")) === true && /(BODY)/i.test(el.nodeName) === false){ stack.clip = (stack.clip) ? clipBounds(stack.clip, bounds) : bounds; } - stackLength = zindex.children.push(stack); + stackLength = zindex.children.push(stack); ctx = zindex.children[stackLength-1].ctx; @@ -876,28 +879,19 @@ _html2canvas.Parse = function (images, options) { bgcolor = (options.iframeDefault === "default") ? "#efefef" : options.iframeDefault; } - bgbounds = { - left: x + borders[3].width, - top: y + borders[0].width, - width: w - (borders[1].width + borders[3].width), - height: h - (borders[0].width + borders[2].width) - }; + backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip); - if (stack.clip){ - bgbounds = clipBounds(bgbounds, stack.clip); - } - - if (bgbounds.height > 0 && bgbounds.width > 0){ + if (backgroundBounds.height > 0 && backgroundBounds.width > 0){ renderRect( ctx, - bgbounds.left, - bgbounds.top, - bgbounds.width, - bgbounds.height, + backgroundBounds.left, + backgroundBounds.top, + backgroundBounds.width, + backgroundBounds.height, bgcolor ); - renderBackground(el, bgbounds, ctx); + renderBackground(el, backgroundBounds, ctx); } switch(el.nodeName){ @@ -947,7 +941,7 @@ _html2canvas.Parse = function (images, options) { } break; case "LI": - renderListItem(el, stack, bgbounds); + renderListItem(el, stack, backgroundBounds); break; case "CANVAS": paddingLeft = getCSSInt(el, 'paddingLeft'); @@ -1003,7 +997,8 @@ _html2canvas.Parse = function (images, options) { if ( support.svgRendering ) { (function( body ){ var img = new Image(), - size = docSize(), + docWidth = documentWidth(), + docHeight = documentHeight(), html = ""; function parseDOM( el ) { @@ -1051,8 +1046,8 @@ _html2canvas.Parse = function (images, options) { parseDOM(body); img.src = [ "data:image/svg+xml,", - "", - "", + "", + "", "", html.replace(/\#/g,"%23"), "", From 5faa45847e67d284e08c4a78a70df4a339b4d59f Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 01:06:11 +0200 Subject: [PATCH 20/31] refactored image rendering --- src/Parse.js | 71 +++++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 8b2c3cb..70b03e6 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -470,6 +470,27 @@ _html2canvas.Parse = function (images, options) { return parentZ; } + function renderImage(ctx, element, image, bounds, borders) { + + var paddingLeft = getCSSInt(element, 'paddingLeft'), + paddingTop = getCSSInt(element, 'paddingTop'), + paddingRight = getCSSInt(element, 'paddingRight'), + paddingBottom = getCSSInt(element, 'paddingBottom'); + + drawImage( + ctx, + image, + 0, //sx + 0, //sy + image.width, //sw + image.height, //sh + bounds.left + paddingLeft + borders[3].width, //dx + bounds.top + paddingTop + borders[0].width, // dy + bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw + bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh + ); + } + function renderBorders(el, ctx, bounds, clip){ var x = bounds.left, y = bounds.top, @@ -647,7 +668,7 @@ _html2canvas.Parse = function (images, options) { body.removeChild(valueWrap); } - function renderImage (ctx) { + function drawImage (ctx) { ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments, 1)); numDraws+=1; } @@ -656,7 +677,7 @@ _html2canvas.Parse = function (images, options) { var sourceX = (elx - x > 0) ? elx-x :0, sourceY= (ely - y > 0) ? ely-y : 0; - renderImage( + drawImage( ctx, image, Math.floor(sourceX), // source X @@ -764,7 +785,7 @@ _html2canvas.Parse = function (images, options) { } if (bgh>0 && bgw > 0){ - renderImage( + drawImage( ctx, image, bgsx, // source X : 0 @@ -897,29 +918,10 @@ _html2canvas.Parse = function (images, options) { switch(el.nodeName){ case "IMG": imgSrc = el.getAttribute('src'); - image = loadImage(imgSrc); - if (image){ - - paddingLeft = getCSSInt(el, 'paddingLeft'); - paddingTop = getCSSInt(el, 'paddingTop'); - paddingRight = getCSSInt(el, 'paddingRight'); - paddingBottom = getCSSInt(el, 'paddingBottom'); - - - renderImage( - ctx, - image, - 0, //sx - 0, //sy - image.width, //sw - image.height, //sh - x + paddingLeft + borders[3].width, //dx - y + paddingTop + borders[0].width, // dy - bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw - bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh - ); - - }else{ + image = loadImage(el.getAttribute('src')); + if (image) { + renderImage(ctx, el, image, bounds, borders); + } else { h2clog("html2canvas: Error loading :" + imgSrc); } break; @@ -944,22 +946,7 @@ _html2canvas.Parse = function (images, options) { renderListItem(el, stack, backgroundBounds); break; case "CANVAS": - paddingLeft = getCSSInt(el, 'paddingLeft'); - paddingTop = getCSSInt(el, 'paddingTop'); - paddingRight = getCSSInt(el, 'paddingRight'); - paddingBottom = getCSSInt(el, 'paddingBottom'); - renderImage( - ctx, - el, - 0, - 0, - el.width, - el.height, - x + paddingLeft + borders[3].width, - y + paddingTop + borders[0].width, - bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), - bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) - ); + renderImage(ctx, el, el, bounds, borders); break; } From bb1cd21367edc05b85b0c49158e1314b537b527a Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 01:11:05 +0200 Subject: [PATCH 21/31] refactored background color rendering --- src/Parse.js | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 70b03e6..a2a90c4 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -718,6 +718,17 @@ _html2canvas.Parse = function (images, options) { } } + function renderBackgroundColor(ctx, backgroundBounds, bgcolor) { + renderRect( + ctx, + backgroundBounds.left, + backgroundBounds.top, + backgroundBounds.width, + backgroundBounds.height, + bgcolor + ); + } + function renderBackground(el,bounds,ctx){ // TODO add support for multi background-images var background_image = getCSS(el, "backgroundImage"), @@ -859,10 +870,6 @@ _html2canvas.Parse = function (images, options) { function renderElement(el, parentStack){ var bounds = _html2canvas.Util.Bounds(el), - x = bounds.left, - y = bounds.top, - w = bounds.width, - h = bounds.height, image, bgcolor = getCSS(el, "backgroundColor"), zindex, @@ -870,12 +877,7 @@ _html2canvas.Parse = function (images, options) { stackLength, borders, ctx, - backgroundBounds, - imgSrc, - paddingLeft, - paddingTop, - paddingRight, - paddingBottom; + backgroundBounds; stack = createStack(el, parentStack, bounds); zindex = stack.zIndex; @@ -903,26 +905,16 @@ _html2canvas.Parse = function (images, options) { backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip); if (backgroundBounds.height > 0 && backgroundBounds.width > 0){ - renderRect( - ctx, - backgroundBounds.left, - backgroundBounds.top, - backgroundBounds.width, - backgroundBounds.height, - bgcolor - ); - + renderBackgroundColor(ctx, backgroundBounds, bgcolor); renderBackground(el, backgroundBounds, ctx); } switch(el.nodeName){ case "IMG": - imgSrc = el.getAttribute('src'); - image = loadImage(el.getAttribute('src')); - if (image) { + if ((image = loadImage(el.getAttribute('src')))) { renderImage(ctx, el, image, bounds, borders); } else { - h2clog("html2canvas: Error loading :" + imgSrc); + h2clog("html2canvas: Error loading :" + el.getAttribute('src')); } break; case "INPUT": From cb43e0989910561eebb34af48af1fe1052490176 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 01:39:37 +0200 Subject: [PATCH 22/31] refactor parsing --- src/Parse.js | 122 +++++++++++++++++++++++---------------------------- src/Util.js | 1 - 2 files changed, 54 insertions(+), 69 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index a2a90c4..4ee76cd 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -49,23 +49,6 @@ _html2canvas.Parse = function (images, options) { children, childrenLen; - - function documentWidth () { - return Math.max( - Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), - Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), - Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) - ); - } - - function documentHeight () { - return Math.max( - Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), - Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), - Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) - ); - } - images = images || {}; // Test whether we can use ranges to measure bounding boxes @@ -95,6 +78,22 @@ _html2canvas.Parse = function (images, options) { var getCSS = _html2canvas.Util.getCSS; + function documentWidth () { + return Math.max( + Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), + Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), + Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) + ); + } + + function documentHeight () { + return Math.max( + Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), + Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), + Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) + ); + } + function getCSSInt(element, attribute) { var val = parseInt(getCSS(element, attribute), 10); return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html @@ -840,16 +839,31 @@ _html2canvas.Parse = function (images, options) { } } + function setOpacity(ctx, element, parentStack) { + var opacity = getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1); + ctx.setVariable("globalAlpha", opacity); + return opacity; + } + function createStack(element, parentStack, bounds) { - var stack = { - ctx: h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height), + var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height), + stack = { + ctx: ctx, zIndex: setZ(getCSS(element, "zIndex"), (parentStack) ? parentStack.zIndex : null), - opacity: getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1), + opacity: setOpacity(ctx, element, parentStack), cssPosition: getCSS(element, "position"), + borders: renderBorders(element, ctx, bounds, false), clip: (parentStack && parentStack.clip) ? _html2canvas.Util.Extend( {}, parentStack.clip ) : null }; + // TODO correct overflow for absolute content residing under a static position + if (options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(element, "overflow")) === true && /(BODY)/i.test(element.nodeName) === false){ + stack.clip = (stack.clip) ? clipBounds(stack.clip, bounds) : bounds; + } + + stack.zIndex.children.push(stack); + return stack; } @@ -868,81 +882,54 @@ _html2canvas.Parse = function (images, options) { return backgroundBounds; } - function renderElement(el, parentStack){ - var bounds = _html2canvas.Util.Bounds(el), + function renderElement(element, parentStack){ + var bounds = _html2canvas.Util.Bounds(element), image, - bgcolor = getCSS(el, "backgroundColor"), - zindex, - stack, - stackLength, - borders, - ctx, - backgroundBounds; - - stack = createStack(el, parentStack, bounds); - zindex = stack.zIndex; - - // TODO correct overflow for absolute content residing under a static position - - if (options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(el, "overflow")) === true && /(BODY)/i.test(el.nodeName) === false){ - stack.clip = (stack.clip) ? clipBounds(stack.clip, bounds) : bounds; - } - - stackLength = zindex.children.push(stack); - - ctx = zindex.children[stackLength-1].ctx; - - ctx.setVariable("globalAlpha", stack.opacity); - - - borders = renderBorders(el, ctx, bounds, false); - stack.borders = borders; - - if (ignoreElementsRegExp.test(el.nodeName) && options.iframeDefault !== "transparent"){ - bgcolor = (options.iframeDefault === "default") ? "#efefef" : options.iframeDefault; - } - + bgcolor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"), + stack = createStack(element, parentStack, bounds), + borders = stack.borders, + ctx = stack.ctx, backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip); if (backgroundBounds.height > 0 && backgroundBounds.width > 0){ renderBackgroundColor(ctx, backgroundBounds, bgcolor); - renderBackground(el, backgroundBounds, ctx); + renderBackground(element, backgroundBounds, ctx); } - switch(el.nodeName){ + switch(element.nodeName){ case "IMG": - if ((image = loadImage(el.getAttribute('src')))) { - renderImage(ctx, el, image, bounds, borders); + if ((image = loadImage(element.getAttribute('src')))) { + renderImage(ctx, element, image, bounds, borders); } else { - h2clog("html2canvas: Error loading :" + el.getAttribute('src')); + h2clog("html2canvas: Error loading :" + element.getAttribute('src')); } break; case "INPUT": // TODO add all relevant type's, i.e. HTML5 new stuff // todo add support for placeholder attribute for browsers which support it - if (/^(text|url|email|submit|button|reset)$/.test(el.type) && el.value.length > 0){ - renderFormValue(el, bounds, stack); + if (/^(text|url|email|submit|button|reset)$/.test(element.type) && element.value.length > 0){ + renderFormValue(element, bounds, stack); } break; case "TEXTAREA": - if (el.value.length > 0){ - renderFormValue(el, bounds, stack); + if (element.value.length > 0){ + renderFormValue(element, bounds, stack); } break; case "SELECT": - if (el.options.length > 0){ - renderFormValue(el, bounds, stack); + if (element.options.length > 0){ + renderFormValue(element, bounds, stack); } break; case "LI": - renderListItem(el, stack, backgroundBounds); + renderListItem(element, stack, backgroundBounds); break; case "CANVAS": - renderImage(ctx, el, el, bounds, borders); + renderImage(ctx, element, element, bounds, borders); break; } - return zindex.children[stackLength - 1]; + return stack; } function isElementVisible(element) { @@ -1051,7 +1038,6 @@ _html2canvas.Parse = function (images, options) { stack.backgroundColor = getCSS(document.documentElement, "backgroundColor"); return stack; - }; function h2czContext(zindex) { diff --git a/src/Util.js b/src/Util.js index 939936d..f8d59ec 100644 --- a/src/Util.js +++ b/src/Util.js @@ -14,7 +14,6 @@ window.html2canvas = function(elements, opts) { // parse options svgRendering: false, // use svg powered rendering where available (FF11+) - iframeDefault: "default", ignoreElements: "IFRAME|OBJECT|PARAM", useOverflow: true, letterRendering: false, From 45853a083c4f8a775e665b133f9c04739536edea Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 04:15:51 +0200 Subject: [PATCH 23/31] refactored text rendering --- src/Parse.js | 135 +++++++++++++++---------------------- tests/cases/text/text.html | 2 +- 2 files changed, 54 insertions(+), 83 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 4ee76cd..a8cbc84 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -243,101 +243,72 @@ _html2canvas.Parse = function (images, options) { } } + function getTextBounds(state, text, textDecoration, isLast) { + var bounds; + if (support.rangeBounds) { + if (textDecoration !== "none" || trimText(text).length !== 0) { + bounds = textRangeBounds(text, state.node, state.textOffset); + } + state.textOffset += text.length; + } else if (state.node && typeof state.node.nodeValue === "string" ){ + var newTextNode = (isLast) ? state.node.splitText(text.length) : null; + bounds = textWrapperBounds(state.node); + state.node = newTextNode; + } + return bounds; + } + + function textRangeBounds(text, textNode, textOffset) { + var range = doc.createRange(); + range.setStart(textNode, textOffset); + range.setEnd(textNode, textOffset + text.length); + return range.getBoundingClientRect(); + } + + function textWrapperBounds(oldTextNode) { + var parent = oldTextNode.parentNode, + wrapElement = doc.createElement('wrapper'), + backupText = oldTextNode.cloneNode(true); + + wrapElement.appendChild(oldTextNode.cloneNode(true)); + parent.replaceChild(wrapElement, oldTextNode); + + var bounds = _html2canvas.Util.Bounds(wrapElement); + parent.replaceChild(backupText, wrapElement); + return bounds; + } + function renderText(el, textNode, stack) { var ctx = stack.ctx, color = getCSS(el, "color"), - text_decoration = getCSS(el, "textDecoration"), - text_align = getCSS(el, "textAlign"), - letter_spacing = getCSS(el, "letterSpacing"), - bounds, - text, + textDecoration = getCSS(el, "textDecoration"), + textAlign = getCSS(el, "textAlign"), metrics, - renderList, - listLen, - newTextNode, - textValue, - textOffset = 0, - oldTextNode, - c, - range, - parent, - wrapElement, - backupText; + textList, + state = { + node: textNode, + textOffset: 0 + }; - textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); - text = trimText(textNode.nodeValue); + if (trimText(textNode.nodeValue).length > 0) { - if (text.length > 0){ + textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); + textAlign = textAlign.replace(["-webkit-auto"],["auto"]); - text_align = text_align.replace(["-webkit-auto"],["auto"]); - - renderList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(text_align) && noLetterSpacing(letter_spacing)) ? + textList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(textAlign) && noLetterSpacing(getCSS(el, "letterSpacing"))) ? textNode.nodeValue.split(/(\b| )/) : textNode.nodeValue.split(""); - metrics = setTextVariables(ctx, el, text_decoration, color); - oldTextNode = textNode; + metrics = setTextVariables(ctx, el, textDecoration, color); - for ( c=0, listLen = renderList.length; c < listLen; c+=1 ) { - textValue = null; - - if (support.rangeBounds){ - // getBoundingClientRect is supported for ranges - if (text_decoration !== "none" || trimText(renderList[c]).length !== 0) { - textValue = renderList[c]; - if (doc.createRange){ - range = doc.createRange(); - - range.setStart(textNode, textOffset); - range.setEnd(textNode, textOffset + textValue.length); - } else { - // TODO add IE support - range = body.createTextRange(); - } - - if (range.getBoundingClientRect()) { - bounds = range.getBoundingClientRect(); - } else { - bounds = {}; - } - - } - } else { - // it isn't supported, so let's wrap it inside an element instead and get the bounds there - - // IE 9 bug - if (!oldTextNode || typeof oldTextNode.nodeValue !== "string" ){ - continue; - } - - newTextNode = (i < listLen-1) ? oldTextNode.splitText(renderList[c].length) : null; - - parent = oldTextNode.parentNode; - wrapElement = doc.createElement('wrapper'); - backupText = oldTextNode.cloneNode(true); - - wrapElement.appendChild(oldTextNode.cloneNode(true)); - parent.replaceChild(wrapElement, oldTextNode); - - bounds = _html2canvas.Util.Bounds(wrapElement); - - textValue = oldTextNode.nodeValue; - - oldTextNode = newTextNode; - parent.replaceChild(backupText, wrapElement); + textList.forEach(function(text, index) { + var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1)); + if (bounds) { + drawText(text, bounds.left, bounds.bottom, ctx); + renderTextDecoration(textDecoration, bounds, metrics, color); } - - if (textValue !== null) { - drawText(textValue, bounds.left, bounds.bottom, ctx); - } - renderTextDecoration(text_decoration, bounds, metrics, color); - - textOffset += renderList[c].length; - - } - + }); } - } function listPosition (element, val) { diff --git a/tests/cases/text/text.html b/tests/cases/text/text.html index 4fcc083..2cacb95 100644 --- a/tests/cases/text/text.html +++ b/tests/cases/text/text.html @@ -97,7 +97,7 @@

<h2> text-transform

  • text-transform:none;
  • -
  • text-transform:capitalize; (including foreign characters such as Öaäå)
  • +
  • text-transform: capitalize; (including foreign characters such as Öaäå)
  • text-transform:uppercase;
  • text-transform:lowercase;
From 74e93cbb93be945c03b2940df6b8f6f3ea872167 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 15:21:36 +0200 Subject: [PATCH 24/31] refactoring backgroundimage rendering --- src/Parse.js | 179 +++++++++++++++++++++++++-------------------------- 1 file changed, 88 insertions(+), 91 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index a8cbc84..72eca12 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -291,7 +291,6 @@ _html2canvas.Parse = function (images, options) { }; if (trimText(textNode.nodeValue).length > 0) { - textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); textAlign = textAlign.replace(["-webkit-auto"],["auto"]); @@ -336,7 +335,7 @@ _html2canvas.Parse = function (images, options) { count = 1, childs = el.parentNode.childNodes; - if ( el.parentNode ) { + if (el.parentNode) { while( childs[ ++i ] !== el ) { if ( childs[ i ].nodeType === 1 ) { count++; @@ -346,7 +345,6 @@ _html2canvas.Parse = function (images, options) { } else { return -1; } - } function listItemText(element, type) { @@ -643,7 +641,7 @@ _html2canvas.Parse = function (images, options) { numDraws+=1; } - function renderBackgroundRepeat (ctx, image, x, y, width, height, elx, ely){ + function renderBackgroundSlice (ctx, image, x, y, width, height, elx, ely){ var sourceX = (elx - x > 0) ? elx-x :0, sourceY= (ely - y > 0) ? ely-y : 0; @@ -661,6 +659,75 @@ _html2canvas.Parse = function (images, options) { ); } + function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) { + var bgy, + height, + add, + h; + + backgroundPosition.top -= Math.ceil(backgroundPosition.top / image.height) * image.height; + + for(bgy = (bounds.top + backgroundPosition.top); bgy < (bounds.height + bounds.top); bgy = Math.floor(bgy+image.height) - add) { + h = Math.min(image.height,(bounds.height + bounds.top) - bgy); + + height = (Math.floor(bgy + image.height) > h + bgy) ? (h + bgy) - bgy : image.height; + + if (bgy < bounds.top){ + add = bounds.top - bgy; + bgy = bounds.top; + } else { + add = 0; + } + + renderBackgroundRepeatX(ctx, image, backgroundPosition, bounds.left, bgy, bounds.width, height); + + if (add > 0){ + backgroundPosition.top += add; + } + } + } + + function renderBackgroundNoRepeat(ctx, image, backgroundPosition, bounds) { + var bgw = bounds.width - backgroundPosition.left, + bgh = bounds.height - backgroundPosition.top, + bgsx = backgroundPosition.left, + bgsy = backgroundPosition.top, + bgdx = backgroundPosition.left + bounds.left, + bgdy = backgroundPosition.top + bounds.top; + + if (bgsx<0){ + bgsx = Math.abs(bgsx); + bgdx += bgsx; + bgw = Math.min(bounds.width,image.width-bgsx); + } else { + bgw = Math.min(bgw,image.width); + bgsx = 0; + } + + if (bgsy < 0){ + bgsy = Math.abs(bgsy); + bgdy += bgsy; + bgh = Math.min(bounds.height, image.height - bgsy); + }else{ + bgh = Math.min(bgh, image.height); + bgsy = 0; + } + + if (bgh>0 && bgw > 0){ + drawImage( + ctx, + image, + bgsx, // source X : 0 + bgsy, // source Y : 1695 + bgw, // source Width : 18 + bgh, // source Height : 1677 + bgdx, // destination X :906 + bgdy, // destination Y : 1020 + bgw, // destination width : 18 + bgh // destination height : 1677 + ); + } + } function renderBackgroundRepeatY (ctx, image, backgroundPosition, x, y, w, h){ var height, @@ -671,7 +738,7 @@ _html2canvas.Parse = function (images, options) { for (bgy = y + backgroundPosition.top; bgy < h + y; bgy = Math.round(bgy + image.height)){ height = (Math.floor(bgy + image.height) > h + y) ? (h+y) - bgy : image.height; - renderBackgroundRepeat(ctx, image, x + backgroundPosition.left, bgy,width, height, x, y); + renderBackgroundSlice(ctx, image, x + backgroundPosition.left, bgy,width, height, x, y); } } @@ -684,7 +751,7 @@ _html2canvas.Parse = function (images, options) { for (bgx = x + backgroundPosition.left; bgx < w + x; bgx = Math.round(bgx + image.width)) { width = (Math.floor(bgx + image.width) > w + x) ? (w + x) - bgx : image.width; - renderBackgroundRepeat(ctx, image, bgx,(y + backgroundPosition.top), width, height, x, y); + renderBackgroundSlice(ctx, image, bgx,(y + backgroundPosition.top), width, height, x, y); } } @@ -699,37 +766,24 @@ _html2canvas.Parse = function (images, options) { ); } - function renderBackground(el,bounds,ctx){ + function renderBackground(el, bounds, ctx) { // TODO add support for multi background-images - var background_image = getCSS(el, "backgroundImage"), + var backgroundImage = getCSS(el, "backgroundImage"), background_repeat = getCSS(el, "backgroundRepeat").split(",")[0], image, - bgp, - bgy, - bgw, - bgsx, - bgsy, - bgdx, - bgdy, - bgh, - h, - height, - add; + bgp; - if ( !/data:image\/.*;base64,/i.test(background_image) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(background_image) ) { - background_image = background_image.split(",")[0]; + if (!/data:image\/.*;base64,/i.test(backgroundImage) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(backgroundImage)) { + backgroundImage = backgroundImage.split(",")[0]; } - if ( typeof background_image !== "undefined" && /^(1|none)$/.test(background_image) === false ) { - background_image = _html2canvas.Util.backgroundImage(background_image); - image = loadImage(background_image); - - - bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); + if (typeof backgroundImage !== "undefined" && /^(1|none)$/.test(backgroundImage) === false) { + image = loadImage(_html2canvas.Util.backgroundImage(backgroundImage)); // TODO add support for background-origin - if ( image ){ - switch ( background_repeat ) { + if (image) { + bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); + switch (background_repeat) { case "repeat-x": renderBackgroundRepeatX(ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height); break; @@ -739,72 +793,15 @@ _html2canvas.Parse = function (images, options) { break; case "no-repeat": - bgw = bounds.width - bgp.left; - bgh = bounds.height - bgp.top; - bgsx = bgp.left; - bgsy = bgp.top; - bgdx = bgp.left+bounds.left; - bgdy = bgp.top+bounds.top; - - if (bgsx<0){ - bgsx = Math.abs(bgsx); - bgdx += bgsx; - bgw = Math.min(bounds.width,image.width-bgsx); - }else{ - bgw = Math.min(bgw,image.width); - bgsx = 0; - } - - if (bgsy<0){ - bgsy = Math.abs(bgsy); - bgdy += bgsy; - // bgh = bgh-bgsy; - bgh = Math.min(bounds.height,image.height-bgsy); - }else{ - bgh = Math.min(bgh,image.height); - bgsy = 0; - } - - if (bgh>0 && bgw > 0){ - drawImage( - ctx, - image, - bgsx, // source X : 0 - bgsy, // source Y : 1695 - bgw, // source Width : 18 - bgh, // source Height : 1677 - bgdx, // destination X :906 - bgdy, // destination Y : 1020 - bgw, // destination width : 18 - bgh // destination height : 1677 - ); - - } + renderBackgroundNoRepeat(ctx, image, bgp, bounds); break; + default: - bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; - - for(bgy=(bounds.top+bgp.top);bgy h + bgy) ? (h+bgy)-bgy : image.height; - if (bgy0){ - bgp.top += add; - } - bgy = Math.floor(bgy+image.height)-add; - } + renderBackgroundRepeat(ctx, image, bgp, bounds); break; - } - }else{ - h2clog("html2canvas: Error loading background:" + background_image); + } else { + h2clog("html2canvas: Error loading background:" + backgroundImage); } } From 1357057cbf9c37f0a48c44e6385e50a100f05a99 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 15:38:17 +0200 Subject: [PATCH 25/31] refactored background rendering --- src/Parse.js | 89 +++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 72eca12..e394cf2 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -687,44 +687,44 @@ _html2canvas.Parse = function (images, options) { } } - function renderBackgroundNoRepeat(ctx, image, backgroundPosition, bounds) { - var bgw = bounds.width - backgroundPosition.left, - bgh = bounds.height - backgroundPosition.top, + function renderBackgroundNoRepeat(ctx, image, backgroundPosition, x, y, w, h) { + var bgdw = w - backgroundPosition.left, + bgdh = h - backgroundPosition.top, bgsx = backgroundPosition.left, bgsy = backgroundPosition.top, - bgdx = backgroundPosition.left + bounds.left, - bgdy = backgroundPosition.top + bounds.top; + bgdx = backgroundPosition.left + x, + bgdy = backgroundPosition.top + y; if (bgsx<0){ bgsx = Math.abs(bgsx); bgdx += bgsx; - bgw = Math.min(bounds.width,image.width-bgsx); + bgdw = Math.min(w,image.width-bgsx); } else { - bgw = Math.min(bgw,image.width); + bgdw = Math.min(bgdw,image.width); bgsx = 0; } if (bgsy < 0){ bgsy = Math.abs(bgsy); bgdy += bgsy; - bgh = Math.min(bounds.height, image.height - bgsy); - }else{ - bgh = Math.min(bgh, image.height); + bgdh = Math.min(h, image.height - bgsy); + } else { + bgdh = Math.min(bgdh, image.height); bgsy = 0; } - if (bgh>0 && bgw > 0){ + if (bgdh > 0 && bgdw > 0){ drawImage( ctx, image, - bgsx, // source X : 0 - bgsy, // source Y : 1695 - bgw, // source Width : 18 - bgh, // source Height : 1677 - bgdx, // destination X :906 - bgdy, // destination Y : 1020 - bgw, // destination width : 18 - bgh // destination height : 1677 + bgsx, + bgsy, + bgdw, + bgdh, + bgdx, + bgdy, + bgdw, + bgdh ); } } @@ -766,12 +766,32 @@ _html2canvas.Parse = function (images, options) { ); } - function renderBackground(el, bounds, ctx) { + function renderBackgroundRepeating(el, bounds, ctx, image) { + var backgroundPosition = _html2canvas.Util.BackgroundPosition(el, bounds, image), + backgroundRepeat = getCSS(el, "backgroundRepeat").split(",")[0]; + switch (backgroundRepeat) { + case "repeat-x": + renderBackgroundRepeatX(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height); + break; + + case "repeat-y": + renderBackgroundRepeatY(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height); + break; + + case "no-repeat": + renderBackgroundNoRepeat(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height); + break; + + default: + renderBackgroundRepeat(ctx, image, backgroundPosition, bounds); + break; + } + } + + function renderBackgroundImage(element, bounds, ctx) { // TODO add support for multi background-images - var backgroundImage = getCSS(el, "backgroundImage"), - background_repeat = getCSS(el, "backgroundRepeat").split(",")[0], - image, - bgp; + var backgroundImage = getCSS(element, "backgroundImage"), + image; if (!/data:image\/.*;base64,/i.test(backgroundImage) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(backgroundImage)) { backgroundImage = backgroundImage.split(",")[0]; @@ -782,24 +802,7 @@ _html2canvas.Parse = function (images, options) { // TODO add support for background-origin if (image) { - bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); - switch (background_repeat) { - case "repeat-x": - renderBackgroundRepeatX(ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height); - break; - - case "repeat-y": - renderBackgroundRepeatY(ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height); - break; - - case "no-repeat": - renderBackgroundNoRepeat(ctx, image, bgp, bounds); - break; - - default: - renderBackgroundRepeat(ctx, image, bgp, bounds); - break; - } + renderBackgroundRepeating(element, bounds, ctx, image); } else { h2clog("html2canvas: Error loading background:" + backgroundImage); } @@ -861,7 +864,7 @@ _html2canvas.Parse = function (images, options) { if (backgroundBounds.height > 0 && backgroundBounds.width > 0){ renderBackgroundColor(ctx, backgroundBounds, bgcolor); - renderBackground(element, backgroundBounds, ctx); + renderBackgroundImage(element, backgroundBounds, ctx); } switch(element.nodeName){ From d93e36d768a25713562770863911c4d375ab6193 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 15:48:55 +0200 Subject: [PATCH 26/31] Moved browser support checks to seperate file --- src/Parse.js | 69 +++----------------------------------------------- src/Support.js | 63 +++++++++++++++++++++++++++++++++++++++++++++ tests/test.js | 2 +- 3 files changed, 68 insertions(+), 66 deletions(-) create mode 100644 src/Support.js diff --git a/src/Parse.js b/src/Parse.js index e394cf2..2fc851a 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -1,83 +1,22 @@ _html2canvas.Parse = function (images, options) { window.scroll(0,0); - var support = { - rangeBounds: false, - svgRendering: options.svgRendering && (function( ){ - var img = new Image(), - canvas = document.createElement("canvas"), - ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); - if (ctx === false) { - // browser doesn't support canvas, good luck supporting SVG on canvas - return false; - } - canvas.width = canvas.height = 10; - img.src = [ - "data:image/svg+xml,", - "", - "", - "
", - "sup", - "
", - "
", - "
" - ].join(""); - try { - ctx.drawImage(img, 0, 0); - canvas.toDataURL(); - } catch(e) { - return false; - } - h2clog('html2canvas: Parse: SVG powered rendering available'); - return true; - - })() - }, - element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default + var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default numDraws = 0, fontData = {}, doc = element.ownerDocument, + support = _html2canvas.Util.Support(options, doc), ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), body = doc.body, - r, - testElement, - rangeBounds, - rangeHeight, stack, ctx, i, children, - childrenLen; + childrenLen, + getCSS = _html2canvas.Util.getCSS; images = images || {}; - // Test whether we can use ranges to measure bounding boxes - // Opera doesn't provide valid bounds.height/bottom even though it supports the method. - - - if (doc.createRange) { - r = doc.createRange(); - //this.support.rangeBounds = new Boolean(r.getBoundingClientRect); - if (r.getBoundingClientRect){ - testElement = doc.createElement('boundtest'); - testElement.style.height = "123px"; - testElement.style.display = "block"; - body.appendChild(testElement); - - r.selectNode(testElement); - rangeBounds = r.getBoundingClientRect(); - rangeHeight = rangeBounds.height; - - if (rangeHeight === 123) { - support.rangeBounds = true; - } - body.removeChild(testElement); - } - - } - - var getCSS = _html2canvas.Util.getCSS; - function documentWidth () { return Math.max( Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), diff --git a/src/Support.js b/src/Support.js new file mode 100644 index 0000000..c2db49d --- /dev/null +++ b/src/Support.js @@ -0,0 +1,63 @@ +_html2canvas.Util.Support = function (options, doc) { + + function supportSVGRendering() { + var img = new Image(), + canvas = doc.createElement("canvas"), + ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); + if (ctx === false) { + return false; + } + canvas.width = canvas.height = 10; + img.src = [ + "data:image/svg+xml,", + "", + "", + "
", + "sup", + "
", + "
", + "
" + ].join(""); + try { + ctx.drawImage(img, 0, 0); + canvas.toDataURL(); + } catch(e) { + return false; + } + h2clog('html2canvas: Parse: SVG powered rendering available'); + return true; + } + + // Test whether we can use ranges to measure bounding boxes + // Opera doesn't provide valid bounds.height/bottom even though it supports the method. + + function supportRangeBounds() { + var r, testElement, rangeBounds, rangeHeight, support = false; + + if (doc.createRange) { + r = doc.createRange(); + if (r.getBoundingClientRect) { + testElement = doc.createElement('boundtest'); + testElement.style.height = "123px"; + testElement.style.display = "block"; + doc.body.appendChild(testElement); + + r.selectNode(testElement); + rangeBounds = r.getBoundingClientRect(); + rangeHeight = rangeBounds.height; + + if (rangeHeight === 123) { + support = true; + } + doc.body.removeChild(testElement); + } + } + + return support; + } + + return { + rangeBounds: supportRangeBounds(), + svgRendering: options.svgRendering && supportSVGRendering() + }; +}; \ No newline at end of file diff --git a/tests/test.js b/tests/test.js index 22f649c..c0ce9ca 100644 --- a/tests/test.js +++ b/tests/test.js @@ -11,7 +11,7 @@ var h2cSelector, h2cOptions; document.write(srcStart + '/tests/assets/jquery-1.6.2.js' + scrEnd); document.write(srcStart + '/tests/assets/jquery.plugin.html2canvas.js' + scrEnd); - var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'renderers/Canvas'], i; + var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'Support', 'renderers/Canvas'], i; for (i = 0; i < html2canvas.length; ++i) { document.write(srcStart + '/src/' + html2canvas[i] + '.js' + scrEnd); } From 0dd2c24ab49075be8a230b2f77ffde78120cd7b1 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 16:06:59 +0200 Subject: [PATCH 27/31] refactored parsing init --- src/Parse.js | 143 ++++++++++++++++++++++++--------------------------- 1 file changed, 68 insertions(+), 75 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 2fc851a..b364fe4 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -8,11 +8,6 @@ _html2canvas.Parse = function (images, options) { support = _html2canvas.Util.Support(options, doc), ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), body = doc.body, - stack, - ctx, - i, - children, - childrenLen, getCSS = _html2canvas.Util.getCSS; images = images || {}; @@ -165,7 +160,7 @@ _html2canvas.Parse = function (images, options) { } } - function renderTextDecoration(text_decoration, bounds, metrics, color) { + function renderTextDecoration(ctx, text_decoration, bounds, metrics, color) { switch(text_decoration) { case "underline": // Draws a line at the baseline of the font @@ -243,7 +238,7 @@ _html2canvas.Parse = function (images, options) { var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1)); if (bounds) { drawText(text, bounds.left, bounds.bottom, ctx); - renderTextDecoration(textDecoration, bounds, metrics, color); + renderTextDecoration(ctx, textDecoration, bounds, metrics, color); } }); } @@ -317,6 +312,7 @@ _html2canvas.Parse = function (images, options) { function renderListItem(element, stack, elBounds) { var x, text, + ctx = stack.ctx, type = getCSS(element, "listStyleType"), listBounds; @@ -850,8 +846,6 @@ _html2canvas.Parse = function (images, options) { if (isElementVisible(el)) { stack = renderElement(el, stack) || stack; - ctx = stack.ctx; - if (!ignoreElementsRegExp.test(el.nodeName)) { _html2canvas.Util.Children(el).forEach(function(node) { if (node.nodeType === 1) { @@ -864,90 +858,89 @@ _html2canvas.Parse = function (images, options) { } } - stack = renderElement(element, null); + function svgDOMRender(body, stack) { + var img = new Image(), + docWidth = documentWidth(), + docHeight = documentHeight(), + html = ""; - /* - SVG powered HTML rendering, non-tainted canvas available from FF 11+ onwards - */ + function parseDOM(el) { + var children = _html2canvas.Util.Children( el ), + len = children.length, + attr, + a, + alen, + elm, + i; + for ( i = 0; i < len; i+=1 ) { + elm = children[ i ]; + if ( elm.nodeType === 3 ) { + // Text node + html += elm.nodeValue.replace(//g,">"); + } else if ( elm.nodeType === 1 ) { + // Element + if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) { - if ( support.svgRendering ) { - (function( body ){ - var img = new Image(), - docWidth = documentWidth(), - docHeight = documentHeight(), - html = ""; + html += "<" + elm.nodeName.toLowerCase(); - function parseDOM( el ) { - var children = _html2canvas.Util.Children( el ), - len = children.length, - attr, - a, - alen, - elm, - i; - for ( i = 0; i < len; i+=1 ) { - elm = children[ i ]; - if ( elm.nodeType === 3 ) { - // Text node - html += elm.nodeValue.replace(//g,">"); - } else if ( elm.nodeType === 1 ) { - // Element - if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) { - - html += "<" + elm.nodeName.toLowerCase(); - - // add attributes - if ( elm.hasAttributes() ) { - attr = elm.attributes; - alen = attr.length; - for ( a = 0; a < alen; a+=1 ) { - html += " " + attr[ a ].name + '="' + attr[ a ].value + '"'; - } + // add attributes + if ( elm.hasAttributes() ) { + attr = elm.attributes; + alen = attr.length; + for ( a = 0; a < alen; a+=1 ) { + html += " " + attr[ a ].name + '="' + attr[ a ].value + '"'; } - - - html += '>'; - - parseDOM( elm ); - - - html += ""; } - } + + html += '>'; + + parseDOM( elm ); + + + html += ""; + } } } - parseDOM(body); - img.src = [ - "data:image/svg+xml,", - "", - "", - "", - html.replace(/\#/g,"%23"), - "", - "", - "" - ].join(""); + } - img.onload = function() { - stack.svgRender = img; - }; + parseDOM(body); + img.src = [ + "data:image/svg+xml,", + "", + "", + "", + html.replace(/\#/g,"%23"), + "", + "", + "" + ].join(""); - })(document.documentElement); + img.onload = function() { + stack.svgRender = img; + }; } + function init() { + var stack = renderElement(element, null); - // parse every child element - for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){ - parseElement(children[i], stack); + if (support.svgRendering) { + svgDOMRender(document.documentElement, stack); + } + + Array.prototype.slice.call(element.children, 0).forEach(function(childElement) { + parseElement(childElement, stack); + }); + + stack.backgroundColor = getCSS(document.documentElement, "backgroundColor"); + + return stack; } - stack.backgroundColor = getCSS(document.documentElement, "backgroundColor"); - - return stack; + return init(); }; function h2czContext(zindex) { From 29bd4c8c05594729f9e8c6eb3a987b702c7264ae Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 16:29:01 +0200 Subject: [PATCH 28/31] added result markdown creator --- grunt.js | 4 ++-- tests/results.md | 31 +++++++++++++++++++++++++++++++ tests/selenium.js | 23 +++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 tests/results.md diff --git a/grunt.js b/grunt.js index 75ac5fb..552c691 100644 --- a/grunt.js +++ b/grunt.js @@ -62,8 +62,8 @@ module.exports = function(grunt) { if (arguments.length === 0) { selenium.tests(); - } else if (arg1 === "baseline") { - selenium.baseline(); + } else { + selenium[arg1].apply(null, arguments); } }); diff --git a/tests/results.md b/tests/results.md new file mode 100644 index 0000000..715104e --- /dev/null +++ b/tests/results.md @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
chrome
23.0.1271.97
firefox
12.0
iexplorer
9
background/encoded.html100%100%100%
background/linear-gradient.html82.27%85.64%100%
background/multi.html96.6%96.45%96.89%
background/position.html97.03%96.97%97.03%
background/radial-gradient.html57.9%54.87%94.02%
background/repeat.html100%100%100%
border/dashed.html96.45%98.38%97.7%
border/dotted.html97.41%96.46%95.93%
border/double.html97.96%97.87%97.95%
border/solid.html99.97%99.97%99.98%
forms.html95.96%94.55%95.02%
images/canvas.html99.86%100%100%
images/cross-origin.html97.99%97.58%99.35%
images/empty.html99.86%99.87%99.85%
images/images.html83.72%96.93%55.09%
images/svg.html99.92%96.79%99.93%
list/decimal-leading-zero.html99.63%99.72%35.04%
list/decimal.html99.64%99.73%35.06%
list/lower-alpha.html99.65%99.73%35.05%
list/upper-roman.html99.45%99.61%35.11%
overflow.html96.85%97.49%96.51%
text/linethrough.html97.14%94.12%45.74%
text/text.html95.42%94.41%79.85%
text/underline-lineheight.html97.06%92.35%51.38%
text/underline.html97.65%93.5%45.69%
visibility.html99.19%98.92%99.39%
zindex/z-index1.html97.09%99.38%99.54%
zindex/z-index2.html95.94%98.16%97.81%
zindex/z-index3.html98.98%98.55%98.68%
\ No newline at end of file diff --git a/tests/selenium.js b/tests/selenium.js index 2d7bf39..8361d0a 100644 --- a/tests/selenium.js +++ b/tests/selenium.js @@ -246,4 +246,27 @@ }); }; + exports.markdown = function() { + var data = {}, html = "", + browsers = ["chrome", "firefox", "iexplorer"]; + + browsers.forEach(function(browser) { + data[browser] = JSON.parse(fs.readFileSync("tests/results/" + browser + ".json")); + html += ""; + }); + html += "\n"; + + Object.keys(data[browsers[0]].tests).forEach(function(testFile) { + html += ""; + browsers.forEach(function(browser) { + html += ""; + }); + html += "\n" + }); + + html += "
" + browser + "
" + data[browser].version + "
" + testFile.substring(12) + "" + Math.round(data[browser].tests[testFile] * 100) / 100 + "%
"; + + fs.writeFileSync("tests/results.md", html); + }; + })(); \ No newline at end of file From b19c200c6ccb4109ee0f21a9ae2bc4b527358ddb Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 16:30:28 +0200 Subject: [PATCH 29/31] renamed results to readme --- tests/{results.md => readme.md} | 0 tests/selenium.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/{results.md => readme.md} (100%) diff --git a/tests/results.md b/tests/readme.md similarity index 100% rename from tests/results.md rename to tests/readme.md diff --git a/tests/selenium.js b/tests/selenium.js index 8361d0a..8f7bc88 100644 --- a/tests/selenium.js +++ b/tests/selenium.js @@ -266,7 +266,7 @@ html += ""; - fs.writeFileSync("tests/results.md", html); + fs.writeFileSync("tests/readme.md", html); }; })(); \ No newline at end of file From ba9ace71bae4b7beccb85376ac781dd12eb5ed89 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 16:46:31 +0200 Subject: [PATCH 30/31] improved text-decoration: overline accuracy --- src/Parse.js | 2 +- tests/readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index b364fe4..6ff9d07 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -168,7 +168,7 @@ _html2canvas.Parse = function (images, options) { renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); break; case "overline": - renderRect(ctx, bounds.left, bounds.top, bounds.width, 1, color); + renderRect(ctx, bounds.left, Math.round(bounds.top), bounds.width, 1, color); break; case "line-through": // TODO try and find exact position for line-through diff --git a/tests/readme.md b/tests/readme.md index 715104e..bf29ecd 100644 --- a/tests/readme.md +++ b/tests/readme.md @@ -21,7 +21,7 @@ list/upper-roman.html99.45%99.61%35.11% overflow.html96.85%97.49%96.51% text/linethrough.html97.14%94.12%45.74% -text/text.html95.42%94.41%79.85% +text/text.html95.71%94.67%79.85% text/underline-lineheight.html97.06%92.35%51.38% text/underline.html97.65%93.5%45.69% visibility.html99.19%98.92%99.39% From 496c8488bd9ad9c16e8ea6b0f9e848f2c24d1c9b Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sun, 30 Dec 2012 21:26:25 +0200 Subject: [PATCH 31/31] Moved font metrics to seperate file --- src/Font.js | 64 ++++++++++++++++++++++++++++++++++++++++++++++ src/Parse.js | 70 +++------------------------------------------------ tests/test.js | 2 +- 3 files changed, 68 insertions(+), 68 deletions(-) create mode 100644 src/Font.js diff --git a/src/Font.js b/src/Font.js new file mode 100644 index 0000000..2bc264b --- /dev/null +++ b/src/Font.js @@ -0,0 +1,64 @@ +_html2canvas.Util.Font = (function () { + + var fontData = {}; + + return function(font, fontSize, doc) { + if (fontData[font + "-" + fontSize] !== undefined) { + return fontData[font + "-" + fontSize]; + } + + var container = doc.createElement('div'), + img = doc.createElement('img'), + span = doc.createElement('span'), + sampleText = 'Hidden Text', + baseline, + middle, + metricsObj; + + container.style.visibility = "hidden"; + container.style.fontFamily = font; + container.style.fontSize = fontSize; + container.style.margin = 0; + container.style.padding = 0; + + doc.body.appendChild(container); + + // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) + img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; + img.width = 1; + img.height = 1; + + img.style.margin = 0; + img.style.padding = 0; + img.style.verticalAlign = "baseline"; + + span.style.fontFamily = font; + span.style.fontSize = fontSize; + span.style.margin = 0; + span.style.padding = 0; + + span.appendChild(doc.createTextNode(sampleText)); + container.appendChild(span); + container.appendChild(img); + baseline = (img.offsetTop - span.offsetTop) + 1; + + container.removeChild(span); + container.appendChild(doc.createTextNode(sampleText)); + + container.style.lineHeight = "normal"; + img.style.verticalAlign = "super"; + + middle = (img.offsetTop-container.offsetTop) + 1; + metricsObj = { + baseline: baseline, + lineWidth: 1, + middle: middle + }; + + fontData[font + "-" + fontSize] = metricsObj; + + doc.body.removeChild(container); + + return metricsObj; + }; +})(); diff --git a/src/Parse.js b/src/Parse.js index 6ff9d07..eb5ba85 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -3,7 +3,6 @@ _html2canvas.Parse = function (images, options) { var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default numDraws = 0, - fontData = {}, doc = element.ownerDocument, support = _html2canvas.Util.Support(options, doc), ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), @@ -66,67 +65,6 @@ _html2canvas.Parse = function (images, options) { return text.replace(/^\s*/g, "").replace(/\s*$/g, ""); } - function fontMetrics (font, fontSize) { - - if (fontData[font + "-" + fontSize] !== undefined) { - return fontData[font + "-" + fontSize]; - } - - var container = doc.createElement('div'), - img = doc.createElement('img'), - span = doc.createElement('span'), - sampleText = 'Hidden Text', - baseline, - middle, - metricsObj; - - container.style.visibility = "hidden"; - container.style.fontFamily = font; - container.style.fontSize = fontSize; - container.style.margin = 0; - container.style.padding = 0; - - body.appendChild(container); - - // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) - img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; - img.width = 1; - img.height = 1; - - img.style.margin = 0; - img.style.padding = 0; - img.style.verticalAlign = "baseline"; - - span.style.fontFamily = font; - span.style.fontSize = fontSize; - span.style.margin = 0; - span.style.padding = 0; - - span.appendChild(doc.createTextNode(sampleText)); - container.appendChild(span); - container.appendChild(img); - baseline = (img.offsetTop - span.offsetTop) + 1; - - container.removeChild(span); - container.appendChild(doc.createTextNode(sampleText)); - - container.style.lineHeight = "normal"; - img.style.verticalAlign = "super"; - - middle = (img.offsetTop-container.offsetTop) + 1; - metricsObj = { - baseline: baseline, - lineWidth: 1, - middle: middle - }; - - fontData[font + "-" + fontSize] = metricsObj; - - body.removeChild(container); - - return metricsObj; - } - function drawText(currentText, x, y, ctx){ if (currentText !== null && trimText(currentText).length > 0) { ctx.fillText(currentText, x, y); @@ -136,11 +74,9 @@ _html2canvas.Parse = function (images, options) { function setTextVariables(ctx, el, text_decoration, color) { var align = false, - font_style = getCSS(el, "fontStyle"), bold = getCSS(el, "fontWeight"), family = getCSS(el, "fontFamily"), - size = getCSS(el, "fontSize"), - font_variant = getCSS(el, "fontVariant"); + size = getCSS(el, "fontSize"); switch(parseInt(bold, 10)){ case 401: @@ -152,11 +88,11 @@ _html2canvas.Parse = function (images, options) { } ctx.setVariable("fillStyle", color); - ctx.setVariable("font", [font_style, font_variant, bold, size, family].join(" ")); + ctx.setVariable("font", [getCSS(el, "fontStyle"), getCSS(el, "fontVariant"), bold, size, family].join(" ")); ctx.setVariable("textAlign", (align) ? "right" : "left"); if (text_decoration !== "none"){ - return fontMetrics(family, size); + return _html2canvas.Util.Font(family, size, doc); } } diff --git a/tests/test.js b/tests/test.js index c0ce9ca..77079c7 100644 --- a/tests/test.js +++ b/tests/test.js @@ -11,7 +11,7 @@ var h2cSelector, h2cOptions; document.write(srcStart + '/tests/assets/jquery-1.6.2.js' + scrEnd); document.write(srcStart + '/tests/assets/jquery.plugin.html2canvas.js' + scrEnd); - var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'Support', 'renderers/Canvas'], i; + var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'Support', 'Font', 'renderers/Canvas'], i; for (i = 0; i < html2canvas.length; ++i) { document.write(srcStart + '/src/' + html2canvas[i] + '.js' + scrEnd); }