steveathon / bootstrap-wysiwyg

Tiny bootstrap-compatible WYSIWYG rich text editor
MIT License
661 stars 1.71k forks source link

Image resizing #35

Open Voyteck opened 9 years ago

Voyteck commented 9 years ago

Do you plan adding an image resizing abilities to the editor ? Or maybe you already know a goof plugin that can be used for this purpose, that is also compatible and easy to implement in the editor ?

codewithtyler commented 9 years ago

Are you referring to the images used for the toolbar or when images are added by the user when editing a page?

Voyteck commented 9 years ago

I mean when the user is editing a page...

I am using your (GREAT) editor for email editing - and once I drag'n'drop a large picture in there - it is impossible to resize it... so I've wondering whether there is a nice, easy-to-integrate tool for user to be able to resize the picture (I will be also working on something, that allows user to dbl-click on the picture to open it for e.g. cropping - any hints would be highly welcome :) )

codewithtyler commented 8 years ago

@Voyteck0 Did you find a solution to help you with this this problem?

This is something that given time I plan to add to the project hopefully in the near future. In the mean time I came across this lightweight jQuery plugin that can help you with resizing your images.

Also here is a jQuery plugin called Cropper that you can use to crop images. There is even an example that demonstrates how to open the image in a modal for cropping.

spreadred commented 7 years ago

It would be nice to be able to specify a max image size (height x width) that a user is allowed to paste into the editor. If the image pasted/dragged-dropped exceeds these specifications then the editor could use whatever image resizing library is available to shrink it to those dimensions on the fly.

I would try to implement this myself but I am lacking some technical details of how this could be accomplished. I assume I could add a onChange event handler to the editor, but I am unsure how to find/reference any images located inside the editor window from within its onChange event handler.

EDIT: I eventually settled on using the solution below, which I have since then modified to preserve aspect ratios. The quality of the image resizing leaves something to be desired when going from very large to very small, but it is ok for what I needed.

https://stackoverflow.com/questions/42566469/resizing-image-using-js-jquery-or-html5

steveathon commented 7 years ago

Thanks @kaptainkommie - I'll check out the resizing example you gave. Are you interested in potentially adding the feature to the core?

spreadred commented 7 years ago

@steveathon Yes, I would be interested in integrating this into the core. We would need to figure out how we'd like to implement it. In its current form it is only used for one particular use-case, IE: when an image is added to the editor that is larger than the specified size, it resizes it to that specified maximum size.

I think the best option for integration would to allow the user to set the maximum image size (and whether to keep aspect ratio) in the Options object and resize added images on the fly. In addition to that, it makes sense to give the user the ability to double click on an image to set the desired size (this could also act as an entry point for editing other image properties as well).

As I mentioned in my previous comment, the quality of the resized image leaves a bit to be desired, but I think I can find a way to increase that quality by using some sort of filters/sampling. Hopefully this can be accomplished with pure HTML5/Canvas; I don't think we should be adding additional dependencies to the project.

I should have a bit of free time this weekend. I will look into achieving better image quality as well as rewriting my code for better integration. If anyone could point me to some other open source editors that already have this functionality, that would be helpful as well. Hopefully, I'll be able to push something to my fork soon.

Feel free to let me know if you had a specific way you wanted to integrate this feature that is different from what I mentioned above.

spreadred commented 7 years ago

I have pushed my "image size restrictions" functionality to my fork. After toying with many different ways of resizing the image and comparing quality/performance issues, I have added functions that resize the image using HTML5's Canvas functionality. One function does it in a one step, the other function does it in two steps (for supposedly better image quality). The one-step function is much quicker (and is what is used in the current code) and the difference in quality as far as I can tell between the two is negligible, even down-stepping from 16k resolutions. As mentioned before, this resizing can still result in large amounts of aliasing.

I am working toward allowing users to double-click an image in the editor, which would then bring up an image properties dialog. From there, they could specify their desired dimensions. After changing these dimensions, and clicking apply, the original image in the editor will be run through the resize function. The original image shown in the editor must be replaced by the resized image.

One challenge that immediately springs to mind about this "true resize" functionality is that in order to maintain optimal image resize quality, the original/source image must be retained somehow as all resizes should always operate upon original source image, not resized versions of the source, otherwise quality will suffer terribly.

While I certainly like the idea for true image resizing, perhaps CSS would be the better option, at least for the on-demand, in-editor resize functionality. AFAIK simply changing the img.width or img.height properties would be enough to "resize" the image for display purposes.

Any input on this would be appreciated.

alpertandogan commented 6 years ago

insertFiles = function (files) { editor.focus(); $.each(files, function (idx, fileInfo) { if (/^image\//.test(fileInfo.type)) { $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
execCommand('insertimage', dataUrl); var r=$('img[src="'+dataUrl+'"]'); $( function() { r.resizable(); r.draggable()(); } ); }).fail(function (e) { options.fileUploadError("file-reader", e); }); } else { options.fileUploadError("unsupported-file-type", fileInfo.type); } }); },

Like this method but there is an problem with border and draggable div ... Same works need ...

You can limit image size with hardcoded but it is not a good solution.

spreadred commented 6 years ago

@alpertandogan This is what I settled on for my fork. It's not perfect though. It works on the 2.0 branch

// single step canvas resize
     Wysiwyg.prototype.resizeImageOnce = function( image, width, height) {
        if ( image ) {
            var t0 = performance.now();
            var oc = document.createElement( "canvas" );
            var octx = oc.getContext( "2d" );

            oc.width = width;
            oc.height = height;
            octx.drawImage( image, 0, 0, oc.width, oc.height );

            var t1 = performance.now();

            console.log( "Call to resizeImageOnce took " + (t1 - t0) + " milliseconds." );

            return oc.toDataURL();
        }

        return null;
     };

     // 2-step canvas resize
     Wysiwyg.prototype.resizeImageStepped = function( image, width, height) {
        if ( image ) {
            var t0 = performance.now();
            var oc = document.createElement( "canvas" );
            var octx = oc.getContext( "2d" );
            var fc = document.createElement( "canvas" );
            var fctx = fc.getContext( "2d" );

            fc.width = width;
            fc.height = height;

            oc.width = image.width * 0.5;
            oc.height = image.height * 0.5;
            octx.drawImage( image, 0, 0, oc.width, oc.height );

            // step 2
            octx.drawImage( oc, 0, 0, oc.width * 0.5, oc.height * 0.5 );

            // copy to final canvas
            fctx.drawImage( oc, 0, 0, oc.width * 0.5, oc.height * 0.5, 0, 0, fc.width, fc.height );

            var t1 = performance.now();

            console.log( "Call to resizeImageStepped took " + (t1 - t0) + " milliseconds." );

            return fc.toDataURL();
        }

        return null;
     };

     // calculate and maintain aspect ratio
     Wysiwyg.prototype.calculateAspectRatioFit = function( srcWidth, srcHeight, maxWidth, maxHeight ) {
        var ratio = Math.min( maxWidth / srcWidth, maxHeight / srcHeight );

        return { width: srcWidth * ratio, height: srcHeight * ratio };
     };

     Wysiwyg.prototype.insertFiles = function( files, options, editor, toolbarBtnSelector ) {
        var self = this;
        editor.focus();
        $.each( files, function( idx, fileInfo ) {
            if ( /^image\//.test( fileInfo.type ) ) {
                $.when( self.readFileIntoDataUrl( fileInfo ) ).done( function( dataUrl ) {
                    // are image size restrictions in place?
                    if ( options.enableImageSizeRestrictions ) {                      
                        var img = new Image();

                        img.onload = function() {
                            // is this image larger than restrictions?
                            if ( ( options.imageMaxWidth > 0 && img.width > options.imageMaxWidth ) || ( options.imageMaxHeight > 0 && img.height > options.imageMaxHeight ) ) {
                                var newDimensions = self.calculateAspectRatioFit( img.width, img.height, options.imageMaxWidth, options.imageMaxHeight );

                                var resized = self.resizeImageOnce( img, newDimensions.width, newDimensions.height );

                                if ( resized ) {
                                    dataUrl = resized;
                                }                        
                            }

                            self.execCommand( "insertimage", dataUrl, editor, options, toolbarBtnSelector );
                            editor.trigger( "image-inserted" );                            
                        };
                        img.src = dataUrl;
                    }
                    else { // insert untouched image immediately
                        self.execCommand( "insertimage", dataUrl, editor, options, toolbarBtnSelector );
                        editor.trigger( "image-inserted" );
                    }                    
                } ).fail( function( e ) {
                    options.fileUploadError( "file-reader", e );
                } );
            } else {
                options.fileUploadError( "unsupported-file-type", fileInfo.type );
            }
        } );
     };
gbergeson commented 4 years ago

This doesn't appear to be in the current code. Did this ever get implemented?

Edit: I found a way to do it in https://github.com/steveathon/bootstrap-wysiwyg/issues/124, but it still isn't in the current code (maybe because it depends on another plugin). Will it ever get added?

spreadred commented 4 years ago

@gbergeson I implemented an image size restriction and automated resizing in my fork: https://github.com/kaptainkommie/bootstrap-wysiwyg

You might be able to use that code as starting point to do whatever it is you're trying to do. I never got around to trying to implement any dynamic, user-controlled resizing.

gbergeson commented 4 years ago

I got it worked out, actually, by using the code someone else posted in another issue here... can't remember exactly which one. Thanks, though!