Image-BoxModel
view release on metacpan or search on metacpan
lib/Image/BoxModel.pm view on Meta::CPAN
package Image::BoxModel;
use 5.006000;
use warnings;
use strict;
our $VERSION = '0.50';
use Carp;
use Image::BoxModel::Lowlevel; #Lowlevel methods like boxes, text, graphic primitives
use Image::BoxModel::Text; #Automatically makes a fitting box and puts text on it. Uses Lowlevel methods
our @ISA = ("Image::BoxModel::Text", "Image::BoxModel::Lowlevel");
sub new{
my $class = shift;
#Define a new image an preset some values before..
my $image ={
width => 400,
height => 300,
background => 'white',
lib=> 'GD', #IM or GD ;-)
PI => 3.14159265358979,
verbose => "0", #if we print many messages about what the programs do.
#.. adding the users parameters
@_
};
$image->{height}--; #Because a picture of 400 pixels height ranges from 0-399
$image->{width}--;
$image->{free} = { #This is the standard-box. It will shrink when a new box is added. Now it fills the whole background.
top => 0,
bottom => $image->{height},
height => $image -> {height},
left => 0,
right => $image->{width},
width => $image->{width}
};
# The preset Colors (see below are read in). This is not done with DATA on purpose.
# I had some strange errors when using Image::BoxModel while open Filehandles in calling programs
%{$image->{preset_colors}} = PresetColors();
#Now follow the definitions of the backend-libraries
#Inheritance is granted by using the appropriate backend-modules.
#This means that if GD is used, then the image-object has ::Backend::GD as its parent, so therefore the appropriate methods in ::Backend::GD are found.
#I don't know if this is good software design..
if ($image -> {lib} eq "IM"){
require Image::Magick;
require Image::BoxModel::Backend::IM;
push @ISA, "Image::BoxModel::Backend::IM";
$image->{IM} = new Image::Magick;
$image->{IM} -> Set(size => ($image->{width}+1)."x".($image->{height}+1)); #IM calculates "human-style" 800x400 is from 0 to 799 and 0 to 399 :-) we do width-- and height-- because we don't do human style in this module.
$image->{IM} -> Read("xc:$image->{background}");
}
elsif ($image -> {lib} eq "GD"){
require GD;
require Image::BoxModel::Backend::GD;
push @ISA, "Image::BoxModel::Backend::GD";
$image->{GD} = new GD::Image($image->{width}+1,$image->{height}+1);
$image->{colors}{'#ffffff'} = $image->{GD}->colorAllocate(255,255,255); #allocate white to ensure white background. This is perhaps not a clever move, but otherwise it will be drawn with the first color allocated, which quite surely is seldom desir...
#Fontconfig should not be enabled by default, even it is tempting to do so ;-). The examples will presumably produce many errors if fontconfig can't be enabled.
if (exists $image->{fontconfig} and $image->{fontconfig} != 0){
my $a = $image->{GD}->useFontConfig(1);
if ($a == 0){
print "Fontconfig not available!";
$image->{fontconfig} = 0; #to get(!) errors later on. If loading fails, then we don't want to pretend we loaded it.
}
}
else{
$image->{fontconfig} = 0; #to avoid silly "uninitialized value"-errors later on
}
}
bless $image, $class;
return $image;
}
1;
=head1 NAME
Image::BoxModel - Module for defining boxes on an image and putting things on them
=head1 SYNOPSIS
use Image::BoxModel;
#Define an object
my $image = new Image::BoxModel (
width => 800,
height => 400,
lib => 'GD', #[IM|GD]
precise => '1' #IM only: IM-backend will draw antialiased lines instead of rounding to integers before drawing.
#I don't know, if this is of interest to anyone..
verbose => '1', #If you want to see which modules and submodules do what.
#Be prepared to see many messages :-)
);
#Define a box named "title" on the upper border
print $image -> Box(position =>"top", height=>120, name=>"title", background =>'red');
#Put some rotated text on the "title"-box and demonstrate some options.
#(verdana.ttf needs to be present in the same directory)
print $image -> Text(
box => "title",
text =>"Hello World!\nAligned right, positioned in the center (default)\nslightly rotated.",
textsize=>"16",
rotate => "10" ,
font => './FreeSans.ttf', #must be present in the same directory. See PORTABILITY below.
fill => "yellow",
background=>"green",
align =>"Right"
);
print $image -> Box(position =>"left", width=>200, name=>"text_1", background =>'blue');
print $image -> Text(
box => "text_1", text =>"Some 'North-West'-aligned text.\n:-)",
textsize => "12",
background =>"yellow",
position =>"NorthWest"
);
print $image -> Text(text => "Some more text on the free space.",textsize => 12, rotate=> "-30");
#Save image to file
$image -> Save(file=> "01_hello_world_$image->{lib}.png");
More examples are in examples/
=head1 DESCRIPTION
=head2 OBJECTIVES
Have a way to draw things on images using the same code with different libraries.
Use OO-style design to make the implementation of new library backends (library wrappers) easy. Image::Magick and GD present at the moment.
Use a box model to cut the original image into smaller rectangles. Afterwards objects can be drawn onto these boxes.
=head2 ANOTHER IMAGING / CHARTING / WHATEVER MODULE?
There are many Charting Modules and many Font Modules as well.
There are many concepts about how to layout elements on an image / page.
This module will try hard to make the life of the user easier and the life of the developer more fun.
It has backends for graphic libraries so that you can draw images using the same code with different libraries.
Example: One user (me ;-) starts writing a perl script which produces some charts.
Because Image::Magick is common to me, I use it. After some time I find out that GD would be much faster and is able to do everything I need.
I have to rewrite much of my code because GD does many things different from how IM does them.
..And now someone tells me about the Imager-module from the CPAN!
With this module it is (should be) possible to just replace $image->{lib} in the constructor method and keep the rest of the code.
=head2 PORTABILITY
If you want to write portable code, you have to stick with the following things: (Portability between OSes and between backend libs)
=head3 Don't use fontconfig
You can profit from the wonderful possibilties offered by fontconfig, if you use this module on a system with fontconfig and GD as backend lib.
Anyhow, if you change the backend to Image::Magick later or take your code to a system without fontconfig, it will produce errors.
Perhaps these considerations will lead to removing fontconfig support from Image::BoxModel. Perhaps 'font' will be mandatory for 'Text' and 'Annotate'.
Perhaps there will be a possibilty to specify a default font.
=head3 Copy the font files into your projects directory tree
There is never a guarantee that fonts are present on a different system or on a different machine. You don't know if they are in the same place.
=head3 Always use the 'font' parameter with 'Text' and 'Annotate'
To be safe that the font is found (or an error is displayed), use
font=>'path/to/my/font.ttf'
as a parameter with every 'Text' or 'Annotate' call.
Of course, it is much more conventient to rely on the default settings of fontconfig or some libraries internal magic, but it beaks portability very easily.
=head3 Don't use absolute paths
Yes, of course not. ;-)
It is tempting to use absolute paths to find fonts. Don't. Don't repeat my mistakes. :-)
=head3 Don't use library-methods
It is possible to use every method of the chosen library through the objects library-object:
$image->{IM} -> Edge(radius => "10");
This of course only works with Image::Magick. First, because there will be no {IM} if using GD und second, because GD doesn't know about 'Edge'.
On the other hand, this code will only work with GD:
$image->{GD} -> arc(50,50,95,75,0,360,$image->Colors(color=>'blue'));
There may be cases in which you will want to use library-specific code. Image::BoxModel doesn't implement all features of all libraries. Besides, this would be quite difficult.
=head2 FUTURE
Charts: being done
More graphic primitives
Vector graphic backend
(The problem is, the module "thinks" in bitmaps, so it is not completely clear to me how to transfer this into vectors..)
Imager backend
Any more ideas?
=head2 QUESTIONS
Would it make sense to be able and cut off nonrectangular boxes? / Would it be desirable to cut off boxes which result in a nonrectangular remaining free space?
(Define a rectangle in the upper left corner. Then the free field would be a concave hexagon. This produces some problems later: defining new boxes / find out if object is in the box)
How to translate the used bitmap model into the vector model?
In the bitmap model the smallest unit is one pixel, which defines rather a area of the image with a certain size.
In the world of vectors there is no smallest unit (is there?) and a point has no size.
=head2 EXAMPLES
There is a growing set of sample programs in the examples/ directory together with their respective images.
This should allow you to understand how things work and verify if your copy of the module works as expected.
To ensure they work even if you don't install the module into your system, they use a "use lib ("../lib"); Dunno if this is appropriate.
=head2 SEE:
README for installation & dependencies
L<Image::BoxModel::Lowlevel> - basic functionality
L<Image::BoxModel::Text> - direct and save drawing of text
L<Image::BoxModel::Chart> - charts (incomplete)
L<Image::BoxModel::Color> - mini-tutorial on how to use colors
=head1 BUGS
oh, please ;-)
Bug reports are welcome.
=head1 AUTHOR
Matthias Bloch, <lt>matthias at puffin ch<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2008 by :m)
( run in 2.597 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )