1 package Imager::Font::Wrap;
9 *_first = \&Imager::Font::_first;
11 # we can't accept the utf8 parameter, too hard at this level
13 # the %state contains:
16 # x - the left position
18 # justify - fill, left, right or center
21 my ($state, $spaces, $text, $fill) = @_;
24 my $box = $state->{font}->bounding_box(string=>$text,
25 size=>$state->{size});
27 my $y = $state->{linepos} + $box->global_ascent;
30 && $state->{linepos} + $box->font_height > $state->{bottom}) {
35 if ($text =~ /\S/ && $state->{im}) {
36 my $justify = $fill ? $state->{justify} :
37 $state->{justify} eq 'fill' ? 'left' : $state->{justify};
38 if ($justify ne 'fill') {
40 if ($justify eq 'right') {
41 $x += $state->{w} - $box->advance_width;
43 elsif ($justify eq 'center') {
44 $x += ($state->{w} - $box->advance_width) / 2;
46 $state->{font}->draw(image=>$state->{im}, string=>$text,
48 size=>$state->{size}, %{$state->{input}});
51 (my $nospaces = $text) =~ tr/ //d;
52 my $nospace_bbox = $state->{font}->bounding_box(string=>$nospaces,
53 size=>$state->{size});
54 my $gap = $state->{w} - $nospace_bbox->advance_width;
56 $spaces = $text =~ tr/ / /;
57 while (length $text) {
58 if ($text =~ s/^(\S+)//) {
60 my $bbox = $state->{font}->bounding_box(string=>$word,
61 size=>$state->{size});
62 $state->{font}->draw(image=>$state->{im}, string=>$1,
64 size=>$state->{size}, %{$state->{input}});
65 $x += $bbox->advance_width;
67 elsif ($text =~ s/^( +)//) {
69 my $advance = int($gap * length($sep) / $spaces);
70 $spaces -= length $sep;
75 die "This shouldn't happen\n";
80 $state->{linepos} += $box->font_height + $state->{linegap};
89 # try to get something useful
90 my $x = _first(delete $input{'x'}, 0);
91 my $y = _first(delete $input{'y'}, 0);
93 or return Imager->_set_error('No image parameter supplied');
94 my $im = delete $input{image};
95 my $imerr = $im || 'Imager';
96 my $width = delete $input{width};
97 if (!defined $width) {
98 defined $im && $im->getwidth > $x
99 or return $imerr->_set_error("No width supplied and can't guess");
100 $width = $im->getwidth - $x;
102 my $font = delete $input{font}
103 or return $imerr->_set_error("No font parameter supplied");
104 my $size = _first(delete $input{size}, $font->{size});
106 or return $imerr->_set_error("No font size supplied");
109 or return $imerr->_set_error("Width too small for font size");
111 my $text = delete $input{string};
113 or return $imerr->_set_error("No string parameter supplied");
115 my $justify = _first($input{justify}, "left");
128 linegap => delete $input{linegap} || 0,
130 $state{height} = delete $input{height};
131 if ($state{height}) {
132 $state{bottom} = $y + $state{height};
138 pos($text) = 0; # avoid a warning
139 while (pos($text) < length($text)) {
140 #print pos($text), "\n";
141 if ($text =~ /\G( +)/gc) {
144 $spaces += length($1);
146 elsif ($text =~ /\G(?:\x0D\x0A?|\x0A\x0D?)/gc) {
148 _format_line(\%state, $spaces, $line, 0)
152 $linepos = pos($text);
154 elsif ($text =~ /\G(\S+)/gc) {
157 my $bbox = $font->bounding_box(string=>$line . $word, size=>$size);
158 if ($bbox->advance_width > $width) {
159 _format_line(\%state, $spaces, $line, 1)
163 $linepos = pos($text) - length($word);
166 # check for long words
167 $bbox = $font->bounding_box(string=>$line, size=>$size);
168 while ($bbox->advance_width > $width) {
169 my $len = length($line) - 1;
170 $bbox = $font->bounding_box(string=>substr($line, 0, $len),
172 while ($bbox->advance_width > $width) {
174 $bbox = $font->bounding_box(string=>substr($line, 0, $len),
177 _format_line(\%state, 0, substr($line, 0, $len), 0)
179 $line = substr($line, $len);
180 $bbox = $font->bounding_box(string=>$line, size=>$size);
181 $linepos = pos($text) - length($line);
184 elsif ($text =~ /\G\s/gc) {
185 # skip a single unrecognized whitespace char
187 $linepos = pos($text);
191 if (length $line && !$state{full}) {
192 $linepos += length $line
193 if _format_line(\%state, 0, $line, 0);
196 if ($input{savepos}) {
197 ${$input{savepos}} = $linepos;
200 return ($x, $y, $x+$width, $state{linepos});
209 Imager::Font::Wrap - simple wrapped text output
213 use Imager::Font::Wrap;
215 my $img = Imager->new(xsize=>$xsize, ysize=>$ysize);
217 my $font = Imager::Font->new(file=>$fontfile);
219 my $string = "..."; # text with or without newlines
221 Imager::Font::Wrap->wrap_text( image => $img,
231 This is a simple text wrapper with options to control the layout of
232 text within the line.
234 You can control the position, width and height of the text with the
235 C<image>, C<x>, C<y>, C<width> and C<height> options.
237 You can simply calculate space usage by setting C<image> to C<undef>,
238 or set C<savepos> to see how much text can fit within the given
245 Draw word-wrapped text.
251 C<x>, C<y> - The top-left corner of the rectangle the text is
252 formatted into. Defaults to (0, 0).
256 C<width> - The width of the formatted text in pixels. Defaults to the
257 horizontal gap between the top-left corner and the right edge of the
258 image. If no image is supplied then this is required.
262 C<height> - The maximum height of the formatted text in pixels. Not
267 C<savepos> - The amount of text consumed (as a count of characters)
268 will be stored into the scalar this refers to.
275 while (length $string) {
276 my $img = Imager->new(xsize=>$xsize, ysize=>$ysize);
277 Imager::Font::Wrap->wrap_text(string=>$string, font=>$font,
278 image=>$img, savepos => \$savepos)
281 or die "Could not fit any text on page\n";
282 $string = substr($string, $savepos);
283 $img->write(file=>"page$pagenum.ppm");
288 C<image> - The image to render the text to. Can be supplied as
289 C<undef> to simply calculate the bounding box.
293 C<font> - The font used to render the text. Required.
297 C<size> - The size to render the font in. Defaults to the size stored
298 in the font object. Required if it isn't stored in the font object.
302 C<string> - The text to render. This can contain non-white-space,
303 blanks (ASCII 0x20), and newlines.
305 Newlines must match /(?:\x0A\x0D?|\x0D\x0A?)/. White-space other than
306 blanks and newlines are completely ignored.
312 The way text is formatted within each line. Possible values include:
318 C<left> - left aligned against the left edge of the text box.
322 C<right> - right aligned against the right edge of the text box.
326 C<center> - centered horizontally in the text box.
330 fill - all but the final line of the paragraph has spaces expanded so
331 that the line fills from the left to the right edge of the text box.
337 C<linegap> - Gap between lines of text in pixels. This is in addition
338 to the size from C<< $font->font_height >>. Can be positive or
343 Any other parameters are passed onto Imager::Font->draw().
347 ($left, $top, $right, $bottom)
349 which are the bounds of the space used to layout the text.
351 If C<height> is set then this is the space used within that height.
353 You can use this to calculate the space required to format the text
356 my ($left, $top, $right, $bottom) =
357 Imager::Font::Wrap->wrap_text(string => $string,
360 my $img = Imager->new(xsize=>$xsize, ysize=>$bottom);
361 Imager::Font::Wrap->wrap_text(string => $string,
370 Imager::Font can handle UTF-8 encoded text itself, but this module
371 doesn't support that (and probably won't). This could probably be
372 done with regex magic.
374 Currently ignores the C<sizew> parameter, if you supply one it will be
375 supplied to the draw() function and the text will be too short or too
376 long for the C<width>.
378 Uses a simplistic text model, which is why there's no hyphenation, and
383 Tony Cook <tony@develop-help.com>
387 Imager(3), Imager::Font(3)