File Coverage

blib/lib/Imager/DTP/Letter.pm
Criterion Covered Total %
statement 15 72 20.8
branch 0 34 0.0
condition 0 15 0.0
subroutine 5 11 45.4
pod n/a
total 20 132 15.1


line stmt bran cond sub pod time code
1             package Imager::DTP::Letter;
2 2     2   9 use strict;
  2         4  
  2         55  
3 2     2   10 use Carp;
  2         2  
  2         93  
4 2     2   11 use Imager;
  2         3  
  2         19  
5 2     2   1633 use Imager::Matrix2d;
  2         11127  
  2         108  
6 2     2   15 use vars qw($VERSION);
  2         4  
  2         4590  
7              
8             $VERSION = '0.05';
9              
10             sub new {
11 0     0     my $self = shift;
12 0           my %o = @_;
13             # define properties
14 0           my $p = {
15             text => '',
16             font => '',
17             width => 0,
18             height => 0,
19             descent => 0,
20             ascent => 0,
21             advanced_width => 0,
22             left_bearing => 0,
23             right_bearing => 0,
24             global_ascent => 0,
25             global_descent => 0,
26             end_offset => 0,
27             xscale => 1,
28             yscale => 1,
29             isUpdated => 0, # check flag for _calcWidthHeight needs
30             };
31 0           $self = bless($p,$self);
32             # set properties
33 0 0         $self->setText(text=>$o{text}) if(defined($o{text}));
34 0 0         $self->setFont(font=>$o{font}) if(defined($o{font}));
35 0 0 0       $self->setScale(x=>$o{xscale},y=>$o{yscale}) if($o{xscale} || $o{yscale});
36 0           return $self;
37             }
38              
39             sub draw {
40 0     0     my($self) = shift;
41 0           my %o = $self->_draw_init(@_);
42             # recalculate bounding box
43 0           $self->_calcWidthHeight();
44             # draw frame - for debug
45 0 0         if($o{debug}){
46             # real body frame
47 0 0         $o{target}->box(
48             filled=>1,aa=>0,color=>'#FFAA99',
49             xmin=>$o{x} + $self->getLeftBearing(),
50             ymin=>$o{y} + $self->getGlobalAscent() - $self->getAscent(),
51             xmax=>$o{x} + $self->getEndOffset(),
52             ymax=>$o{y} + $self->getGlobalAscent() - $self->getDescent(),
53             ) if($o{debug} > 1);
54             # virtual (outer) body frame
55 0           $o{target}->box(
56             filled=>0,aa=>0,color=>'#999999',
57             xmin=>$o{x},
58             ymin=>$o{y},
59             xmax=>$o{x} + $self->getAdvancedWidth(),
60             ymax=>$o{y} + $self->getGlobalAscent() - $self->getGlobalDescent(),
61             );
62             }
63             # scale transformation
64 0           my($sx,$sy) = $self->getScale();
65 0 0 0       if($sx != 1 || $sy != 1){
66 0           my $m = Imager::Matrix2d->scale(x=>$sx,y=>$sy);
67 0           $self->getFont()->transform(matrix=>$m);
68             }
69             # draw letter - using Imager::String method
70             $o{target}->string(
71 0 0         %{$o{others}},
  0            
72             x => $o{x} + $self->getLeftBearing(),
73             y => $o{y} + $self->getGlobalAscent() - $self->getAscent(),
74             text => $self->getText(),
75             font => $self->getFont(),
76             utf8 => 1, vlayout => 0, align => 0
77             ) or die $o{target}->errstr;
78             # draw baseline position - for debug
79 0 0         if($o{debug}){
80 0           $o{target}->box(
81             filled=>1,aa=>0,color=>'#880000',
82             xmin=>$o{x},
83             xmax=>$o{x}+$self->getAdvancedWidth(),
84             ymin=>$o{y}+$self->getGlobalAscent(),
85             ymax=>$o{y}+$self->getGlobalAscent()
86             );
87             }
88 0           return 1;
89             }
90              
91             sub _draw_init {
92 0     0     my($self) = shift;
93 0           my %o = @_;
94             # validation
95 0 0 0       if(!defined($self->getFont()) && !defined($self->getText())){
96 0           confess "you must define both text and font before drawing";
97             }
98 0 0         if(ref($o{target}) !~ /^Imager(::.+)?/){
99 0           confess "target must be an Imager Object ($o{target})";
100             }
101 0 0         $o{x} = 0 if(!$o{x});
102 0 0         $o{y} = 0 if(!$o{y});
103 0           return %o;
104             }
105              
106             sub setText {
107 0     0     my $self = shift;
108 0           my %o = @_;
109 0 0         if($o{text} eq ''){
110 0           confess "text: must define some text";
111             }
112 0           $self->{text} = $o{text};
113 0           $self->{isUpdated} = 0;
114 0           return 1;
115             }
116              
117             sub setFont {
118 0     0     my $self = shift;
119 0           my %o = @_;
120 0 0 0       if(!defined($o{font}) || ref($o{font}) !~ /^Imager::Font(::.+)?$/){
121 0           confess "font: must supply an Imager::Font Object ($o{font})";
122             }
123 0           $o{font}->{utf8} = 1;
124 0           $o{font}->{vlayout} = 0;
125 0           $self->{font} = $o{font};
126 0           $self->{isUpdated} = 0;
127 0           return 1;
128             }
129              
130             sub setScale {
131 0     0     my $self = shift;
132 0           my %o = @_;
133             # validation
134 0           foreach my $v (qw(x y)){
135 0 0 0       if($o{$v} && $o{$v} !~ /^\d+(\.\d+)?$/){
136 0           confess "$v: must be a ratio value (like 0.5, 1.2, and so on)";
137             }
138             };
139 0 0         $self->{xscale} = $o{x} if($o{x});
140 0 0         $self->{yscale} = $o{y} if($o{y});
141 0           $self->{isUpdated} = 0;
142 0           return 1;
143             }
144              
145             sub _calcWidthHeight {
146             my $self = shift;
147             return undef if($self->{isUpdated});
148             return undef if($self->getText() eq '' || !$self->getFont());
149             my %o = @_;
150             # validation
151             foreach my $v (keys %o){
152             if($o{$v} !~ /^\d+$/){
153             confess "$v: must be an integer ($o{$v})";
154             }
155             }
156             my $f = $self->getFont();
157             my $b = $f->bounding_box(string=>$self->getText());
158             unless(defined($b->ascent)){
159             confess qq(unable to map string '$self->getText()' with the specified font.
160             Perhaps you forgot to encode your text to utf8?
161             *ATTENTION* utf8-flag must be enabled! try using \&utf8::decode() );
162             }
163             my ($x,$y) = $self->getScale();
164             $self->{width} = $b->total_width();
165             $self->{height} = $b->text_height();
166             $self->{descent} = $b->descent();
167             $self->{ascent} = $b->ascent();
168             $self->{advanced_width} = $b->advance_width();
169             $self->{left_bearing} = $b->left_bearing;
170             $self->{right_bearing} = $b->advance_width() - $b->end_offset();
171             $self->{global_ascent} = $b->global_ascent();
172             $self->{global_descent} = $b->global_descent();
173             $self->{end_offset} = $b->end_offset();
174             if($x != 1){
175             foreach my $v qw(width advanced_width left_bearing right_bearing end_offset){
176             $self->{$v} *= $x;
177             }
178             }
179             if($y != 1){
180             foreach my $v qw(height descent ascent global_ascent global_descent){
181             $self->{$v} *= $y;
182             }
183             }
184             # for blank space
185             if($self->{text} eq ' '){
186             $self->{ascent} = $self->{width};
187             $self->{height} = $self->{width};
188             }
189             $self->{isUpdated} = 1;
190             return 1;
191             }
192              
193             sub getText {
194             return shift->{text};
195             }
196             sub getFont {
197             return shift->{font};
198             }
199             sub getScale {
200             my $self = shift;
201             return ($self->{xscale},$self->{yscale});
202             }
203             sub getWidth {
204             my $self = shift;
205             $self->_calcWidthHeight();
206             return $self->{width};
207             }
208             sub getHeight {
209             my $self = shift;
210             $self->_calcWidthHeight();
211             return $self->{height};
212             }
213             sub getAscent {
214             my $self = shift;
215             $self->_calcWidthHeight();
216             return $self->{ascent};
217             }
218             sub getDescent {
219             my $self = shift;
220             $self->_calcWidthHeight();
221             return $self->{descent};
222             }
223             sub getAdvancedWidth {
224             my $self = shift;
225             $self->_calcWidthHeight();
226             return $self->{advanced_width};
227             }
228             sub getLeftBearing {
229             my $self = shift;
230             $self->_calcWidthHeight();
231             return $self->{left_bearing};
232             }
233             sub getRightBearing {
234             my $self = shift;
235             $self->_calcWidthHeight();
236             return $self->{right_bearing};
237             }
238             sub getGlobalAscent {
239             my $self = shift;
240             $self->_calcWidthHeight();
241             return $self->{global_ascent};
242             }
243             sub getGlobalDescent {
244             my $self = shift;
245             $self->_calcWidthHeight();
246             return $self->{global_descent};
247             }
248             sub getEndOffset {
249             my $self = shift;
250             $self->_calcWidthHeight();
251             return $self->{end_offset};
252             }
253              
254             1;
255             __END__