Hi all,

First post here, so I am not sure I am in the right forum. But let's go, it's a code contribution ;)

I use a lot of flexinodes, and don't want to use Fleximax to be able to use the full power of PHPTemplate. However, the image field in the Flexinode has a big drawback: it doesn't resize images. You can choose a max widthxheight, but in fact, the Flexinode will refuse the upload if the image is bigger. Very confusing for users, who often don;t know how to rescale an image on their computer.

The Fleximax seem to resize images, but is yet to be cleaned out apparently (http://drupal.org/node/30376).

And I don't understand how this thread answers the question: http://drupal.org/node/42399

Anyway, the fix is simple. Just edit field_image.inc, and instead of

        return t('The uploaded image is too large; the maximum dimensions are %nxn pixels.', array('%nxn' => $field->options[1]));

put:

        $_imagick_convert = variable_get('flexinode_convert_path', '/usr/local/bin/convert');
        $filter = ' -filter QUADRATIC -scale '. $field->options[1];
        $path = $file->filepath;
        $name = $file->filename;
        $path_only = str_replace($name, '', $path);
        $name_alt = 'alt_'. $name;      
        $path_alt = $path_only . $name_alt;      
        
        exec("$_imagick_convert $filter $path $path_alt");
        @unlink($file->filepath);
        @copy($path_alt, $file->filepath);
        $file->filesize = @filesize($file->filepath);

Then use your flexinode image fields as usual. The image will be automatically resized if one of its dimension is bigger than what you set in the image field property. The aspect ratio will be kept, etc...

I hope this is not redundant, but I couldn't find such fix in the drupal forums.

Comments

verot’s picture

I forgot to clean the file names before the exec() call...

        $_imagick_convert = variable_get('flexinode_convert_path', '/usr/local/bin/convert');
        $filter = ' -filter QUADRATIC -scale '. $field->options[1];
        $path = $file->filepath;
        $name = $file->filename;
        $path_only = str_replace($name, '', $path);
        $name_alt = 'alt_'. $name;      
        $path_alt = $path_only . $name_alt;      
        $_path = flexinode_image_escape_shell($path);
        $_path_alt = flexinode_image_escape_shell($path_alt);
        exec("$_imagick_convert $filter $_path $_path_alt");
        unlink($file->filepath);
        copy($path_alt, $file->filepath);
        $file->filesize = @filesize($file->filepath);
the_undefined’s picture

Hi verot,

I don't like the exec() command, too much since it isn't available on most shared hosting solutions (at least the ones I've been working with). So the best way of resizing images for most people will be using the GD Library which is mostly available.

If anyone out there would like to do what verot did with his flexinodes but doesn't have access to "exec()" just send me an e-mail at:

felixge [at] web [dot] de

And I'll send you some code of mine which should do the job just as well.

bye, Felix

verot’s picture

I chose to use exec() because that's what was already used in the image field code (which bit of code, if I am not mistaken was'nt actually used).

Also, ImageMagick is used in the image module, with exec(), although probably a bit more securely. But my fix is only a quick fix ;)

To make it safe, add this line before the exec() call:

$filter = preg_replace("/[^A-Za-z0-9\.\-\+\040]/", '', $filter);

This said, I picked up Drupal one week ago, so I might have missed something...

PS: Can you post the code for GD?

the_undefined’s picture

Hi everyone,

I couldn't test my code since I'm not using Flexinode right now but I think it should work with little to none changes on it.

I posted it right here:
http://cakephp.org/pastes/show/211d3147cd971703ee16285bbae26cc5

potential’s picture

Thank you both for working on this functionality. I was the originator of that other thread... looking for a solution. I am so happy to find that you have been trying here!

I implemented the GD code posted by the_undefined into my site and here is the problem:

  • the size of the originally uploaded picture is 2048x1536 @ 1MB
  • the size of the resized picture (located in the files directory) is 2048x1536 @ 474kb

Looks to me like the image was downsized in terms of file size/quality, but not resolution. Just so you know, my flexinode field max sizes are set to 400x400 @ 1.5MB.

The GD code linked to above (what I am using) is posted below, just in case that link is not available in the future.

Please modify the code to resize properly or let me know if I am doing something wrong. Thanks for the help!


/*
I've written the code for the image class 2 1/2 years ago, I know it's not that nice to look at today ; ).

This code should be used instead of the one verot posted in order to avoid using exec():
*/
 
<?php
            $image = &new class_image($file->filepath);
            $image->fit_in_box($size[0], $size[1]);
            $image->save_jpg($file->filepath, 90);
            $file->filesize = @filesize($file->filepath);
