quill-image-resize-module
quill-image-resize-module copied to clipboard
Image align styles not being retained when they are already set
I cannot get the image styles that are set with the align to be kept when re-displaying the image inside a quill editor.
Use case:
- Add and right align an image in the editor.
- Save the editor innerHTML in a database
- Redisplay the edited HTML in a new quill editor to allow user to update the HTML
Expected: image in the new editor should still be aligned right. Actual: image in new editor has had all the style attributes removed
I've updated the demo Plunk to show the issue. In this example, I've added the align style attributes to the first image in the HTML (style="display: inline; float: right; margin: 0px 0px 1em 1em;"
). The editor has removed them and the image is not being aligned right.
i have the same proble. ,
Have you tried adding 'width
' to the formats: [...]
option?
I have the same problem, the image gets the following styles after I align center: display: block; margin: auto;
. I then save the html to a database and when I load it again, the previous style is not there.
Have the same issue. none of the styles get saved.
To allow for the style data to be stored within the Delta, I have used the following
Quill.register(new Parchment.Attributor.Style('display', 'display', {
whitelist: ['inline']
}));
Quill.register(new Parchment.Attributor.Style('float', 'float', {
whitelist: ['left', 'right', 'center']
}));
Quill.register(new Parchment.Attributor.Style('margin', 'margin', {}));
I figured out the core issue is that there is a whitelist of allowed attributes on the image tag and style is not one of them. See https://github.com/quilljs/quill/issues/1556
After an npm upgrade
it works as expected now
@muhammaddadu they way you did it with register it seems it didn't work for me .. what other modifications did you do to allow it ? :D
@bogdaniel Had to monkeypatch the imageformat.
ImageFormat.formats = function formats(domNode: any): any {
return IMAGE_FORMAT_ATTRIBUTES.reduce(
(formats, attribute) => {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
},
{}
);
};
ImageFormat.prototype.format = function format(name: string, value: any): void {
if (IMAGE_FORMAT_ATTRIBUTES.indexOf(name) !== -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
this.super.format(name, value);
}
};
Quill.register(ImageFormat, true);
had an issue with creating a new Format so went down this route for a quickfix.
@bdurand , you're absolutely right! thanks!
here's my solution:
var BaseImageFormat = Quill.import('formats/image');
const ImageFormatAttributesList = [
'alt',
'height',
'width',
'style'
];
class ImageFormat extends BaseImageFormat {
static formats(domNode) {
return ImageFormatAttributesList.reduce(function(formats, attribute) {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
format(name, value) {
if (ImageFormatAttributesList.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
}
Quill.register(ImageFormat, true);
var quill = new Quill('#editor', {
theme: 'snow',
modules: {
imageResize: {}
}
});
FYI CodePen with a working example using @MaximusBaton and @bdurand's work
I've tried everything recommended in this thread but no matter how I override, extend or register ImageFormat, the overridden format(name, value)
is never entered. I can break into static formats(domNode)
and style is being recognized and added to the returned formats
but passing html with img tags containing style attributes are still stripped of their attributes and then sent through onEditorChange.
My most recent attempt was to remove 'image' from formats and replace it with my custom blot... No luck
Upon setting editor value, it strips attributes.
const ParchmentEmbed = Quill.import('blots/block/embed');
console.log('>>>> ParchmentEmbed');
console.log(ParchmentEmbed);
const ATTRIBUTES = [
'alt',
'height',
'width',
'style'
];
class ImageWithStyle extends ParchmentEmbed {
static create(value) {
let node = super.create(value);
if (typeof value === 'string') {
node.setAttribute('src', this.sanitize(value));
}
return node;
}
static formats(domNode) {
//debugger;
return ATTRIBUTES.reduce(function(formats, attribute) {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
static match(url) {
return /\.(jpe?g|gif|png)$/.test(url) || /^data:image\/.+;base64/.test(url);
}
static sanitize(url) {
return url;
//return sanitize(url, ['http', 'https', 'data']) ? url : '//:0';
}
/*
static value(domNode) {
debugger;
return domNode.getAttribute('src');
}*/
format(name, value) {
debugger; // never gets hit
if (ATTRIBUTES.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
}
ImageWithStyle.blotName = 'imagewithstyle';
ImageWithStyle.tagName = 'IMG';
Quill.register(ImageWithStyle, true);
Quill.register('modules/imageDrop', ImageDrop);
Quill.register('modules/imageResize', ImageResize);
...
<ReactQuill
ref={(el) => { this.reactQuillRef = el }}
theme='snow'
onChange={this.handleChange}
value={this.props.editorHtml}
modules={Editor.modules}
formats={Editor.formats}
bounds={'.app'}
placeholder='Author or Paste your contents'
/>
Editor.modules = {
toolbar: [
[{ 'header': '1'}, {'header': '2'}, { 'font': [] }],
[{size: []}],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'},
{'indent': '-1'}, {'indent': '+1'}],
['link', 'image', 'video'],
['clean']
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
imageDrop: true,
imageResize: {}
}
Editor.formats = [
'header', 'font', 'size',
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet', 'indent',
'link', 'imagewithstyle', 'video'
]
...
If anyone can point me in the right direction, I'd really appreciate it
What I do to fix it is rewriting the image format. In order to sanitize style preventing from attacks such as XSS Or unexpected style, I also add white list for style. Environment: React-quill
Like this:
import {Quill} from 'react-quill';
const Parchment = Quill.import('parchment');
const BaseImage = Quill.import('formats/image');
const ATTRIBUTES = [
'alt',
'height',
'width',
'style'
];
const WHITE_STYLE = ['margin', 'display', 'float'];
class Image extends BaseImage {
static formats(domNode) {
return ATTRIBUTES.reduce(function(formats, attribute) {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
format(name, value) {
if (ATTRIBUTES.indexOf(name) > -1) {
if (value) {
if (name === 'style') {
value = this.sanitize_style(value);
}
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
sanitize_style(style) {
let style_arr = style.split(";")
let allow_style = "";
style_arr.forEach((v, i) => {
if (WHITE_STYLE.indexOf(v.trim().split(":")[0]) !== -1) {
allow_style += v + ";"
}
})
return allow_style;
}
}
export default Image;
I've tried to solve this problem with @MaximusBaton solution and images aligning works correctly now. But considering that I use editor only for creating news, problem occured again (with a vengeance) on client app where the news should be displayed. There is no quill installed there and I've been trying to display data using [innerHtml] tag (in Angular html component)
<div [innerHtml]="newsContent" class="m-widget19__body" style="padding-top: 15px"></div>
Everything looks great except images aligning. If necessary I can install quill both on admin/client side, but how can I insert changed images styles to displayed data too? I would be so grateful for any help.
FWIW, I'm using react-quill, and also needed to add 'style'
to the formats
prop, in addition to https://github.com/kensnyder/quill-image-resize-module/issues/10#issuecomment-317747389
@levous I am in the same position as you: my overridden format() method is never entered and so (I think) format attributes recorded in the Quill delta op are never applied to the Parchment DOM node. This means that not even width is translated from delta into HTML -- even though the built-in Quill image blot contains this attribute in its whitelist!
If you ever figured out a solution, I'd love to know what it was.
Does anyone has a solution for this?. Using the above solutions doesn't work
@mdathersajjad I had better luck using quill-blot-formatter
which is derived from this gem, but is customizable. I configured it to use HTML float
attribute, and I extended the quill Image format to recognize float
as a Quill style.
For the package, see: https://github.com/Fandom-OSS/quill-blot-formatter
For my configuration, see: https://codesandbox.io/s/quill-sandbox-in8p7?file=/src/BlotFormatter.ts
@mdathersajjad I had better luck using
quill-blot-formatter
which is derived from this gem, but is customizable. I configured it to use HTMLfloat
attribute, and I extended the quill Image format to recognizefloat
as a Quill style.For the package, see: https://github.com/Fandom-OSS/quill-blot-formatter
For my configuration, see: https://codesandbox.io/s/quill-sandbox-in8p7?file=/src/BlotFormatter.ts
I was able to configure blotformatter with your given configuration it still behaves the same as this module styles are not applied to them when alignment changes and if you store it in database and load them again since style is not there it shows left aligned
Hmm; you're sure that you are registering ImageWithStyle
both at create time and at load time?
If ImageWithStyle
is registered as the Quill image blot, then the HTML should contain float: left
in the image style (or float right, etc) and when the HTML is parsed by Parchment, ImageWithStyle.format
should translate the HTML float back into a float
format in the Quill delta.
My application does not store HTML, only Quill Delta - so it's possible there is a missing piece of my solution. Do you see the float
format in the editor deltas?
Preserving Image Styles In Quill Deltas
I have a working example in Codesandbox if this is TL;DR.
If you want to preserve image styles during Delta⇄HTML conversion, you must do three things:
- extend the default Quill
Image
blot so it can apply the new formats to the HTML DOM - register
width
,height
andfloat
with the Parchment registry with the proper scope (INLINE_BLOT
orBLOCK_BLOT
depending on the superclass of your customImage
) - add
width
,height
andfloat
to the list of allowed formats when you instantiate your Quill editor
If you only work with Quill HTML and never with delta, you can skip all of the above. If you only extend Image
, then its static formats
will allow the new attributes to appear in deltas, but the formats won't be applied to the DOM. To achieve that final step of applying the formats to HTML DOM when processing a delta, the formats must be registered and allowed.
Some subtleties that you also need to pay attention to:
- Always use
Quill.import
to get theImage
blot, orParchment
or other Quill primitives. If youimport
them directly via ES6 then you will probably not get the right objects! - Instead of rewriting Quill's built-in
Image
, it is better to import it, extend it with a derived class that callssuper
, and then re-registerformats/image
your derived class. This minimizes the code you write and preserves other customizations from Quill modules.
Feel free to contact me with any questions; I'm not a Quill expert, but after 20+ hours of debugging this issue I have a certain level of understanding!
Solution for Angular app (and other typeScript one), heavily inspired by xeger, MaximusBaton comments
import Quill from 'quill';
interface EmbedBlot {
new(...args: any[]): EmbedBlot;
domNode: any;
format(name, value);
}
const Image: EmbedBlot = Quill.import('formats/image');
const ImageFormatAttributesList = [
'alt',
'height',
'width',
'style'
];
class StyledImage extends Image {
static formats(domNode) {
return ImageFormatAttributesList.reduce(function (formats, attribute) {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
format(name, value) {
if (ImageFormatAttributesList.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
}
Quill.register(StyledImage, true);
@vbobroff-app @MidaAiZ I applied this solution but still the problem is arising. Image resizing and alignment that applied with style, is sanitized if I go to edit mode. How can I solve this?
Code which I applied
// Resize module start
const ImageResize = await import('quill-image-resize-module');
Quill.register('modules/ImageResize', ImageResize);
const Parchment = Quill.import('parchment');
const BaseImage = Quill.import('formats/image');
const ATTRIBUTES = ['alt', 'height', 'width', 'style'];
const WHITE_STYLE = ['margin', 'display', 'float'];
class Image extends BaseImage {
static formats(domNode) {
return ATTRIBUTES.reduce((formats, attribute) => {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
format(name, value) {
if (ATTRIBUTES.indexOf(name) > -1) {
if (value) {
if (name === 'style') {
value = this.sanitize_style(value);
}
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
sanitize_style = (style) => {
const style_arr = style.split(';');
let allow_style = '';
style_arr.forEach((v) => {
if (WHITE_STYLE.indexOf(v.trim().split(':')[0]) !== -1) {
allow_style += `${v};`;
}
});
return allow_style;
};
}
Quill.register(Image, true);
// Resize module end
return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
what will be the type of formats
in reduce
?
Thank you to @kensnyder for the original work on this excellent package, and to @Fandom-OSS for continuing his work. Neither package has been maintained in several years, so I have decided to try and resurrect support for it.
Please see my TypeScript port of quill-blot-formatter
which is derived from this package. It is available on npmjs.org as @xeger/quill-image-actions
and @xeger/quill-image-formats
and has working examples of how to use. Bug reports and pull requests are welcome!
Switching to the new packages should fix the problems of @insomenia-heejae @mdathersajjad and others.
I am happy to assist @dextel2 with any questions; you can see how to register formats
and modules
in the working demo or, with a React app, in my README.
Happy coding!
@xeger - Could you post your solution in react (typescript), I am facing challenge in deleting the image on 'delete'.
@dextel2 have you played with the demo of @xeger/quill-image
? Delete seems to work okay when using that module. I gaveup working with quill-image-resize-module
a couple years ago, as I found its fork (quill-blot-formatter
) was more reliable, and ultimately I ended up forking and rewriting that in order to create @xeger/quill-image
.
Have you tried adding
'width
' to theformats: [...]
option?
Bro! I've been looking for hours, then it saves me!