PerlMagick Chokes on ICO File? - perl

I've been working on a script that retrieves favicons from sites, which is now mostly working, but I've run into a huge roadblock. When I call on the ImageMagick module in Perl, it doesn't seem to know what to do with the venerable favicon.ico file (everything is working great when a site has a non-ICO favicon). I can find lots of information on converting to ICO to create a favicon, but not much about converting from ICO.
After I retrieve the favicon, I use PerlMagick's ping function to figure out what kind of file I'm dealing with (so I'm not dependent on the icon's server to report accurately):
use Image::Magick;
my $im = Image::Magick->new();
my ($width, $height, $size, $format) = $im->Ping( $saveFile );
When the file is an ICO file, $format comes back empty (the server I'm requesting it from reports it as image/x-icon). I also have a little subroutine that creates JPEG thumbnails of everything I download. It works great on non-ICO files, but ImageMagick creates a blank file when converting from an ICO:
open my $file, $params->{'openFile'};
my $imageData = do { local $/; <$file> };
my $image = Image::Magick->new;
$image->BlobToImage($imageData);
$image->SetAttribute(quality => 80);
$image->SetAttribute(compression => 'JPEG');
$image->SetAttribute(geometry => $thumbnailWidth . "x" . $thumbnailHeight);
$image->Thumbnail();
my $thumbnailData = $image->ImageToBlob();
open(my $file, '>', $params->{'saveFile'}) or die "Could not open file '" . $params->{'saveFile'} . "'.";
print $file $thumbnailData;
close $file;
Do I need to somehow coax ImageMagick into recognize the file? I've been saving the favicons I download and the initial file is a valid ICO, even though ImageMagick won't recognize it.
Update: Here is a link to one of the ico files that is acting up. All the ico files I've tried have acted up, however.
If I try the command line ImageMagick convert tool, here is the result:
[root#local favicons]# convert 1299 1299-jpg.jpg
convert: no decode delegate for this image format `' # error/constitute.c/ReadImage/564.
convert: no images defined `1299-jpg.jpg' # error/convert.c/ConvertImageCommand/3235.

Based on #MarkSetchell's comments, I can add code to deal with the issue laid out above. I had been depending on PerlMagick's ping function to determine the file type, hoping to avoid possible bad information from a server I connect to. What I've done now is examine the Content-type header if ImageMagick cannot determine the file type and return it in $format:
if ((! $format) and (($mech->content_type() eq "image/x-icon") or ($mech->content_type() eq "image/vnd.microsoft.icon"))) {
$format = "ICO";
}
I then manually pass along the ICO format to ImageMagick before giving it the file blob:
my %imParam;
%imParam = ( 'magick' => 'ico' ) if ($params->{'format'} eq "ICO");
my $image = Image::Magick->new( %imParam );
This seems to be working so far. Thankfully on GIF, PNG, SVG and JPEG, ImageMagick is working fine on its own, which is even better, since I'd rather trust ImageMagick than the remote server's headers.

Related

Uploading large files with WebService::Dropbox

Can someone here give me some example code for using the
WebService::Dropbox module
to upload files bigger than 1GB?
I followed the instructions and successfully uploaded files less than 150MB but I don't understand how to upload larger files.
The
module documentation
says this about the update method
Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start.
And this is presumably why you have mentioned 150MB in your question.
The documentation for upload_session has this
Uploads large files by upload_session API
# File Handle
my $content = IO::File->new('./mysql.dump', '<');
my $result = $dropbox->upload_session($path, $content);
my $result = $dropbox->upload_session($path, $content, {
mode => 'add',
autorename => JSON::true,
mute => JSON::false
});
Note that, just like the documentation for upload, those two examples of calling upload_session are alternatives, and you should choose the second only if you have special requirements that require non-default option values
There is also no need to use IO::File to open a file: the standard Perl open call will work fine, and you should add a :raw layer whether you are using IO::File or not, like this
open my $content, '<:raw', './mysql.dump' or die $!
There is also no need for JSON::true and JSON::false: a simple 1 and 0 will do fine
This is pretty much identical to the upload use case, which you say you have working fine. What exactly are you having problems with?

perl output image from a remote source

So I am trying to use a script to simply proxy some images through it. the images are hosted on a remote server so I need to download the image and display it. This is the code I have so far:
binmode STDOUT;
print "Content-type: image/jpeg\n";
#DB commands to find the image
my $file = $image_folder."/".$file_real."0000.jpg";
print $file;
my $html0 = get($file);
print $html0;
For some reason this does not work, when I change the header to html and print html0 it shows the data but when I change the header to jpeg or png it fails to compile!
Need two return characters in the header:
print "Content-type: image/jpeg\n\n";
Also, should be sure that the file is loaded in binmode as well.

i am not getting kannada text when i run the perl script on a file

i am having following code for extracting the text from the html files and writing to a text file. in html it contain kannada text(utf-8) when programs runs i am getting a text file in that i am getting text but its not in proper formate. text is in unreadable formate
enter code here
use utf8;
use HTML::FormatText;
my $string = HTML::FormatText->format_file(
'a.html',
leftmargin => 0, rightmargin => 50
);
open mm,">t1.txt";
print mm "$string";
so please do help me.how to handle the file formates while we are processing it.
If I understand you correctly, you want the output file to be UTF-8 encoded so that the characters from the Kannada language are encoded in the output correctly. Your code is probably trying (and failing) to encode incorrectly into ISO-8859-1 instead.
If so, then what you can do is make sure your file is opened with a UTF-8 encoding filter.
use HTML::FormatText;
open my $htmlfh, '<:encoding(UTF-8)', 'a.html' or die "cannot open a.html: $!";
my $content = do { local $/; <$htmlfh> }; # read all content from file
close $htmlfh;
my $string = HTML::FormatText->format_string(
$content,
leftmargin => 0, rightmargin => 50
);
open my $mm, '>:encoding(UTF-8)', 't1.txt' or die "cannot open t1.txt: $!";
print $mm $string;
For further reading, I recommend checking out these docs:
perlunitut
perlunifaq
perlunicode
A few other notes:
The use utf8 line only makes it so that your Perl script/library may contain UTF formatting. It does not make any changes to how you read or write files.
Avoid using two-argument forms of open() like in your example. It may allow a malicious user to compromise your system in certain cases. (Though, your usage in this example happens to safe.
When opening a file, you need to add an or die afterwards or failures to read or write the file will be silently ignored.
Update 3/12: I changed it to read the file in UTF-8 and send that to HTML::FormatText. If your a.html file is saved with a BOM character at the start, it may have done the right thing anyway, but this should make it always assume UTF-8 for the incoming file.

PERL CGI: Filehandle that uses both binary and text mode

I have a perl program that writes application/zip document with binary data. I can do the following code in my cgi script.
print $cgi->header(-type=>"application/x-zip; name=\"$filename.zip\"", -content_disposition=> "attachment; filename=\"$filename.zip\""). "\n";
print $result
where $result is the binary data. This will then output a page that prompts the user to download the zip
What I want to do though is pass that entire 'webpage' as form parameter, so I did this:
open $resultfh, ">", \$output_buffer or die "could not open buffer";
print $resultfh $cgi->header(-type=>"application/x-zip; name=\"$filename.zip\"", -content_disposition=> "attachment; filename=\"$filename.zip\""). "\n";
print $resultfh $result
and then I can pass the $output_buffer around as variable.
The problem is that this doesn't work, something seems to get passed because I'm prompted to download the zipfile, but the zipfile is corrupted, I get a mismatch between the expected bytes and the actual bytes or something.
I think this has to do with that output buffer not being in binary mode, but I can't read the content header in binary mode, so can I have a file handle be partially in text and partially in binary?
If not, what options do I have?
EDIT: The problem actually seems to happen when I pass the binary data as a cgi form param. Anyone know what the problem might be? Maybe a size limit?
Set the filehandle to use binary. When you need to print something that you know is "text", use the appropriate end-of-line sequence explicitly. For example, for data that will be processed on Windows:
binmode $handle;
print $handle $some_text, "\r\n";
print $handle $some_binary_data;

Why do my images get clipped when served by this Perl CGI script?

When I try to print an image to STDOUT in a Perl CGI script, the image gets clipped when viewed in the browser.
Here is the following code:
if ($path =~ m/\.jpe?g$/i)
{
my $length = (stat($path))[7];
$| = 1;
print "Content-type: image/jpg\r\n";
print "Content-length: $length\r\n\r\n";
open(IMAGE,"<$path");
binmode(IMAGE);
binmode(STDOUT);
my ($image, $buff);
read IMAGE, $buff, $length;
syswrite STDOUT, $buff, $length;
close IMAGE;
}
If you really want to read the entire file into memory before serving, use File::Slurp:
#!/usr/bin/perl
use strict; use warnings;
use CGI::Simple;
use File::Slurp;
use File::stat;
local $| = 1;
my $cgi = CGI::Simple->new;
my $st = stat($path) or die "Cannot stat '$path'";
print $cgi->header(
-type => 'image/jpeg',
-length => $st->size,
);
write_file(\*STDOUT, {binmode => ':raw'},
\ read_file( $path, binmode => ':raw' )
);
However, reading the entire file will consume large amounts of memory for large images. Therefore, see How can I serve an image with a Perl CGI script?.
EDIT: as the stat doesn't seem to be problem, some more ideas:
try using unbuffered instead of buffered reading, ie. use sysread instead of read. or the other way round: use both buffered read and write. also, try commenting out the $|. see Suffering from Buffering? for details on perl buffered io. see also How can I serve an image with a Perl CGI script? here on SO for an apparently working solution. EDIT END
you are using the wrong stat field. (stat($path))[10] is ctime: inode change time in seconds since the epoch. it should be (stat($path))[7], size: total size of file, in bytes.
FYI: I have come to the conclusion that the images are in fact corrupt, though they are fully viewable in Windows File Explorer.
The FireFox browser shows the Images clipped(no matter how they are accessed, so I guess this is no longer a Perl problem), but the Safari Browser displays them completely.
The images were re sampled from using Java's imageIO in "jpg" mode. I just changed the mode to "png", and now the newly generated images are showing perfectly in all browsers. So this was actually a Java imageIO issue.
It is solved.
Thank you everyone for your responses.