From 6b628ebd5b8c7dd125eaa661d5f5ffc0b1f66ca3 Mon Sep 17 00:00:00 2001 From: Antoine Guenet Date: Fri, 13 May 2022 09:48:32 +0000 Subject: [PATCH] [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 --- .../static/src/js/backend/convert_inline.js | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/addons/web_editor/static/src/js/backend/convert_inline.js b/addons/web_editor/static/src/js/backend/convert_inline.js index 01be0f0aa0c..b1a65918ce1 100644 --- a/addons/web_editor/static/src/js/backend/convert_inline.js +++ b/addons/web_editor/static/src/js/backend/convert_inline.js @@ -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. *