Skip to content

Commit

Permalink
Work on table padding issue #95
Browse files Browse the repository at this point in the history
Changed `getDefaultStyle` to create the element in the sandbox so it is pure (not tainted by CSS / ShadowDOM in the main document).
Never clone SCRIPT elements (that's just asking for trouble).
Removed `imagediff` package as it's not useful anymore.
  • Loading branch information
IDisposable committed Jan 12, 2023
1 parent a29ba3d commit b51f0d0
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 77 deletions.
1 change: 0 additions & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ module.exports = function(config) {
},

'test-lib/jquery/dist/jquery.js',
'node_modules/imagediff/imagediff.js',
'test-lib/tesseract-1.0.19.js',

'src/dom-to-image-more.js',
Expand Down
55 changes: 9 additions & 46 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"grunt-contrib-uglify": "^5.2.2",
"grunt-contrib-watch": "^1.1.0",
"grunt-karma": "^4.0.2",
"imagediff": "^1.0.8",
"js-yaml": "^4.1.0",
"karma": "^6.4.1",
"karma-chai": "^0.1.0",
Expand Down
46 changes: 31 additions & 15 deletions spec/dom-to-image-more.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
'use strict';

const assert = global.chai.assert;
const imagediff = global.imagediff;
const domtoimage = global.domtoimage;
const Promise = global.Promise;
const BASE_URL = '/base/spec/resources/';
Expand Down Expand Up @@ -384,20 +383,31 @@
.then(done).catch(done);
});

it('should honor zero-padding table elements', function (done) {
loadTestPage(
'padding/dom-node.html',
'padding/style.css',
'padding/control-image'
)
.then(renderAndCheck)
.then(done)
.catch(done);
});