?>
 
 
/*
Here is the class that you have to include or copy into the PHP file in order to make things work.
*/
<?php
    class class_image
    {      
        var $image;                         # Das Bild der Klasse
        var $height;
        var $width;
    
        function class_image($picture = NULL)
        {      
            if ($picture) 
            {
                $this->image = $this->load_image($picture);
                $this->set_infos($picture);
            }
        }
        
        function save_jpg($file, $quality = 90)
        {
            imagejpeg($this->image, $file, $quality);      
        }
        
        function fit_in_box($width, $height)
        {      
            $old_factor = $this->width / $this->height;
            $new_factor = $width / $height;
            
            if ($old_factor<$new_factor)
            {
                $new_height = $height;
                $new_width = $new_height * $old_factor;
            }
            if ($old_factor>$new_factor)
            {
                $new_width = $width;
                $new_height = $new_width / $old_factor;    
            }         
            if ($old_factor==$new_factor)
            {
                $new_height = $height;
                $new_width = $width;
            }                  
 
            $this->resize_image($new_width, $new_height);
        }
        
        function resize_image($new_width, $new_height)
        {
            $new_image = imagecreatetruecolor($new_width, $new_height);   
            imagecopyresized($new_image, $this->image, 0, 0, 0, 0,
                              $new_width, $new_height, $this->width, $this->height);            
            $this->image = $new_image;          
            $this->width = $new_width;
            $this->height = $new_height;
        }
        
        function set_infos($file)
        {
            $size = getimagesize($file);
            $this->width = $size[0];
            $this->height = $size[1];                  
        }
        
        function load_image($picture)
        {
            $extension = $this->get_extension($picture);
            
            switch ($extension)
            {
                case "jpg";
                    $image = imagecreatefromjpeg($picture);
                    break;
                case "gif";
                    $image = imagecreatefromgif($picture);
                    break;
                case "png";
                    $image = imagecreatefrompng($picture);
                    break;
            }
                                    
            return $image;
        }
        
        function get_extension ($name)
        {
            $tmp_arr = explode (".", $name);
            $str_output = $tmp_arr[count($tmp_arr)-1];
            $str_output = strtolower ($str_output);
            if ($str_output == "jpeg") $str_output = "jpg";
            return $str_output;
        }   
    }
?>
the_undefined’s picture

My bad,

I must have been slightly drunk writing the code above. This is how it should be (and work).

        	$image = &new class_image($file->filepath);
        	$image->fit_in_box($maxwidth, $maxheight);
        	$image->save_jpg($file->filepath, 90);
        	$file->filesize = @filesize($file->filepath);        	

If you don't want to loose quality then you should set

        	$image->save_jpg($file->filepath, 100);

And/Or if you want the downscaled images to look smoother you can replace the function:

		function resize_image($new_width, $new_height)
		{
			$new_image = imagecreatetruecolor($new_width, $new_height);			
			imagecopyresized($new_image, $this->image, 0, 0, 0, 0,
							  $new_width, $new_height, $this->width, $this->height);			
			$this->image = $new_image;					
			$this->width = $new_width;
			$this->height = $new_height;
		}

with

		function resize_image($new_width, $new_height)
		{
			$new_image = imagecreatetruecolor($new_width, $new_height);			
			imagecopyresampled($new_image, $this->image, 0, 0, 0, 0,
							  $new_width, $new_height, $this->width, $this->height);			
			$this->image = $new_image;					
			$this->width = $new_width;
			$this->height = $new_height;
		}

I hope this is working now.
Felix

verot’s picture

Note that imagecopyresampled() is available only in recent versions of GD.

You will have to use imagecopyresized() and imagecreate() if you have a GD version <2 (from memory)

potential’s picture

Felix and Verot,

It's working great now. Outstanding job. Thanks to you both.

I was thinking it would be nice to have the image resized into two sizes. The teaser can display the thumbnail (ie. 200x200) and the node can display a larger size (ie. 600x600).

One way to do this would be to add a second flexinode image field, and have the one limited to 200px and the other 600px. Then, I could specify which belongs in the teaser and which in the full display. It would be redundent however, to select the same picture twice when creating a new node.

Is it possible to modify your code to resize the image twice and assign each image to the respective flexinode field? Then I would have to figure out how not to show the second image upload box when creating the node.

You can probably tell I am new to PHP and web design in general and very greatful for all the help you are providing.

Thanks!!

verot’s picture

In order to have the image resized in two sizes, like the image module, the flexinode image field has to be changed to generates two files from one upload, and be able to deliver both at display time.

However, that requires a fair bit of changes, like adding configuration options for this field, such as sizes, etc... and also possible options for the resizing (ratio, quality...)

It's definitively possible.

scroogie’s picture

Well, as far as i know it already does that. See in flexinode_field_image_insert() those lines:

$node->$fieldname = file_save_upload($node->$fieldname, $node->$fieldname->filename);
$node->$fieldname->smallpath = flexinode_field_image_make_smaller($node->$fieldname->filepath, '_sm', $field->options[2]);
$node->$fieldname->thumbpath = flexinode_field_image_make_smaller($node->$fieldname->filepath, '_th', $field->options[3]);

So after the upload there are three versions of the file.

monjohn’s picture

I would also be interested in putting the thumbnail in the teaser. If anyone gets this to work with the image files created, could you post the code?

scroogie’s picture

As I said, the thumbnails are already there, you can access them in the node-flexinode-x.tpl.php with

print $node->flexinode_1->smallpath
and
print $node->flexinode_1->thumbpath

potential’s picture

Hi Scroogie,

