[FIX] web_editor: fix gap between transformed shape and next section

Previously, when flipping some shapes vertically on chrome, and
vertically or horizontally on firefox, there was a gap of 1px or less at
some screen widths. This is caused by the way these browsers render
backgrounds on transformed elements. It seems like firefox will round
the size of the element to render the background image on it if the
element is transformed, while chrome will always render the background
at the correct size (or render it at a larger size and clip it to the
correct size after the fact), but will round the coordinates of the
symmetry point, causing only the vertical flip to have this issue, and
only if the background image would not extend beyond the bounds of the
element (on firefox, even shapes that would extend beyond the bounds of
the element would showcase a gap, eg origins 1)

This commit fixes that by applying the transform to the SVG rather than
to the element, by use of the shapes controller.

task-2369560

closes odoo/odoo#61609

X-original-commit: 42b3ad10e0b32b7fc72f801e2c67d6baf938c566
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
Samuel Degueldre
2020-11-10 18:27:45 +00:00
committed by qsm-odoo
parent 49838338e0
commit c5a2652c9b
11 changed files with 55 additions and 25 deletions
+12 -5
View File
@@ -531,13 +531,20 @@ class Web_Editor(http.Controller):
svg = file.read()
user_colors = []
for key, color in kwargs.items():
match = re.match('^c([1-5])$', key)
if match:
for key, value in kwargs.items():
colorMatch = re.match('^c([1-5])$', key)
if colorMatch:
# Check that color is hex or rgb(a) to prevent arbitrary injection
if not re.match(r'(?i)^#[0-9A-F]{6,8}$|^rgba?\(\d{1,3},\d{1,3},\d{1,3}(?:,[0-9.]{1,4})?\)$', color.replace(' ', '')):
if not re.match(r'(?i)^#[0-9A-F]{6,8}$|^rgba?\(\d{1,3},\d{1,3},\d{1,3}(?:,[0-9.]{1,4})?\)$', value.replace(' ', '')):
raise werkzeug.exceptions.BadRequest()
user_colors.append([tools.html_escape(color), match.group(1)])
user_colors.append([tools.html_escape(value), colorMatch.group(1)])
elif key == 'flip':
if value == 'x':
svg = svg.replace('<svg ', '<svg style="transform: scaleX(-1);" ')
elif value == 'y':
svg = svg.replace('<svg ', '<svg style="transform: scaleY(-1)" ')
elif value == 'xy':
svg = svg.replace('<svg ', '<svg style="transform: scale(-1)" ')
default_palette = {
'1': '#3AADAA',
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1400 244.7" style="enable-background:new 0 0 1400 244.7;" xml:space="preserve">
viewBox="0 0 1400 244.7" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.58;}
.st1{fill:none;stroke:#383E45;stroke-width:1.4665;stroke-miterlimit:10;}

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1400 1400" style="enable-background:new 0 0 1400 1400;" xml:space="preserve">
viewBox="0 0 1400 1400" xml:space="preserve">
<style type="text/css">
.st0{fill:#F6F6F6;}
</style>

Before

Width:  |  Height:  |  Size: 489 B

After

Width:  |  Height:  |  Size: 444 B

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1400 450" style="enable-background:new 0 0 1400 450;" xml:space="preserve">
viewBox="0 0 1400 450" xml:space="preserve">
<style type="text/css">
.st0{fill:#F6F6F6;}
</style>

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 432 B

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1400 2800" style="enable-background:new 0 0 1400 350.1;" xml:space="preserve">
viewBox="0 0 1400 2800" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 726 B

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1400 446.9" style="enable-background:new 0 0 1400 446.9;" xml:space="preserve">
viewBox="0 0 1400 446.9" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>

Before

Width:  |  Height:  |  Size: 782 B

After

Width:  |  Height:  |  Size: 736 B

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 -1354.3 1400 1400" style="enable-background:new 0 0 1400 45.7;" xml:space="preserve">
viewBox="0 -1354.3 1400 1400" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 441 B

+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 -840 1400 1400" style="enable-background:new 0 0 1400 560;" xml:space="preserve">
viewBox="0 -840 1400 1400" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:#FFFFFF;fill-opacity:0.3;}

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1400 1400" style="enable-background:new 0 0 1400 363.6;" xml:space="preserve">
viewBox="0 0 1400 1400" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.2;fill:#3AADAA;enable-background:new ;}
.st1{opacity:0.25;fill:#3AADAA;enable-background:new ;}

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1400 1140" style="enable-background:new 0 0 1400 1140;" xml:space="preserve">
viewBox="0 0 1400 1140" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.5;fill:#3AADAA;enable-background:new ;}
.st1{opacity:0.08;fill:#3AADAA;enable-background:new ;}

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -3927,10 +3927,14 @@ registry.BackgroundShape = SnippetOptionWidget.extend({
return color || '';
}
case 'flipX': {
return this.$target.find('> .o_we_shape.o_we_flip_x').length !== 0;
// Compat: flip classes are no longer used but may be present in client db
const hasFlipClass = this.$target.find('> .o_we_shape.o_we_flip_x').length !== 0;
return hasFlipClass || this._getShapeData().flip.includes('x');
}
case 'flipY': {
return this.$target.find('> .o_we_shape.o_we_flip_y').length !== 0;
// Compat: flip classes are no longer used but may be present in client db
const hasFlipClass = this.$target.find('> .o_we_shape.o_we_flip_y').length !== 0;
return hasFlipClass || this._getShapeData().flip.includes('y');
}
}
return this._super(...arguments);
@@ -4036,11 +4040,27 @@ registry.BackgroundShape = SnippetOptionWidget.extend({
target.style.position = 'relative';
}
shapeContainer.className = `o_we_shape o_${shape.replace(/\//g, '_')}`;
flip.forEach(dir => {
shapeContainer.classList.add(`o_we_flip_${dir}`);
});
// Custom colors, overwrite shape that is set by the class
$(shapeContainer).css('background-image', colors ? `url("${this._getShapeSrc()}")` : '');
// Compat: remove old flip classes as flipping is now done inside the svg
shapeContainer.classList.remove('o_we_flip_x', 'o_we_flip_y');
if (colors || flip.length) {
// Custom colors/flip, overwrite shape that is set by the class
$(shapeContainer).css('background-image', `url("${this._getShapeSrc()}")`);
shapeContainer.style.backgroundPosition = '';
if (flip.length) {
let [xPos, yPos] = $(shapeContainer)
.css('background-position')
.split(' ')
.map(p => parseFloat(p));
// -X + 2*Y is a symmetry of X around Y, this is a symmetry around 50%
xPos = flip.includes('x') ? -xPos + 100 : xPos;
yPos = flip.includes('y') ? -yPos + 100 : yPos;
shapeContainer.style.backgroundPosition = `${xPos}% ${yPos}%`;
}
} else {
// Remove custom bg image and let the shape class set the bg shape
$(shapeContainer).css('background-image', '');
}
if (previewMode === false) await this.updateChangesInWysiwyg();
},
@@ -4081,16 +4101,19 @@ registry.BackgroundShape = SnippetOptionWidget.extend({
* @private
*/
_getShapeSrc() {
const {shape, colors} = this._getShapeData();
const {shape, colors, flip} = this._getShapeData();
if (!shape) {
return '';
}
const queryString = Object.entries(colors)
const searchParams = Object.entries(colors)
.map(([colorName, colorValue]) => {
const encodedCol = encodeURIComponent(colorValue);
return `${colorName}=${encodedCol}`;
}).join('&');
return `/web_editor/shape/${shape}.svg?${queryString}`;
});
if (flip.length) {
searchParams.push(`flip=${flip.sort().join('')}`);
}
return `/web_editor/shape/${shape}.svg?${searchParams.join('&')}`;
},
/**
* Retrieves current shape data from the target's dataset.