function compareToControlImage(image, tolerance) {
const control = controlImage();
if (imagediff.equal(image, control, tolerance)) {
assert.isTrue(true, 'rendered and control images should be same');
} else {
// get the data representation so we can update the control images easily
const imageUrl = getImageBase64(image, 'image/png');
const controlUrl = getImageBase64(control, 'image/png');
assert.equal(imageUrl, controlUrl, 'rendered and control images should be same');
if (imageUrl !== controlUrl) {
console.log(` image: ${image.src}`);
console.log(` imageBase64: ${imageUrl}`);
console.log(`controlBase64: ${controlUrl}`);
}

const imageUrl = getImageBase64(image, 'image/png');
const controlUrl = getImageBase64(control, 'image/png');
assert.equal(
imageUrl,
controlUrl,
'rendered and control images should be same'
);
if (imageUrl !== controlUrl) {
console.log(` image: ${image.src}`);
console.log(` imageBase64: ${imageUrl}`);
console.log(`controlBase64: ${controlUrl}`);
}
}

Expand Down Expand Up @@ -474,8 +484,14 @@
it('should parse urls', function () {
const parse = domtoimage.impl.inliner.impl.readUrls;

assert.deepEqual(parse('url("http://acme.com/file")'), ['http://acme.com/file']);
assert.deepEqual(parse('url(foo.com), url(\'bar.org\')'), ['foo.com', 'bar.org']);
assert.deepEqual(parse('url("http://acme.com/file")'), [
'http://acme.com/file',
]);
// eslint-disable-next-line quotes
assert.deepEqual(parse("url(foo.com), url('bar.org')"), [
'foo.com',
'bar.org',
]);
});

it('should ignore data urls', function () {
Expand Down
1 change: 1 addition & 0 deletions spec/resources/padding/control-image
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAL0AAABdCAYAAADwk2eyAAAAAXNSR0IArs4c6QAAEEBJREFUeF7tnX9sVNeVgD8CrrEDxsjjMqbOEMuGtRG4y48sQbv+A0vEzQpn5Uj1lgUqSAUiIEJx+SGxQDbEpSqlpMWCVF4FUEIWFaRYC2woRWv+cFYpCYESEPaCLcB1sVmbxXitwe5gWJ2Zd+03wwAzo8nAs8+VIpzne9+5577vnnvOGb8zw36wcOEDtOkKDKEVGKbQD6Gnrar6V0ChVxCG3AoEQX/wo48G9QLMX7SoXz8n62rX4ycHDgzqZxYv5X65cGH/rRT6eK1qAu+j0Ee/2Ao9oJY+enCcPEKhV+idzG9Mc1foFfqYwHHyIIVeoXcyvzHNXaFX6GMCx8mDFHqF3sn8xjR3hV6hjwkcJw9S6BV6J/Mb09wVeoU+CJxbwHGgFrhg/eZvgdVAThjEnNhfoVfo+1EWyN+wAJcP6v8P2Az8l9VD/sjhr2zgO7W/Qq/Q92MsoP83MBX4DfAtYC/wvtXjTWtTmAFO7a/QK/T90P/YZtX/HRj/BOid2l+hV+iDPHVxWQT2DGsD/Bq4+ghLL5ed2F+hV+gfCk+/BCqB9Za78yj3xgx0Wn+FXqHvh14C13eBU9a/33uCe+PU/gq9Qt8PvWRuTJpSMjZPCmSd2l+hV+j90EvWZuB9IjgEuKz0pdkI/wBssraIk/sr9Ap9WOjlokAuefntNq9fXB5xgUKhd1L/dH1dUN+cMkwfBj4A0qx8vAD+F8u6i58vm0CCW3F7pDm1f5VCr9DH9AcsDh6k7o26Nw7GN7apK/QKfWzkOHiUQq/QOxjf2Kau0Cv0sZHj4FEKvULvYHxjm/ojoY/tdjpKV8BZK6AFXJ31vHS2cVgBhT4Oi6i3cNYKaAFXZz0v/2y1gGv0D00DWQ1ko6fG4SMUeoXe4QhHP32FXqGPnhqHj1DoFXqHIxz99BV6hT56ahw+QqFX6B2OcPTTV+gV+uipcfgIhV6hdzjC0U9foVfoo6fG4SMU+iEOvVQ6kPdiTZHWl4AFgFQqtjd5V1YKuEq5vxtWFTTpI/UtR4f0lXo4UiBKKh9LVWOpdizv20rJkNAm797KNxabigtzgB+FFIqVMfGUr9APYegFdKlH+SsLcnvxpj2AbADTBFgpCSJVjAVyM1bAl/GmyT2+D7xqlQ+R6/ICuVRUkHvIJjFNNpGUDZRyI7IxZINISXCptGBqaX4T8hX6IQy9VDYQSytl+UwzMEuZD7HO0kxJbvs1uW6qFtvH/87aGFLtWKofS5ONUAyIFbeXE5FNFXrNbAT7+HjLV+iHMPTigghQYtVNM4DZy3KLOyNWWqy83e0xm0ZAl4KvZoOss9wb+5c4mFPDvkFk/HdsJ4KMN5vGvsHiLV+hH8LQ21j3W2MBTiytQBZai97eVzaGuCTS34BqoLf3k5/l5BD3xsQMdujtfcW1EZdG7is/h54q8ZSv0A9x6AUw8cmNLy6FnFY8AnqBXNwOcXuk8NPPLffIbukNnLJ5jlj+vfQX90ZaKPRSBlz8eokTyoE/WydKOOjjJV+hH8LQiz8vLoYJZO3+u93SS+ZEAl6x5tLfZGvCuTfmlBDQ7UFrOPdGNpBYdnGzjCsUzr2Jt3yFfohCLxZewJRgVDImj/PpzVfwhGZUwkFvrpmqx+a+odCb2EEKwsqpYVo46OMtX6EfotAb6OyViI0PLlbdWHp7LcvQL1oTN0jcFQFVrL+4RgZuydGbE8Fkb4x7I/eU30tgLBvOXi3ZpDeNe/NNyN/+g/kMHzHcv8/0dcHQKMwB/x/r64LGhxeLL5mZQivYlFy8uBuSSpRMTT7wtZVqlBTkIqALuGbl0wV6AdRkWCQlKeDKWMnXm6/uEb9dmvy+A5hp+fByTU4HkSv3SraCadkI4k5Jvl82iIyLl/zfvjqfiRkKvQPwDj/FWKGXu8mHQGJtBUyx+GJ1BTqx9AKmwGy+QlOgFUsvvrdsEnGNTExgd5HEMss4sf4C6RrrXxkr/8mnrbIZzKkiMPdacMvpIptR7i9yZDPI919Ji6f8Zq1arFWLHbvjY5y4+vRD1KePkZdBMUyhV+gHBcjRKKHQK/TR8DIo+ir0Cv2gADkaJRR6hT4aXgZFX61aPCgeoyoR6wpoAddYV07HOXYFFHrHPjqdeKwrEAR96oFVsd7HEeO8C6v65+lkXQeLHomExr5mCn0iVz5OshT66BdSoQfU0kcPjpNHKPQKvZP5jWnuCr1CHxM4Th6k0Cv0TuY3prkr9Ap9TOA4eZBCr9A7md+Y5q7QK/QxgePkQQq9Qu9kfmOa+xOgT6e8dgJFRcmkjgB6+rhz7R5JPV2smmZe+YXC7fksWJqGKx24dx9vWy/ekQ+oz7zIfjOtvHEsq8lmev5wkqx7dTTeh/ZWNhS3Wb0SJ+/hD3Uik+16M5cfbx1Llus5/5x9Hb109EDnzvPseM9SI466Pknew3okU/zhROaVpjJGnoe0nj5aj7dS/foNmu2YyDwPjmfqXycFnq9fn7tc2NXE7ne9QUAVbp9E+ZK0fr39LJzp4LdLrnO60d41CvmlHlZvz6Qgz2LCz46XP6xv4uOD8ubsQIun/MdCX3RkGovn3uP0iiaq9/VRsM7D4q1jcV1r50cFFvRLJrFzbxrej65T/cN2vHPc/GN1NtPzeqkbdsGCPpnyc1Mpye7mWPlVak4Np2jXi5SvGgWnmlllQZ9IeaGwRCQ7z8PbF9y4Pr/BnmUt1KdnsqDKQ/HLUF/xlQV9HHWNQF5YPUqT6Ki5zu7Xb+Kd72HlHjeedOg42MCGf5JaBtIyWd2aQ6Grl7MVV9hd1cesnRNZuCaVVHo5/cp5qk8Gerp2TqFyTSq+M20cmN/M6fRxrDw8gekvgu+zFpYXSS2EQPOvYyTyLd08PV5OrLjCoYPDKf5kIgvKkqG7m5rRlzhm3TPe8h8DfSVr2yeQd62N5S8N2AfX1slUfv8uyy3oi38/gwVFvZxIueivVuVvedlsPDeWG6MN9DlUPsgkteYyFa939i9Q4aHvstR104J+XELlBcMSma5UT+WDpSM4+9o5dh81aqSz8mouKbsM9PHTNRJ5wXr8lLW3PRSk37UZHJh1ZBrLSpPw2gwMa/Kp2plGaoPNgJHB6tZcCt191K//ih2/COi4uP5vKMrvs21sYOcUPliTCo232DCxyV89AdyRy987lQ+WpATPCQ9v+9x4RvRyeuJ5qq0TJN7yHwP9vwQWoN8SmOPOzdraFHYUByy9WVBjWcz2KKnNJ6u4wbL0ljLdZldbR1deDmv33GXHK+LeWAueIHnBsESmq3nQXmPxrIfi2pvP4gsNlqWPn66RyAvW459ZdiWfWdndnEi5NGCELMCwAz43l59/msGYM20snz1g1AKAQeu+L9hkfYvCvNoZlBXd4+wb59kt36AgzWyazi4OjW3ghP9iWuTyN+dTtTUN7+EmNpRL9R37pgneYPGW/1j3pqB6KquXppAEeBu6+GpPM/urgn095uZQeSSTrJFA513qD/8Ph5bdDPYdgZJPp1H+qtzpPnfO3KFuZzM1IX5bIuWFugURycbN6lYPhW7xk300H2/nd+tbQnzaOOoagbzH/cGZZ0k2pSsy/T67P44Ksur9B66E8hRt+w7Fi8bgyQ7EKnbo7T1dc9zM25zJjKKUQBwQBL29J0QsPy+NkvXjKSqTmEHuEXKq2G4bD/lPzN643pzAsvWZ5L5oBW7XuqjdeJVDdmDzxrFgn5uiv0v2bxB6eql/v4X9FbesYy8w68LNEymvGEuWFWB5L96iZkkTtWcGtEqUvHCwRCSbdOZ98gIlpdZDv9dH68EWqn8YvNHjpusT5IXTw7Mql2VbMsga6aP5ZDu13WNZvCglDPSpFH+YS9n8FJLavFz46Ca+shxmhVh6eTqu+R4Wb/s2BdkPaK37X+o+S2be5jRSw0Afsfy8DMqrsykuSsbX2MVXe7txbRxPQfrD0MdT/hOhNzh6VuWwsCIjAH/PXerKL7C/36+1es0cx+L33Lzsh/8+rfsa2fTGgA8f6JXMrK0eSldZ8Hd0cWh2AyeCMgDwTcurefnRf08fkey8DMq2j6fYgl/85XeK24I2eVx1fYS85qD3AtZS8vvJlM8dTkfNn/yBrN9xCefe5LlZWyf+fy9n10sgGzjBw7k3hXunsmJJCr4/tHFgUXPgZAvr3iRHLr80h8pDmWR1d3PirSbLiJqYIBj6eMt/DPQ/Ze3no/mP2Veo7zfEqZR/OZmSmc9x52gTFa/doqR2Mnk/u8RuK9KXrp7tU9i4LpWktk6qsy5zmhw2fu5j2+yWAZOel8HKk7n+DEDze1/wToU7ofI2nLVDH5mu7J3MxpZLbNtiO5nmT2Tdv43Fdc/LiSQJ5uOnayTy9tuhz9wdCE6v3WZHju25hYG+pHYG5XOG03G4gQ3lJqMTBvq8HCqvBOA8NvoSNUb1cNCba0+Un8zi+u9SlH+fpnfP2NYzDPTfgPzHQ387m29VXWDbFlvOdPsUfrMuFW8/9DMoS7nJptktNivn4e27bjydA9BXPkij9bXztqwHFH4yjdVlSQPQJ1DeQ9BHINtvMef08OucK/6ipoGWzso/TWK6ewD6eOkaibwg6Iur/RkR2jrZn3WZOnOyfjKZZWVJlntzg8LSZKZvz/cHrHeOX6Xi79sDqoghOpXL9GzLp9+WTuFP3CxdnkbqPS+1BRf52DqRPTunsHFNKkmWe9Nams7XZS9EKD+LVz78NgXp0Fx1nnfesvia6eHtU248oyxL35hOYZ6bpbKR4yh/0i8/5Y/nLvlVDnlzykp/jfLR9H4LNbvaqZf87MEXmJ7d2+/eGItx57Ob1Gxpo+5UIN9aXpZER797E0jjZfX0cnbrdY7+rNOfP162y00uxr2xdnmC5AW7N5HpatwEX+NtjlX8mWNHJbedy8I1gc8bAu5N/HSNRF6QezNsFzs/HMsYSTw0dtFwDrJmp0A3ZOUnQaeXry8O4/n66/ynKzewEeij41QXzYwkdyJ4R6SQ5QbvHztpHTmCG291M+GIG89I8LV5qT/5F54vep4x3fcZMyWZpHu91J+8TzrtbDo4OmL5jdMm+T0Gf0LgVBedo0cxIfs+PlcKrlHQcaoTXxbUFfTwshjQOMpP8f2KLXsfBX1rNnmjgJHPBaJ/WcxrXdTZAtmS2mmUvTQcRjxHkmRwpHX2Ur/PHsjmUOnLwNUDSaMCAbH/k9uG27ZA1s3aBMp7KL8dgWw/hPOT8WHTVT7tPGwPZOOnayTyQgPZwl1TWPpmqj+zIp+unt1ymerG8YEM24g+mt9v4p23JM5KZ8GXuRTPHD7wLMqaaF1vZew6vNS+cZGPj4Jr3STWbUn3w+h/tv96nR3rRwYyWa77dBxv4RevBeKZiOXnuVl9MptCiRHv9dFR187Hxc1kWVk+37VOauZe9sd68Za/4Qupqxxo+o7sgKvumJ/0HdnoH1XE2Zvob/1sjxgssAwWPRJJi0Kvf2WZSN6eCVkKvUL/TICYyEko9Ap9Inl7JmQp9Ar9MwFiIifxSOgTOQmVpSvwtFZAC7g+rZVXuU9tBRT6p7b0KvhprcD/A2zEuZTpbDr+AAAAAElFTkSuQmCC
51 changes: 51 additions & 0 deletions spec/resources/padding/dom-node.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<div id="mywrap" class="wrapper">
<style type="text/css">
.tg {
border-collapse: collapse;
border-spacing: 0;
}

.tg .tg-lqy6 {
text-align: right;
vertical-align: top
}

.tg .tg-ogn4 {
background-color: #cb0000;
text-align: right;
vertical-align: top
}

.tg .tg-zv49 {
background-color: #6200c9;
color: #ffffff;
text-align: right;
vertical-align: top
}

.wrapper {
display: inline-block;
}
</style>
<table class="tg">
<thead>
<tr>
<th class="tg-lqy6"></th>
<th class="tg-lqy6" style="padding-left: 5px"></th>
<th class="tg-ogn4">aaaaa</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tg-lqy6"></td>
<td class="tg-lqy6"></td>
<td class="tg-ogn4">aaaaaa</td>
</tr>
<tr>
<td class="tg-zv49">ssssss</td>
<td class="tg-zv49">sssss</td>
<td class="tg-zv49">aaaaaaa</td>
</tr>
</tbody>
</table>
</div>
17 changes: 17 additions & 0 deletions spec/resources/padding/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
th,
td {
padding: 0px;
border: 2px solid #585c5c;
}

* {
box-sizing: border-box;
}

table {
border-collapse: collapse;
}

.wrapper {
display: inline-block;
}
41 changes: 27 additions & 14 deletions src/dom-to-image-more.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@
} else {
domtoimage.impl.options.useCredentials = options.useCredentials;
}

if (typeof (options.httpTimeout) === 'undefined') {
domtoimage.impl.options.httpTimeout = defaultOptions.httpTimeout;
} else {
Expand Down Expand Up @@ -236,7 +236,10 @@
}

function cloneNode(node, filter, root, parentComputedStyles, ownerWindow) {
if (!root && filter && !filter(node)) {
// NEVER clone SCRIPT blocks and if not at root, and there's a filter
// ignore anything for which filter returns falsey
if (node.tagName === 'SCRIPT'
|| (!root && filter && !filter(node))) {
return Promise.resolve();
}

Expand All @@ -248,7 +251,7 @@
.then(function (clone) {
return processClone(node, clone);
});

function makeNodeCopy(original) {
return util.isHTMLCanvasElement(original)
? util.makeImage(original.toDataURL())
Expand Down Expand Up @@ -358,7 +361,10 @@

function formatPseudoElementStyle() {
const selector = `.${cloneClassName}:${element}`;
const cssText = style.cssText ? formatCssText() : formatCssProperties();
const cssText = style.cssText
? formatCssText()
: formatCssProperties();

return document.createTextNode(`${selector}{${cssText}}`);

function formatCssText() {
Expand Down Expand Up @@ -878,7 +884,7 @@
const value = node.style.getPropertyValue(propertyName);
const priority = node.style.getPropertyPriority(propertyName);

if(!value) {
if (!value) {
return Promise.resolve();
}

Expand Down Expand Up @@ -921,10 +927,15 @@

util.asArray(sourceComputedStyles).forEach(function (name) {
const sourceValue = sourceComputedStyles.getPropertyValue(name);
const defaultValue = defaultStyle[name];
const parentValue = parentComputedStyles?.getPropertyValue(name);

// If the style does not match the default, or it does not match the parent's, set it. We don't know which
// styles are inherited from the parent and which aren't, so we have to always check both.
if (sourceValue !== defaultStyle[name] ||
(parentComputedStyles && sourceValue !== parentComputedStyles.getPropertyValue(name))) {
if (
sourceValue !== defaultValue ||
(parentComputedStyles && sourceValue !== parentValue)
) {
const priority = sourceComputedStyles.getPropertyPriority(name);
setStyleProperty(targetStyle, name, sourceValue, priority);
}
Expand Down Expand Up @@ -953,32 +964,34 @@
sandbox.contentDocument.head.appendChild(charset);
sandbox.contentDocument.title = 'sandbox';
}
const defaultElement = document.createElement(tagName);
sandbox.contentWindow.document.body.appendChild(defaultElement);
const sandboxWindow = sandbox.contentWindow;
const defaultElement = sandboxWindow.document.createElement(tagName);
sandboxWindow.document.body.appendChild(defaultElement);
// Ensure that there is some content, so that properties like margin are applied.
defaultElement.textContent = '.';
const defaultComputedStyle = sandbox.contentWindow.getComputedStyle(defaultElement);
const defaultComputedStyle = sandboxWindow.getComputedStyle(defaultElement);
const defaultStyle = {};
// Copy styles to an object, making sure that 'width' and 'height' are given the default value of 'auto', since
// their initial value is always 'auto' despite that the default computed value is sometimes an absolute length.
util.asArray(defaultComputedStyle).forEach(function (name) {
defaultStyle[name] =
(name === 'width' || name === 'height') ? 'auto' : defaultComputedStyle.getPropertyValue(name);
});
sandbox.contentWindow.document.body.removeChild(defaultElement);
sandboxWindow.document.body.removeChild(defaultElement);
tagNameDefaultStyles[tagName] = defaultStyle;
return defaultStyle;
}

function removeSandbox() {
if (!sandbox) {
return;
document.body.removeChild(sandbox);
sandbox = null;
}
document.body.removeChild(sandbox);
sandbox = null;

if (removeDefaultStylesTimeoutId) {
clearTimeout(removeDefaultStylesTimeoutId);
}

removeDefaultStylesTimeoutId = setTimeout(() => {
removeDefaultStylesTimeoutId = null;
tagNameDefaultStyles = {};
Expand Down

0 comments on commit b51f0d0

Please sign in to comment.