File Coverage

blib/lib/Image/Base/Text.pm
Criterion Covered Total %
statement 147 154 95.4
branch 60 70 85.7
condition 25 33 75.7
subroutine 23 23 100.0
pod 7 14 50.0
total 262 294 89.1


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012 Kevin Ryde
2              
3             # This file is part of Image-Base-Other.
4             #
5             # Image-Base-Other is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Image-Base-Other is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Image-Base-Other. If not, see .
17              
18              
19             package Image::Base::Text;
20             # maybe one day 5.005 for 4-arg substr() replacing
21             # 5.6 for easier clean open()
22 4     4   14332 use 5.006;
  4         16  
  4         357  
23 4     4   23 use strict;
  4         8  
  4         166  
24 4     4   23 use Carp;
  4         7  
  4         310  
25 4     4   4203 use Text::Tabs ();
  4         4702  
  4         129  
26 4     4   33 use List::Util 'min','max';
  4         9  
  4         712  
27 4     4   26 use vars '$VERSION', '@ISA';
  4         9  
  4         284  
28              
29             $VERSION = 9;
30              
31 4     4   3662 use Image::Base 1.12; # version 1.12 for ellipse() $fill
  4         9373  
  4         225  
32             @ISA = ('Image::Base');
33              
34             # uncomment this to run the ### lines
35             #use Smart::Comments;
36              
37              
38 4         7901 use constant default_colour_to_character => { 'black' => ' ',
39             'clear' => ' ',
40             '#000000' => ' ',
41             '#000000000000' => ' ',
42             other => '*',
43 4     4   31 };
  4         6  
