views:

118

answers:

3

Using ImageMagick on the command line I can say

convert -background '#0000' -fill white -stroke black -strokewidth 3 -gravity center -pointsize 78 -size 568x1000 caption:'Lorem ipsum etc etc' -trim +repage out.png

And produce the output I'm looking for. What I'd like to do is the same thing but within PerlMagick so that I don't have to keep reading and writing files as I perform various other steps. Here's what I have so far

use strict;
use warnings;
use Image::Magick;

my $im = new Image::Magick;
my $e = $im->Set(
        background => '#0000',
        fill => 'white',
        stroke => 'black',
        strokewidth => 3,
        gravity => 'center',
        pointsize => 78,
        size => '586x1000',
);
die $e if $e;

$e = $im->Read("caption:Lorem ipsum etc etc");
die $e if $e;

$e = $im->Trim();
die $e if $e;

$e = $im->Set(page=>'0x0+0+0'); # +repage
die $e if $e;

$e = $im->Write('out.png');
die $e if $e;

And this works precisely the same way, except that the resulting text is not centered.

Documentation on PerlMagick is almost nonexistent. I based this "read caption" syntax on some MagicWand examples, where it is claimed that this will result in centered text. Clearly something is different for PerlMagick.

So, the question: How can I make PerlMagick respect gravity in this case? How do I get multi-line, centered and word-wrapped text via PerlMagick? Note that this requires that I use caption and not annotate or draw. I'd prefer to avoid manual per-line centering, but I would consider it.

Alternatively, if someone has a sample of doing word wrapping and with proportional fonts and Annotate then that would work for me.

EDIT: Please note that the -caption option to polaroid, though it shares implementation with what I'm doing, is not the same as the caption: pseudo-image. I would still accept an answer using polaroid and -caption if the output closely matches what is given by the example convert command above.

EDIT 2: Here's a more minimal example of the problem.

use strict;
use warnings;
use Image::Magick;

my $im = new Image::Magick;
my $e = $im->SetAttribute(
        background => '#0000',
        pointsize=>12,
        size => '100x100',
        gravity => 'center',
);
die $e if $e;
$e = $im->ReadImage('caption:The quick brown fox jumps over the lazy dog.');
die $e if $e;
$e = $im->Write('out.png');
die $e if $e;

Expected result: The text is centered.

Actual result: The text is left-justified.

Actual result should be identical to the output of this command:

convert -background '#0000' -size 100x100  -pointsize 12 -gravity center caption:'The quick brown fox jumps over the lazy dog.' out.png

From looking at the perlmagick source I see nothing that should be intercepting a particular SetAttribute call, so I don't see why gravity is being ignored. How can I get gravity to not be ignored for this? Or, how else can I do word wrapped and centered text with this kind of output?

A: 

This works on Ubuntu 10.04 for me. I only see documentation for "caption" working with the "polaroid" effect.

#!/usr/bin/perl

use strict;
use warnings;
use Image::Magick;

my $im = new Image::Magick;
$im->Set(size => '586x1000');

my $e = $im->ReadImage('xc:black');

$im->Polaroid(
        fill => 'white',
        stroke => 'black',
        strokewidth => 3,
        gravity => 'center',
        pointsize => 78,
        caption => "Lorem ipsum etc etc"
);

$e = $im->Trim();
die $e if $e;

$e = $im->Set(page=>'0x0+0+0'); # +repage
die $e if $e;

$e = $im->Write('out.png');
die $e if $e;

alt text

bob.faist
Good example code for PerlMagick here: http://www.imagemagick.org/source/examples.pl
bob.faist
Please post your exact imagemagick and perlmagick versions. You can find examples of caption being used like this here: http://www.imagemagick.org/Usage/text/#caption (please note that polaroid uses -caption, whereas this is the pseudo-image caption:) Also please note that I am going to be using this to apply text over top of an existing image which *must not* have the polaroid effect applied.
Sorpigal
Also, your resulting image using this technique is not sufficient. This particular step should result in an image with a transparent background which is precisely large enough to hold the rendered text (precisely like the results if you run the command line version given above).
Sorpigal
+1  A: 

How about using Annotate()?

use strict;
use warnings;
use Image::Magick;

my $im = Image::Magick->new();
$im->Set(size => "1000x568");
$im->ReadImage('xc:black');
$im->Annotate(text => "Lorem ipsum etc etc",
              gravity => "Center",
              fill => 'white',
              stroke => 'black',
              strokewidth => 3,
              pointsize => 78);
$im->Write('myout.png');

alt text

bob.faist
This is not word wrapped. Try adding more text until wrapping is required, or make the size 568 pixels wide as in my original example, and see what happens. FYI, according to the imagemagick docs you cannot wordwrap with Annotate or Draw, only caption.
Sorpigal
An answer involving Annotate or Draw would have to include code to slice up the input string and wrap where appropriate, taking in to account the proportional font. This is possible (QueryFontMetrics) but difficult, especially if you also need to "best fit" into a predefined box while minimizing number of lines of text and maximizing font size as I do.
Sorpigal
+1  A: 

Version: ImageMagick 6.5.7-8

Closer but still does not center horizontally, just vertically. Kinda running out of ideas...

#!/usr/bin/perl

use strict;
use warnings;
use Image::Magick;

my $t = Image::Magick->new();
my $b = Image::Magick->new();
$t->SetAttribute(pointsize=>12, size => '100x50', background=>'transparent');
$b->SetAttribute(size => '100x100');
$t->ReadImage('caption:The quick brown fox jumps over the lazy dog.');
$b->ReadImage('xc:transparent');
$b->Composite(image => $t, gravity=>'center', compose=>'over');
$b->Write('out.png');

alt text

bob.faist
My original example gets me this far as well. Here's the issue: In Magick.xs when parsing the "gravity" option there's a loop like this: `for( ; image ; image = image->next ) image->gravity = (MagickGravity)sp;` (note that the exact variable names may be a bit off since this is from memory). So as you see it sets gravity for each image. But, there is no image at this time (image is NULL) and when ReadImage creates a *new* image it obviously does not have this gravity value. This is what I learned looking at the imagemagick source yesterday.
Sorpigal
I should perhaps mention that when you Read the caption: image type it immediately renders the text with what options are available and doesn't seem to wait for e.g. a composite step. That's the reason "gravity" doesn't work the way you used it here: the text is already rendered left-justified.
Sorpigal
Seems that both convert on the command line and the C API for this go though the wand system, which is different and actually does what you would expect. It strangely also seems like caption.c's ReadCAPTIONImage is never hit if you're running convert(1) which is a bit mystifying (I'm still working on that one).
Sorpigal
I'm upvoting and accepting this one even though it's not exactly the solution. Through my investigation I have discovered that there actually is no native solution due to deficiencies in PerlMagick. You did come as close as anyone is likely to while staying with a pure PerlMagick solution (and not patching imagemagick first).
Sorpigal