This code exists only in the flexinode module for drupal 4.7. I don't think it can be used for 4.6. Am I correct? If so, is there an easy way to incorporate this code into the 4.6 flexinode?

Also, if my site accumulates alot of content using the modified 4.6 flexinode module, is it going to be difficult to upgrade to 4.7?

ngstigator’s picture

Does this code belong in field_image.inc?

Thanks for all the great work everyone has put in! To me, this seems to be functionality that should be included in flexinode.

Cosmos-1’s picture

it appears to be using poor re-sizing techniques... is it possible to use Smart resizing instead of pixel resizing? to avoid wavy lines on edges.

patchak’s picture

a

tott’s picture

Thanks for the help!

ddegner’s picture

Man, this is exatctly what I have been looking for, an easy way to resize images as the come up. I applied it and got it to work but I ran into a problem. It seems to strip the IPTC and EXIF information from the JPEGs. I store my caption information in there and need to keep it. This is a pretty specialized question, but searching around the web hasn't given me any clues, how do I resize without stripping away the IPTC and EXIF?

Thanks,

-------------------------------------------------------
Jump through a few hoops, get a free iPod.
http://ipods.freepay.com/?r=8480908

scd67’s picture

DannyV: To make this work in 4.6, do this:

below

  $node->$fieldname = file_save_upload($node->$fieldname, $node->$fieldname->filename);

put

  $node->$fieldname->smallpath = flexinode_field_image_make_smaller($node->$fieldname->filepath, '_sm', '200x200');
  $node->$fieldname->thumbpath = flexinode_field_image_make_smaller($node->$fieldname->filepath, '_th', '100x100');

changing the dimensions at the end to whatever you want them to be.

Add this function just below (copied from 4.7 version)

function flexinode_field_image_make_smaller($path, $add, $size) {
    list($width, $height) = explode('x', $size);
    $dest_path = preg_replace('!(\.[^/.]+?)?$!', "$add\\1", $path, 1); // Add $add before extension. Works if there's no extension and with other stupid cases ;)
    
    if ($size = getimagesize($path)) {
      if ($size[0] > $width or $size[1] > $height) {
        image_scale($path, $dest_path, $width, $height);
        return $dest_path;
      }
      return $path;
    }
    return NULL;
}

and below

    file_delete($file->filepath);

add

	file_delete($file->smallpath);
	file_delete($file->thumbpath);

That should do it.

Chris_five: The first piece of the php code from DannyV's post above goes in field_image.inc and the second goes into flexinode.module (at least, that's how it's working for me)

thomjjames’s picture

thats brilliant! thanks everyone!

i've been having a few problems with some clients not resizing images prior to uploading and this has really helped!

cheers
Tom

______________________________________________
https://tomswebstuff.com

Anonymous’s picture

HI !
Is there a way to link automaticaly the image to the full flexinode ?
Thanks ;)

Anonymous’s picture

HI!
It's me again.
True. Really nice job ;)

But I wonder how both thumbnail and big image ( I use two images fields ) could be automaticaly croped to have square images ?

Could I use this ? http://drupal.org/node/21811 And if it's possible, how and where do I have to put it please ?

B.T.W, If I'm right, the image is uploaded and become three different files ? So if I use two images fields to upload the same image at different sizes, I'll have six files ? :-/ How could I do to solve that ?

Well... Thank you , one more time :)

Anonymous’s picture

Hi !
I found the solution to link the teaser image to the full node. Was so easy.... :-/

For those who could be interested, just create a node-flexinode-x.tpl.php file to control you flexinode layout (x = your flexi Id) http://drupal.org/node/45475 and play with the variables $node_url : Link to node.

http://drupal.org/node/46012

So this is ok but I'm still looking for a solution for the square images (up) Furthermore, the script should in this case, not display an error message as "The image is too large" but "The image isn't large enough" and then if it's large enough, proceed to the size change with the GD script.

Ok guys... :)
Take it easy.
Bye.

patchak’s picture

Hi there,

The code above works well.. Except when the image file size exceeds someting like 500k.

The image module settings accept any file size and the flexynode configuration for the image field is set at 10mb.

But still, seems that the program is not able to handle big images, that exceed 500ko.

Any ideas where to look at?

Thanks

flunardelli’s picture

try this in flexinode field_image.inc (flexinode_field_image_validate):

else if ($size[0] > $maxwidth || $size[1] > $maxheight) {
            $path = $file->filepath;
            $name = $file->filename;
            $path_only = str_replace($name, '', $path);
            $name_alt = 'alt_'. $name;
            $path_alt = $path_only . $name_alt;
            if (image_scale($path, $path_alt, $maxwidth, $maxheight)) {
                @unlink($file->filepath);
                @copy($path_alt, $file->filepath);
                $file->filesize = @filesize($file->filepath);
            } else {
                form_set_error($fieldname, t('The uploaded image is too large; the maximum dimensions are %nxn pixels.', array('%nxn' => $field->options[1])));
            }
      }

The function image_scale exist in api documentation .... http://api.drupal.org/api/HEAD/function/image_scale

One more time ... thks drupal ;)