44              
45             sub new {
46 53     53 1 10948 my ($class, %param) = @_;
47              
48 53 50       118 if (ref $class) {
49             # clone by copying fields and data array
50 0         0 my $self = bless { %$class }, $class;
51 0         0 $self->{'-rows_array'} = [ @{$class->{'-rows_array'}} ];
  0         0  
52 0         0 return $self;
53             }
54              
55 53         334 my $self = bless
56             { -rows_array => [],
57             -width => 0,
58             -colour_to_character => $class->default_colour_to_character,
59             }, $class;
60              
61 53 100       148 if (defined (my $filename = delete $param{'-file'})) {
62 2         5 $self->load($filename);
63             }
64 53         164 $self->set (%param);
65 53         175 return $self;
66             }
67              
68             sub _get {
69 1996     1996   36365 my ($self, $key) = @_;
70             # ### Image-Base-Text _get(): $key
71              
72 1996 100       4014 if ($key eq '-height') {
73 936         834 return scalar @{$self->{'-rows_array'}};
  936         3013  
74             }
75 1060         2650 return $self->SUPER::_get ($key);
76             }
77              
78             sub set {
79 81     81 1 222 my ($self, %param) = @_;
80             ### set(): \%param
81              
82 81 100       197 if (defined (my $width = delete $param{'-width'})) {
83 56         58 foreach my $row (@{$self->{'-rows_array'}}) {
  56         122  
84 60 100       81 if (length($row) < $width) {
85 20         35 $row .= ' ' x ($width - length($row));
86             } else {
87 40         66 substr($row,$width) = '';
88             }
89             }
90             # ready for -height to use
91 56         84 $self->{'-width'} = $width;
92             }
93              
94 81 100       178 if (defined (my $height = delete $param{'-height'})) {
95 41         58 my $rows_array = $self->{'-rows_array'};
96 41 100       76 if (@$rows_array >= $height) {
97             ### rows_array shorten
98 4         7 splice @$rows_array, $height;
99             } else {
100             ### rows_array extend by: ($height - scalar(@$rows_array))
101 37         65 my $row = ' ' x $self->{'-width'};
102 37         147 push @$rows_array, ($row) x ($height - scalar(@$rows_array));
103             }
104             }
105              
106 81         447 %$self = (%$self, %param);
107             }
108              
109             sub load {
110 3     3 1 7 my ($self, $filename) = @_;
111             ### Image-Base-Text load()
112 3 50       7 if (@_ == 1) {
113 0         0 $filename = $self->get('-file');
114             } else {
115 3         7 $self->set('-file', $filename);
116             }
117             ### $filename
118              
119 3 50       89 open my $fh, '<', $filename or croak "Cannot open $filename: $!";
120 3         7 $self->load_fh ($fh);
121 3 50       39 close $fh or croak "Error closing $filename: $!";
122             }
123              
124             # these undocumented yet ...
125             sub load_fh {
126 3     3 0 4 my ($self, $fh) = @_;
127             ### Image-Base-Text load_fh(): $fh
128 3         38 $self->load_lines (map {chomp; $_} <$fh>);
  3         5  
  3         8  
129             }
130             sub load_string {
131 12     12 0 53 my ($self, $str) = @_;
132             ### Image-Base-Text load_string(): $str
133             # split
134 12         34 my @lines = split /\n/, $str, -1;
135 12 100 100     53 if (@lines && $lines[-1] eq '') {
136             # drop the empty element after the last newline, but keep a non-empty
137             # final element from chars without a final newline
138 9         11 pop @lines;
139             }
140 12         569 $self->load_lines (@lines);
141             }
142             sub load_lines {
143 16     16 0 41 my ($self, @rows_array) = @_;
144             ### load_lines: @rows_array
145              
146 16         17 my $width = 0;
147 16         23 foreach my $row (@rows_array) {
148 29         59 $row = Text::Tabs::expand ($row);
149 29 100       353 if ($width < length($row)) {
150 16         29 $width = length($row);
151             }
152             }
153              
154 16         49 $self->{'-rows_array'} = \@rows_array;
155 16         43 $self->set (-width => $width); # pad out shorter lines
156             }
157              
158             sub save {
159 2     2 1 57 my ($self, $filename) = @_;
160             ### Image-Base-Text save(): @_
161 2 100       7 if (@_ == 2) {
162 1         2 $self->set('-file', $filename);
163             } else {
164 1         5 $filename = $self->get('-file');
165             }
166             ### $filename
167              
168 2         12 my $fh;
169 2 50 33     226 (open $fh, '>', $filename
      33        
170             and $self->save_fh($fh)
171             and close $fh)
172             or croak "Error writing $filename: $!";
173             }
174              
175             # these undocumented yet ...
176             sub save_fh {
177 2     2 0 4 my ($self, $fh) = @_;
178 2         3 my $rows_array = $self->{'-rows_array'};
179 2         3 local $, = "\n";
180 2 50       125 return print $fh @$rows_array,(@$rows_array ? '' : ());
181             }
182             sub save_string {
183 17     17 0 59 my ($self) = @_;
184 17         20 my $rows_array = $self->{'-rows_array'};
185 17 50       83 return join ("\n", @$rows_array, (@$rows_array ? '' : ()));
186             }
187              
188             #------------------------------------------------------------------------------
189             # drawing
190              
191             sub xy {
192 5320     5320 1 102446 my ($self, $x, $y, $colour) = @_;
193             ### Image-Base-Text xy(): @_[1 .. $#_]
194              
195             # clip to width,height
196 5299         16864 return if ($x < 0 || $x >= $self->{'-width'}
197 5320 100 100     30798 || $y < 0 || $y >= @{$self->{'-rows_array'}});
      100        
      100        
198              
199 5295         7008 my $rows_array = $self->{'-rows_array'};
200 5295 100       8283 if (@_ == 3) {
201 5023         14042 return $self->character_to_colour (substr ($rows_array->[$y], $x, 1));
202             } else {
203 272         485 substr ($rows_array->[$y], $x, 1) = $self->colour_to_character($colour);
204             }
205             }
206              
207             sub line {
208 79     79 1 477 my ($self, $x1,$y1, $x2,$y2, $colour) = @_;
209              
210 79 100       131 if ($y1 == $y2) {
211             ### horizontal line by substr() block store ...
212              
213 48         75 my $rows_array = $self->{'-rows_array'};
214 48 100 100     253 return if $y1 < 0 || $y1 > $#$rows_array; # entirely outside
215              
216 44 100       90 if ($x1 > $x2) { ($x1,$x2) = ($x2,$x1) } # x1 smaller
  9         32  
217 44         68 my $xmax = $self->{'-width'}-1;
218 44 100 100     161 return if $x2 < 0 || $x1 > $xmax; # entirely outside
219              
220 42         74 $x1 = max($x1,0);
221 42         70 $x2 = min($x2,$xmax);
222 42         66 my $x_width = $x2-$x1+1;
223 42         98 substr($rows_array->[$y1], $x1, $x_width,
224             $self->colour_to_character($colour) x $x_width);
225              
226             } else {
227 31         126 shift->SUPER::line(@_);
228             }
229             }
230              
231             # rectangle() can do a substr() block store on each filled row (either all
232             # if $fill, or the top and bottom if not).
233             #
234             sub rectangle {
235 369     369 1 6372 my ($self, $x1,$y1, $x2,$y2, $colour, $fill) = @_;
236             ### Image-Base-Text xy(): @_[1,$#_]
237              
238 369         500 my $rows_array = $self->{'-rows_array'};
239              
240 369 50 66     2791 unless ($x2 >= 0
      66        
      33        
241             && $y2 >= 0
242             && $x1 < $self->{'-width'}
243             && $y1 <= $#$rows_array) {
244             ### entirely outside 0,0,width,height ...
245 6         23 return;
246             }
247              
248 363         753 my $x1_clip = max($x1,0);
249 363         733 my $x2_clip = min($x2,$self->{'-width'}-1);
250              
251 363         661 my $char = $self->colour_to_character($colour);
252 363         516 my $x_width = $x2_clip - $x1_clip + 1;
253 363         609 my $repl = $char x $x_width;
254              
255 363 100       548 if ($fill) {
256 266         840 foreach my $y (max($y1,0) .. min($y2,$#$rows_array)) {
257 2129         3473 substr ($rows_array->[$y], $x1_clip, $x_width) = $repl;
258             }
259              
260             } else {
261             ### top, if in range ...
262 97 100       176 if ($y1 >= 0) {
263 91         162 substr ($rows_array->[$y1], $x1_clip, $x_width) = $repl;
264             }
265              
266 97         92 $y1++;
267 97 100       289 if ($y2 >= $y1) {
268 53 100       110 if ($y2 <= $#$rows_array) {
269             ### bottom in range and not same as top ...
270 49         72 substr ($rows_array->[$y2], $x1_clip, $x_width) = $repl;
271             }
272              
273             ### sides, if any ...
274 53         65 $y2--;
275 53 100       147 if ($y2 >= $y1) {
276 32         65 my $y1_clip = max($y1,0);
277 32         56 my $y2_clip = min($y2,$#$rows_array);
278              
279 32 100       63 if ($x1 == $x1_clip) {
280 27         51 foreach my $y ($y1_clip .. $y2_clip) {
281 126         167 substr ($rows_array->[$y], $x1, 1) = $char;
282             }
283             }
284 32 100       83 if ($x2 == $x2_clip) {
285 28         48 foreach my $y ($y1_clip .. $y2_clip) {
286 129         243 substr ($rows_array->[$y], $x2, 1) = $char;
287             }
288             }
289             }
290             }
291             }
292             }
293              
294             sub colour_to_character {
295 681     681 0 832 my ($self, $colour) = @_;
296             ### colour_to_character(): $colour
297 681 100       1680 if (defined (my $char = $self->{'-colour_to_character'}->{$colour})) {
298 182         354 return $char;
299             }
300 499 100       828 if (length($colour) == 1) {
301 69         205 return $colour;
302             }
303 430 50       963 if (defined (my $char = $self->{'-colour_to_character'}->{'other'})) {
304 430         1620 return $char;
305             }
306 0         0 croak "Unknown colour: $colour";
307             }
308             sub character_to_colour {
309 5023     5023 0 9512 my ($self, $char) = @_;
310 5023 50       9101 if (length ($char) == 0) {
311 0         0 return undef;
312             }
313 5023 100       20946 if (defined (my $colour = $self->{'-character_to_colour'}->{$char})) {
314 4988         17467 return $colour;
315             }
316 35         156 return $char;
317             }
318              
319             1;
320             __END__