[FIX] web_editor: flatten background-images in convert_inline

Outlook on Windows doesn't support background images. This has the
unfortunate effect that there is now way to write text over an image and
have it be seen on Outlook Windows.
This "flattens" background images by converting them and all their
contents to a `png` image, using `html2canvas`.
Note that _all the contents_ of the element with a background-image are
flattened into an image. This means that any interactive content such as
buttons is unsupported in that context.

task-2856858

Part-of: odoo/odoo#91314
This commit is contained in:
Antoine Guenet
2022-05-24 14:51:06 +02:00
parent 3fcb74533a
commit 6b628ebd5b
@@ -3,6 +3,7 @@
import FieldHtml from 'web_editor.field.html';
import { isBlock, rgbToHex } from '../../../lib/odoo-editor/src/utils/utils';
/* global html2canvas */
//--------------------------------------------------------------------------
// Constants
@@ -389,6 +390,10 @@ function classToStyle($editable, cssRules) {
async function toInline($editable, cssRules, $iframe) {
const editable = $editable.get(0);
const iframe = $iframe && $iframe.get(0);
const wysiwyg = $editable.data('wysiwyg');
if (wysiwyg) {
wysiwyg.odooEditor.historyPauseSteps(); // Prevent history reverts.
}
const doc = editable.ownerDocument;
cssRules = cssRules || doc._rulesCache;
if (!cssRules) {
@@ -441,6 +446,7 @@ async function toInline($editable, cssRules, $iframe) {
const rootFontSizeProperty = getComputedStyle(editable.ownerDocument.documentElement).fontSize;
const rootFontSize = parseFloat(rootFontSizeProperty.replace(/[^\d\.]/g, ''));
normalizeRem($editable, rootFontSize);
await flattenBackgroundImages(editable);
// Styles were applied inline, we don't need a style element anymore.
$editable.find('style').remove();
@@ -449,6 +455,34 @@ async function toInline($editable, cssRules, $iframe) {
node.style.setProperty('display', displayValue);
}
}
/**
* Take all elements with a `background-image` style and convert them, along
* with their contents, to `png` images. The images are then appended to the
* original elements stripped of their background images and padding, while all
* of their children are removed.
*
* @param {Element} editable
*/
async function flattenBackgroundImages(editable) {
for (const backgroundImage of editable.querySelectorAll('*[style*=background-image]')) {
if (backgroundImage.parentElement) { // If the image was nested, we removed it already.
const canvas = await html2canvas(backgroundImage);
const image = document.createElement('img');
image.setAttribute('src', canvas.toDataURL('png'));
image.setAttribute('width', canvas.getAttribute('width'));
image.setAttribute('height', canvas.getAttribute('height'));
image.style.setProperty('margin', 0);
image.style.setProperty('display', 'block'); // Ensure no added vertical space.
// Clean up the original element.
for (const child of [...backgroundImage.childNodes]) {
child.remove();
}
backgroundImage.style.setProperty('padding', 0);
backgroundImage.style.removeProperty('background-image');
backgroundImage.prepend(image); // Add the image to the original element.
}
}
}
/**
* Convert font icons to images.
*