[FIX] website, *: fix conversion of specific svgs on Firefox
*: website_slides A function allowing to convert a SVG to a PNG has been added as an utility function in website.utils and is used for the upload of document in website_slides. This function add support for a specific case which was not supported by the previous conversion function used for the document upload in website_slides: SVG without width and height attributes on Firefox. Related to task-2602521 closes odoo/odoo#75866 X-original-commit: b5ad8d66e311afcc30dae0ab62642e61ea74b156 Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com> Co-authored-by: qsm-odoo <qsm@odoo.com>
This commit is contained in:
committed by
qsm-odoo
co-authored by
qsm-odoo
parent
018d4856db
commit
97b103dcd2
@@ -275,6 +275,82 @@ function sendRequest(route, params) {
|
||||
form.submit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a base64 SVG into a base64 PNG.
|
||||
*
|
||||
* @param {string|HTMLImageElement} src - an URL to a SVG or a *loaded* image
|
||||
* with such an URL. This allows the call to potentially be a bit more
|
||||
* efficient in that second case.
|
||||
* @returns {Promise<string>} a base64 PNG (as result of a Promise)
|
||||
*/
|
||||
async function svgToPNG(src) {
|
||||
function checkImg(imgEl) {
|
||||
// Firefox does not support drawing SVG to canvas unless it has width
|
||||
// and height attributes set on the root <svg>.
|
||||
return (imgEl.naturalHeight !== 0);
|
||||
}
|
||||
function toPNGViaCanvas(imgEl) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = imgEl.width;
|
||||
canvas.height = imgEl.height;
|
||||
canvas.getContext('2d').drawImage(imgEl, 0, 0);
|
||||
return canvas.toDataURL('image/png');
|
||||
}
|
||||
|
||||
// In case we receive a loaded image and that this image is not problematic,
|
||||
// we can convert it to PNG directly.
|
||||
if (src instanceof HTMLImageElement) {
|
||||
const loadedImgEl = src;
|
||||
if (checkImg(loadedImgEl)) {
|
||||
return toPNGViaCanvas(loadedImgEl);
|
||||
}
|
||||
src = loadedImgEl.src;
|
||||
}
|
||||
|
||||
// At this point, we either did not receive a loaded image or the received
|
||||
// loaded image is problematic => we have to do some asynchronous code.
|
||||
return new Promise(resolve => {
|
||||
const imgEl = new Image();
|
||||
imgEl.onload = () => {
|
||||
if (checkImg(imgEl)) {
|
||||
resolve(imgEl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set arbitrary height on image and attach it to the DOM to force
|
||||
// width computation.
|
||||
imgEl.height = 1000;
|
||||
imgEl.style.opacity = 0;
|
||||
document.body.appendChild(imgEl);
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', imgEl.src, true);
|
||||
request.onload = () => {
|
||||
// Convert the data URI to a SVG element
|
||||
const parser = new DOMParser();
|
||||
const result = parser.parseFromString(request.responseText, 'text/xml');
|
||||
const svgEl = result.getElementsByTagName("svg")[0];
|
||||
|
||||
// Add the attributes Firefox needs and remove the image from
|
||||
// the DOM.
|
||||
svgEl.setAttribute('width', imgEl.width);
|
||||
svgEl.setAttribute('height', imgEl.height);
|
||||
imgEl.remove();
|
||||
|
||||
// Convert the SVG element to a data URI
|
||||
const svg64 = btoa(new XMLSerializer().serializeToString(svgEl));
|
||||
const finalImg = new Image();
|
||||
finalImg.onload = () => {
|
||||
resolve(finalImg);
|
||||
};
|
||||
finalImg.src = `data:image/svg+xml;base64,${svg64}`;
|
||||
};
|
||||
request.send();
|
||||
};
|
||||
imgEl.src = src;
|
||||
}).then(loadedImgEl => toPNGViaCanvas(loadedImgEl));
|
||||
}
|
||||
|
||||
return {
|
||||
loadAnchors: loadAnchors,
|
||||
autocompleteWithPages: autocompleteWithPages,
|
||||
@@ -282,5 +358,6 @@ return {
|
||||
prompt: prompt,
|
||||
sendRequest: sendRequest,
|
||||
websiteDomain: websiteDomain,
|
||||
svgToPNG: svgToPNG,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { qweb as QWeb, _t } from 'web.core';
|
||||
import Dialog from 'web.Dialog';
|
||||
import publicWidget from 'web.public.widget';
|
||||
import utils from 'web.utils';
|
||||
import wUtils from 'website.utils';
|
||||
|
||||
var SlideUploadDialog = Dialog.extend({
|
||||
template: 'website.slide.upload.modal',
|
||||
@@ -124,7 +125,7 @@ var SlideUploadDialog = Dialog.extend({
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_formValidateGetValues: function (forcePublished) {
|
||||
_formValidateGetValues: async function (forcePublished) {
|
||||
var canvas = this.$('#data_canvas')[0];
|
||||
var values = _.extend({
|
||||
'channel_id': this.channelID,
|
||||
@@ -150,18 +151,19 @@ var SlideUploadDialog = Dialog.extend({
|
||||
} else if (values['slide_type'] === 'webpage') {
|
||||
_.extend(values, {
|
||||
'mime_type': 'text/html',
|
||||
'image_1920': this.file.type === 'image/svg+xml' ? this._svgToPng() : this.file.data,
|
||||
'image_1920': this.file.type === 'image/svg+xml' ? await this._svgToPNG() : this.file.data,
|
||||
});
|
||||
} else if (/^image\/.*/.test(this.file.type)) {
|
||||
const fileData = this.file.type === 'image/svg+xml' ? await this._svgToPNG() : this.file.data;
|
||||
if (values['slide_type'] === 'presentation') {
|
||||
_.extend(values, {
|
||||
'slide_type': 'infographic',
|
||||
'mime_type': this.file.type === 'image/svg+xml' ? 'image/png' : this.file.type,
|
||||
'datas': this.file.type === 'image/svg+xml' ? this._svgToPng() : this.file.data
|
||||
'datas': fileData,
|
||||
});
|
||||
} else {
|
||||
_.extend(values, {
|
||||
'image_1920': this.file.type === 'image/svg+xml' ? this._svgToPng() : this.file.data,
|
||||
'image_1920': fileData,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -364,18 +366,18 @@ var SlideUploadDialog = Dialog.extend({
|
||||
this.$('.o_slide_preview').addClass('d-none');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* TODO: Remove this part, as now SVG support in image resize tools is
|
||||
* included?
|
||||
* Python PIL does not support SVG, so converting SVG to PNG.
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
// TODO: Remove this part, as now SVG support in image resize tools is included
|
||||
//Python PIL does not support SVG, so converting SVG to PNG
|
||||
_svgToPng: function () {
|
||||
var img = this.$el.find('img#slide-image')[0];
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
canvas.getContext('2d').drawImage(img, 0, 0);
|
||||
return canvas.toDataURL('image/png').split(',')[1];
|
||||
_svgToPNG: async function () {
|
||||
const imgEl = this.$el.find('img#slide-image')[0];
|
||||
const png = await wUtils.svgToPNG(imgEl);
|
||||
return png.split(',')[1];
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handler
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -589,21 +591,27 @@ var SlideUploadDialog = Dialog.extend({
|
||||
this.modulesToInstallStatus = null;
|
||||
}
|
||||
},
|
||||
|
||||
_onClickFormSubmit: function (ev) {
|
||||
var self = this;
|
||||
var $btn = $(ev.currentTarget);
|
||||
if (this._formValidate()) {
|
||||
var values = this._formValidateGetValues($btn.hasClass('o_w_slide_upload_published')); // get info before changing state
|
||||
var oldType = this.get('state');
|
||||
this.set('state', '_upload');
|
||||
return this._rpc({
|
||||
route: '/slides/add_slide',
|
||||
params: values,
|
||||
}).then(function (data) {
|
||||
self._onFormSubmitDone(data, oldType);
|
||||
});
|
||||
/**
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onClickFormSubmit: async function (ev) {
|
||||
if (!this._formValidate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get info before changing state
|
||||
const forcePublished = $(ev.currentTarget).hasClass('o_w_slide_upload_published');
|
||||
const values = await this._formValidateGetValues(forcePublished);
|
||||
|
||||
var oldType = this.get('state');
|
||||
this.set('state', '_upload');
|
||||
|
||||
const data = await this._rpc({
|
||||
route: '/slides/add_slide',
|
||||
params: values,
|
||||
});
|
||||
this._onFormSubmitDone(data, oldType);
|
||||
},
|
||||
|
||||
_onFormSubmitDone: function (data, oldType) {
|
||||
|
||||
Reference in New Issue
Block a user