From 64c993f2fe79e0b42cd78abf15d4de1f012b670a Mon Sep 17 00:00:00 2001 From: MoyuScript 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 1e8fffa2c9ad1af1950118d5877a7b81e514ee57 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 c278deead446eb2b2dceef6436c67b96570f020f Mon Sep 17 00:00:00 2001 From: MoyuScript 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 51c8cd965b23428252573745b295b945779a3d13 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 de8859f650e3b1aa329913f74dcce21458a5daa8 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 29cbc9a34892fe5a9c87e17d3abc9f73b09d35c1 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 4e3ae130e1ff6d87034b425b4262566890e44eb1 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 baa04cbae4b778593f14a88b0ac8f24333737b42 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 c2fa817b9c257550651fffa5168daa728926344c Mon Sep 17 00:00:00 2001 From: MoyuScript 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 d3ba601bbe21413774d6b5686ed9d3dc6560af8b Mon Sep 17 00:00:00 2001 From: MoyuScript 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 229da9727b903c272bb98753458d8a268d922415 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 d30c658b83a03d295b1eac86a9787b2e57113f76 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 35e274eae1f12ae9585aec4468fd7dcb5b057f04 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 fb731e3037272a87005bda4ff2ff876b2a70b8bf Mon Sep 17 00:00:00 2001 From: MoyuScript 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 f8586e81dd8511c7f26ab59d7ed0c0be6146701c Mon Sep 17 00:00:00 2001 From: MoyuScript 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 f85426f6cbb41f9c6e158658a11dcb9ac4b69b66 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 dfa4c1bd46b3a63ebb73c7b6f79c9216a6799cfe Mon Sep 17 00:00:00 2001 From: MoyuScript 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 bbdd29725a571e9d437ac6cbb878642aa4f87482 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 89e901d000f0eda89019cf15cc93c923fa2789c4 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 e352330c695275a63b4374fad97e174887e3ca94 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 86e1ca0f0073921a530284bb85a35a571701c326 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 9c61608a4bef505fb247ac7fc52c7e8b0fa48bf6 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 23642f2524e100c435a037beb90f1e3f18308bcc Mon Sep 17 00:00:00 2001 From: MoyuScript 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 82f42fb23b02152ca48d1aa678a8fba3e0e92a2e Mon Sep 17 00:00:00 2001 From: MoyuScript 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 0ebb1e41b092b89c20992a2d6a6195f329f64ab0 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 a0506de9948a84194622ebf3cae9440df967c227 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 5fc6e715cd3961b8d15c7421583255473881a5e6 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 ed58f990ae9e3a68adc3316be72f965c3026bf4a Mon Sep 17 00:00:00 2001 From: MoyuScript 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 587a6f609b1bf2ea5e787d527dfbb996fea75097 Mon Sep 17 00:00:00 2001 From: MoyuScript 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 dee10850467d023c242e96a2b352064a45378658 Mon Sep 17 00:00:00 2001 From: MoyuScript 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); }