File Coverage

blib/lib/PostScript/MailLabels.pm
Criterion Covered Total %
statement 339 536 63.2
branch 56 160 35.0
condition 8 42 19.0
subroutine 19 30 63.3
pod 0 26 0.0
total 422 794 53.1


line stmt bran cond sub pod time code
1             package PostScript::MailLabels;
2              
3 1     1   995 use strict;
  1         4  
  1         48  
4 1     1   6 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         1  
  1         89  
5              
6 1     1   1078 use PostScript::MailLabels::BasicData;
  1         4  
  1         105  
7              
8             require Exporter;
9              
10             @ISA = qw(Exporter);
11             # Items to export into callers namespace by default. Note: do not export
12             # names by default without a very good reason. Use EXPORT_OK instead.
13             # Do not simply export all your public functions/methods/constants.
14             @EXPORT = qw( labelsetup labeldata averycode);
15              
16             $VERSION = '2.32';
17              
18 1     1   6 use Carp;
  1         1  
  1         6958  
19              
20             sub new {
21 1     1 0 12 my $proto = shift;
22 1   33     9 my $class = ref($proto) || $proto;
23 1         2 my $self = {};
24              
25 1         3 $self->{SETUP} = {};
26 1         3 $self->{FREETEXT} = {};
27 1         2 $self->{COMPONENTS} = {};
28 1         3 $self->{LABELDEF} = [];
29              
30 1         3 $self->{MAKEBOX} = ''; # ps code to draw a box
31 1         2 $self->{MAKERULE} = ''; # ps code to make rulers
32 1         2 $self->{PRTTEXT} = ''; # ps code to output the text
33 1         3 $self->{PRTBAR} = ''; # ps code to output the barcode
34              
35 1         2 $self->{DATA} = {}; # pointer to various arrays & hashes of basic data.
36              
37 1         3 bless $self, $class;
38              
39 1         4 &initialize($self);
40              
41 1         4 return $self;
42             }
43              
44             sub initialize {
45              
46             # Initialize with some reasonable default values
47             # printable area : define width of borders
48             # printable_left, printable_right, printable_top, printable_bot
49              
50             # output definition : define left and top offsets, width and
51             # height of a label
52             # output_left, output_top, output_width, output_height
53              
54             # Other controls
55             # Postnet => 'yes' : print barcode on bottom for zip code
56             # Font => 'Helvetica'
57             # units => 'inches' or 'cm'
58             #
59              
60 1     1 0 1 my $self = shift;
61              
62 1         4 %{$self -> {SETUP}} = (
  1         18  
63              
64             # paper size
65              
66             papersize => 'Letter',
67             orientation => 'portrait',
68            
69             # printable area on physical page
70              
71             printable_left => 0.0,
72             printable_right => 0.0,
73             printable_top => 0.0,
74             printable_bot => 0.0,
75              
76             # define where the labels live (ideally)
77              
78             output_top => 0.0,
79             output_left => 0.0,
80             output_width => 0.0,
81             output_height => 0.0,
82             x_gap => 0.0,
83             y_gap => 0.0,
84             number => 0,
85             columns => 0,
86              
87             # Adjustments for printer idiosyncracies
88              
89             x_adjust => 0.0,
90             y_adjust => 0.0,
91              
92             # Other controls
93              
94             postnet => 'yes',
95             font => 'Helvetica',
96             fontsize => 12,
97             units => 'english',
98             firstlabel => 1,
99              
100             # Character encoding
101              
102             encoding => 'StandardEncoding', # or ISOLatin1Encoding
103              
104             # set equal to the Avery(tm) product code, and the label description
105             # will be updated from the database.
106             avery => undef,
107              
108             # set equal to the Avery(tm) product code, and the label description
109             # will be updated from the database.
110             dymo => undef,
111              
112             );
113            
114             # Default (US-style) address components
115            
116 1         11 %{$self -> {COMPONENTS}} = (
  1         6  
117             # first name
118             fname => { type => 'name', adj => 'yes', font => 'Helvetica', 'index' => 0 },
119             # last name
120             lname => { type => 'name', adj => 'yes', font => 'Helvetica', 'index' => 1 },
121             # street address and street
122             street => { type => 'road', adj => 'yes', font => 'Helvetica', 'index' => 2 },
123             # city
124             city => { type => 'place', adj => 'yes', font => 'Helvetica', 'index' => 3 },
125             # state
126             state => { type => 'place', adj => 'no', font => 'Helvetica', 'index' => 4 },
127             # country
128             country => { type => 'place', adj => 'no', font => 'Helvetica', 'index' => 6 },
129             # zip
130             zip => { type => 'place', adj => 'no', font => 'Helvetica', 'index' => 5 },
131             # postnet
132             postnet => { type => 'bar', adj => 'no', font => 'PostNetJHC', 'index' => 5 },
133             );
134              
135             # Default label definition
136              
137 1         4 @{$self -> {LABELDEF}} = (
  1         2  
138             # line 1
139             [ 'fname', 'lname' ],
140             # line 2
141             [ 'street', ],
142             # line 3
143             [ 'city', 'state', 'zip' ],
144             # line 4
145             [ 'postnet', ],
146             );
147              
148             # Default free text
149              
150 1         2 %{$self->{FREETEXT}} = (
  1         3  
151             X => 0,
152             Y => 0,
153             Text => '',
154             );
155              
156            
157             # Go get the basic data
158              
159 1         7 $self->{DATA} = new PostScript::MailLabels::BasicData;
160             }
161              
162             ##########################################################
163             ## Add or Edit a component ##
164             ##########################################################
165              
166             # editcomponent(name, type, adjust, index, font, )
167             # where
168             # name = component name
169             # type = name, road, place (controls trimming)
170             # adjust = yes or no (can I shorten it?)
171             # index = which element of input array?
172             # font = which font to use
173             sub editcomponent {
174 0     0 0 0 my $self = shift;
175 0         0 my ($name, $type, $adj, $index, $font) = @_;
176              
177 0 0       0 if (!defined $name) {return [keys %{$self->{COMPONENTS}}];}
  0         0  
  0         0  
178              
179 0 0       0 if (!defined $type) {return [values %{$self->{COMPONENTS}{$name}}];}
  0         0  
  0         0  
180              
181 0 0       0 if (!defined $font) {$font = $self->{SETUP}{font};}
  0         0  
182 0 0       0 if (!defined $adj) {$adj = 'no';}
  0         0  
183              
184 0 0 0     0 if (!defined $index && defined $self->{COMPONENTS}{$name}) {
    0          
    0          
185 0         0 $index = $self->{COMPONENTS}{$name}->{'index'};
186             }
187             elsif (!defined $index) {
188             }
189             elsif ($index !~ /^\d+$/) {
190             }
191              
192 0 0 0     0 if ($type ne 'name' && $type ne 'road' && $type ne 'place' && $type ne 'bar') {
      0        
      0        
193 0         0 print STDERR "Invalid type $type, in editcomponent call\n";
194 0         0 die;
195             }
196 0 0 0     0 if ($adj ne 'yes' && $adj ne 'no') {
197 0         0 print STDERR "Invalid adjust $adj, in editcomponent call\n";
198 0         0 die;
199             }
200 0         0 my @fonts = ListFonts($self);
201 0         0 my $okay=0;
202 0         0 foreach (@fonts) {
203 0 0       0 if ($font eq $_){
204 0         0 $okay=1;
205 0         0 last;
206             }
207             }
208 0 0       0 if (!$okay) {
209 0         0 print STDERR "Invalid font, $font, requested.\n",
210             "Available fonts are :\n",
211             join("\n",@fonts),"\n";
212 0         0 die;
213             }
214              
215             # Whew! input verified, lets apply it...
216              
217 0         0 $self->{COMPONENTS}{$name} = { type => $type,
218             adj => $adj,
219             'index' => $index,
220             font => $font};
221 0         0 return ;
222             }
223              
224             ##########################################################
225             ## Label definition ##
226             ##########################################################
227              
228             # definelabel(line #, component, component, ...)
229              
230             sub definelabel {
231 0     0 0 0 my $self = shift;
232 0         0 my $line = shift;
233 0         0 my @comps;
234              
235 0 0       0 if (!defined $line) { return $self->{LABELDEF};}
  0         0  
236              
237 0 0       0 if ($line eq 'clear') { # clear old definition
238 0         0 $self -> {LABELDEF} = ();
239 0         0 return;
240             }
241              
242 0 0       0 if ($#_<0) {return $self->{LABELDEF}[$line];}
  0         0  
243              
244 0 0       0 if ($line !~ /^\d+$/) {
245 0         0 print STDERR "Invalid line number $line, in definelabel call\n";
246 0         0 die;
247             }
248              
249             # verify components exist
250              
251 0         0 my $postnet=0;
252 0         0 foreach (@_) {
253 0         0 push @comps, $_; # take this opportunity to do a deep copy...
254 0 0       0 if ($_ eq 'postnet') {$postnet=1;}
  0         0  
255 0 0       0 if (!defined $self->{COMPONENTS}{$_}) {
256 0         0 print STDERR "Invalid component $_, in definelabel call\n";
257 0         0 die;
258             }
259             }
260              
261             # Make certain that if barcode is requested, it is the only component
262             # on that line
263              
264 0 0 0     0 if ($postnet && $#comps > 0) {
265 0         0 print STDERR "postnet (barcode) must be the only component on the line, in definelabel call\n";
266 0         0 die;
267             }
268            
269 0         0 $self -> {LABELDEF}[$line] = \@comps;
270              
271 0         0 return ;
272             }
273              
274             ##########################################################
275             ## Free Text ##
276             ##########################################################
277              
278             sub freetext {
279 0     0 0 0 my $self = shift;
280 0         0 my %args = @_;
281              
282 0         0 foreach (keys %args) {
283 0 0       0 if (/^X$/i) {
    0          
    0          
284 0         0 $self->{FREETEXT}{X} = $args{$_};
285             }
286             elsif (/^Y$/i) {
287 0         0 $self->{FREETEXT}{Y} = $args{$_};
288             }
289             elsif (/^text$/i) {
290 0         0 $self->{FREETEXT}{Text} = $args{$_};
291             }
292             else {
293 0         0 print STDERR "Invalid parameter, $_, given to FREETEXT\n";
294             }
295             }
296 0         0 return;
297             }
298              
299             ##########################################################
300             ## Set the settings ##
301             ##########################################################
302              
303             sub labelsetup {
304 6     6 0 488 my $self = shift;
305 6         25 my %args = @_;
306              
307 6         8 my %params;
308             # when adding a parameter, be sure to increment the array at the end of the statement.
309 6         63 @params{ qw / papersize height width orientation printable_left printable_right printable_top printable_bot output_top
310             output_left output_width output_height x_gap y_gap number x_adjust y_adjust
311             postnet font fontsize units firstlabel avery dymo columns encoding / } = (0..25);
312              
313 6         10 my @papers = @{$self->{DATA}{PAPER}};
  6         76  
314              
315 6         10 my @encodings = qw / ISOLatin1Encoding StandardEncoding / ;
316              
317             # conversion from inches/centimeters to points
318              
319 6         7 my $f = 72;
320 6 50 33     16 if (defined($args{units}) && $args{units} eq 'metric') {$f = 28.3465;}
  0         0  
321              
322 6         15 foreach (keys %args)
323             {
324             # print "$_\n"; #################################
325 20 50       47 if (!defined $params{lc($_)}) {
326 0         0 print STDERR "Invalid setup parameter $_\n";
327 0         0 die;
328             }
329 20 100       96 if ( lc($_) eq 'font') {
    50          
    100          
    50          
    50          
    50          
330 2         4 my @fonts = ListFonts($self);
331 2         9 my $okay=0;
332 2         4 foreach my $font (@fonts) {
333 83 100       130 if ($font eq $args{$_}){
334 2         3 $okay=1;
335 2         3 last;
336             }
337             }
338 2 50       5 if (!$okay) {
339 0         0 print STDERR "Invalid font, $args{$_}, requested.\n",
340             "Available fonts are :\n",
341             join("\n",@fonts),"\n";
342 0         0 die;
343             }
344 2         15 $self->{SETUP}{lc($_)} = $args{$_};
345             }
346             elsif (lc($_) eq 'encoding') {
347 0         0 my $okay=0;
348 0         0 foreach my $encoding (@encodings) {
349 0 0       0 if ($encoding =~ /$args{$_}/i){
350 0         0 $okay=1;
351 0         0 $args{$_} = $encoding;
352 0         0 last;
353             }
354             }
355 0 0       0 if (!$okay) {
356 0         0 print STDERR "Invalid encoding, $args{$_}, requested.\n",
357             "Available values are :\n",
358             join("\n",@encodings),"\n";
359 0         0 die;
360             }
361 0         0 $self->{SETUP}{lc($_)} = $args{$_};
362             }
363             elsif (lc($_) eq 'papersize') {
364 1         2 my $okay=0;
365 1         1 foreach my $paper (@papers) {
366 1 50       16 if ($paper =~ /$args{$_}/i){
367 1         8 $okay=1;
368 1         1 $args{$_} = $paper;
369 1         2 last;
370             }
371             }
372 1 50       2 if (!$okay) {
373 0         0 print STDERR "Invalid papersize, $args{$_}, requested.\n",
374             "Available sizes are :\n",
375             join("\n",@papers),"\n";
376 0         0 die;
377             }
378 1         8 $self->{SETUP}{lc($_)} = $args{$_};
379             }
380             elsif (lc($_) eq 'width') {
381 0         0 my($val) = lc($args{$_});
382 0         0 $self->{SETUP}{papersize} = 'Userdefined';
383 0         0 $self->{DATA}->{WIDTH}{Userdefined} = $val*$f;
384             }
385             elsif (lc($_) eq 'height') {
386 0         0 my($val) = lc($args{$_});
387 0         0 $self->{SETUP}{papersize} = 'Userdefined';
388 0         0 $self->{DATA}->{HEIGHT}{Userdefined} = $val*$f;
389             }
390             elsif (lc($_) eq 'orientation') {
391 0         0 my($val) = lc($args{$_});
392 0 0 0     0 if ($val ne 'portrait' && $val ne 'landscape') {
393 0         0 die "Invalid orientation \"$args{$_}\" -- must be \"portrait\" or \"landscape\"\n";
394             }
395 0         0 $self->{SETUP}{lc($_)} = $val;
396             }
397             else {
398 17         52 $self->{SETUP}{lc($_)} = lc($args{$_});
399 17         35 $args{lc($_)} = $args{$_};
400             }
401             }
402            
403             # convert all parameters to points
404              
405 6         12 foreach (qw/output_left output_top output_width output_height printable_left printable_right printable_top printable_bot x_gap y_gap x_adjust y_adjust /) {
406 72 100       130 if (defined $args{$_}) {$self->{SETUP}{$_} *= $f;}
  12         23  
407             }
408              
409              
410             ############ Process and verify parameters
411              
412             # If avery code is defined, use it.
413             # layout=>[paper-size,[list of product codes], description,
414             # number per sheet, left-offset, top-offset, width, height]
415             # distances measured in points
416              
417              
418 6 100       10 my( $labelmaker ) = grep { defined $self->{SETUP}{$_} && $self->{SETUP}{$_} ne '' } qw(avery dymo);
  12         49  
419            
420 6 50       12 if ( $labelmaker ) {
421 6         11 my $code = $self->{SETUP}{$labelmaker};
422            
423 6         8 my $key = uc $labelmaker;
424            
425 6         15 $self->{SETUP}{papersize} = $self->{DATA}{$key}{$code}->[0];
426 6         10 $self->{SETUP}{number} = $self->{DATA}{$key}{$code}->[3];
427 6         12 $self->{SETUP}{output_left} = $self->{DATA}{$key}{$code}->[4];
428 6         10 $self->{SETUP}{output_top} = $self->{DATA}{$key}{$code}->[5];
429 6         11 $self->{SETUP}{output_width} = $self->{DATA}{$key}{$code}->[6];
430 6         11 $self->{SETUP}{output_height} = $self->{DATA}{$key}{$code}->[7];
431 6         10 $self->{SETUP}{x_gap} = $self->{DATA}{$key}{$code}->[8];
432 6         13 $self->{SETUP}{y_gap} = $self->{DATA}{$key}{$code}->[9];
433             }
434              
435             # Verify that measurements sum correctly...
436              
437             # $self->{SETUP}{columns} = 1 if( $self->{SETUP}{columns} == 0 and $labelmaker eq 'dymo' );
438            
439 6 50       14 if ($self->{SETUP}{columns} > 0) {
440 0         0 my $pwidth = $self->{SETUP}{output_width}*$self->{SETUP}{columns}
441             + $self->{SETUP}{x_gap}*($self->{SETUP}{columns}-1)
442             + $self->{SETUP}{output_left}*2;
443 0 0       0 if (abs($pwidth - papersize($self, 1)->[0]) > $self->{SETUP}{output_width}/2) {
444 0         0 print STDERR "Sum of label widths ($pwidth) differs from paper width (",
445             papersize($self, 1)->[0],
446             ") by > ",$self->{SETUP}{output_width}/2," points\n";
447 0         0 die;
448             }
449             }
450 6         51 return $self->{SETUP};
451             }
452              
453              
454              
455             # ****************************************************************
456              
457             # printable area : define width of borders
458             # printable_left, printable_right, printable_top, printable_bot
459              
460             # output definition : define left and top offsets, width and
461             # height of a label
462             # output_left, output_top, output_width, output_height
463              
464             # Other controls
465             # Postnet => 'yes' : print barcode on bottom for zip code
466             # Font => 'Helvetica'
467             #
468              
469             # ****************************************************************
470             sub labelcalibration {
471 1     1 0 2 my $self = shift;
472              
473             # Create a postscript file that will place centered axes on the page
474             # marked off in inches or centimeters, that will allow the user to
475             # actually see what the printable area of their printer is.
476              
477             # Calculate the following quantites to place in the postscript file :
478             # x and y coordinates of page center in points
479             # inc = 0.1 inch or 0.1 cm depending on units, but expressed in points
480             # numx and numy : number of inches or cm on each axis, rounded up.
481            
482 1         4 my $paperwidth = papersize($self)->[0] ; # total width of paper
483 1         3 my $paperheight = papersize($self)->[1] ; # total height of paper
484            
485 1         6 my $xcenter = papersize($self, 1)->[0]/2;
486 1         4 my $ycenter = papersize($self, 1)->[1]/2;
487 1         3 my $landscape = ($self->{SETUP}{orientation} eq 'landscape');
488 1 50       3 my $translate = $landscape ? "$paperwidth 0 translate 90 rotate" : '';
489              
490 1         2 my $inc = 7.2;
491 1 50       3 if ($self->{SETUP}{units} eq 'metric') {$inc = 2.8346457;}
  1         2  
492              
493 1         4 my $numx = int((($xcenter*2)/($inc*10))+0.9);
494 1         3 my $numy = int((($ycenter*2)/($inc*10))+0.9);
495              
496 1         2 my $postscript = $self->{DATA}{CALIBRATE};
497              
498 1         18 $postscript =~ s/%pagesize%/<< \/PageSize [$paperwidth $paperheight]>> setpagedevice/g;
499 1         7 $postscript =~ s/%translate%/$translate/g;
500 1         10 $postscript =~ s/%xcenter%/$xcenter/g;
501 1         5 $postscript =~ s/%ycenter%/$ycenter/g;
502 1         31 $postscript =~ s/%inc%/$inc/g;
503 1         11 $postscript =~ s/%numx%/$numx/g;
504 1         6 $postscript =~ s/%numy%/$numy/g;
505              
506 1         4 return $postscript;
507             }
508              
509             # ****************************************************************
510             sub labeltest {
511 1     1 0 5 my $self = shift;
512              
513             # Create a postscript file to test the calibration
514              
515 1         2 my $postscript = $self->{DATA}{TESTPAGE};
516              
517 1   33     6 my $cols = $self->{SETUP}{columns} || int(papersize($self)->[0] / ($self->{SETUP}{x_gap} + $self->{SETUP}{output_width}));
518 1         3 my $rows = $self->{SETUP}{number}/$cols;
519              
520 1         2 my $physical_paperwidth = papersize($self)->[0] ; # total width of paper
521 1         2 my $physical_paperheight = papersize($self)->[1] ; # total height of paper
522 1         39 my $paperwidth = papersize($self, 1)->[0] ;
523 1         3 my $paperheight = papersize($self, 1)->[1] ;
524              
525 1         4 my $landscape = ($self->{SETUP}{orientation} eq 'landscape');
526 1 50       3 my $translate = $landscape ? "$physical_paperwidth 0 translate 90 rotate" : '';
527              
528 1         19 $postscript =~ s/%pagesize%/<< \/PageSize [$physical_paperwidth $physical_paperheight]>> setpagedevice/g;
529 1         13 $postscript =~ s/%translate%/$translate/g;
530 1         13 $postscript =~ s/%paperwidth%/$paperwidth/g ; # total width of paper
531 1         12 $postscript =~ s/%paperheight%/$paperheight/g ; # total height of paper
532 1         5 $postscript =~ s/%boxwidth%/$self->{SETUP}{output_width}/e ; # label width
  1         11  
533 1         5 $postscript =~ s/%boxheight%/$self->{SETUP}{output_height}/e ; # label height
  1         11  
534 1         4 $postscript =~ s/%xgap%/$self->{SETUP}{x_gap}/e ; # x gap between labels
  1         10  
535 1         4 $postscript =~ s/%ygap%/$self->{SETUP}{y_gap}/e ; # y gap between labels
  1         15  
536 1         16 $postscript =~ s/%rows%/$rows/ ; # rows of labels on each page
537 1         14 $postscript =~ s/%cols%/$cols/ ; # columns of labels on each page
538 1         6 $postscript =~ s/%by%/$self->{SETUP}{output_top}/e ; # gap between top of first label and top of page
  1         5  
539              
540             # adjustments
541 1         10 $postscript =~ s/%xadjust%/$self->{SETUP}{x_adjust}/e ; # adjustment if paper not x centered
  1         6  
542 1         10 $postscript =~ s/%yadjust%/$self->{SETUP}{y_adjust}/e ; # adjustment if paper not y centered
  1         6  
543 1         11 $postscript =~ s/%lbor%/$self->{SETUP}{printable_left}/e ; # left border
  1         6  
544 1         10 $postscript =~ s/%rbor%/$self->{SETUP}{printable_right}/e ; # right border
  1         6  
545 1         5 $postscript =~ s/%tbor%/$self->{SETUP}{printable_top}/e ; # top border
  1         9  
546 1         6 $postscript =~ s/%bbor%/$self->{SETUP}{printable_bot}/e ; # bottom border
  1         14  
547              
548 1         5 return $postscript;
549             }
550              
551             # ****************************************************************
552             # Make mailing labels
553              
554             sub makelabels {
555 1     1 0 2 my $self = shift;
556 1         2 my $addrs = shift;
557              
558 1         2 my $pageno=1;
559              
560             #---------- set up preamble
561 1         1 my $postscript = <<'LABELS';
562             %!PS
563              
564             % This code copyright 1999, Alan Jackson, alan@ajackson.org and is
565             % protected under the Open Source license. Code may be copied and
566             % modified so long as attribution to the original author is
567             % maintained.
568              
569             % Notes : the -15 points for the barcode is to produce the legally required
570             % 13.5 point gap above the codes
571             % The barcode font must be 12 point or greater
572              
573             LABELS
574             #---------- end preamble
575 1         3 my $physical_paperwidth = papersize($self)->[0] ; # total width of paper
576 1         3 my $physical_paperheight = papersize($self)->[1] ; # total height of paper
577 1         3 my $paperwidth = papersize($self, 1)->[0] ;
578 1         3 my $paperheight = papersize($self, 1)->[1] ;
579 1         2 my $landscape = ($self->{SETUP}{orientation} eq 'landscape');
580 1 50       3 my $translate = $landscape ? "%\tlandscape orientation\n$paperheight 0 translate 90 rotate\n" : '';
581              
582 1         5 $postscript .= "% set the page size\n" .
583             "<< /PageSize [$physical_paperwidth $physical_paperheight]>> setpagedevice\n";
584              
585 1         2 $postscript .= "gsave\n" . $translate;
586              
587 1 50       4 if ($self->{SETUP}{postnet} eq 'yes') {
588 1         12 $postscript .= $self->{DATA}{POSTNET}; # add in barcode stuff
589             }
590              
591 1   33     5 my $cols = $self->{SETUP}{columns} || int(papersize($self)->[0] / ($self->{SETUP}{x_gap} + $self->{SETUP}{output_width}));
592              
593 1         3 my $rows = $self->{SETUP}{number}/$cols;
594              
595 1         1 my $boxwidth = $self->{SETUP}{output_width} ; # label width
596 1         2 my $boxheight = $self->{SETUP}{output_height} ; # label height
597 1         1 my $xgap = $self->{SETUP}{x_gap} ; # x gap between labels
598 1         2 my $ygap = $self->{SETUP}{y_gap} ; # y gap between labels
599 1         2 my $by = $self->{SETUP}{output_top} ; # gap between top of first label and top of page
600              
601             # adjustments
602 1         2 my $xadjust = $self->{SETUP}{x_adjust} ; # adjustment if paper not x centered
603 1         2 my $yadjust = $self->{SETUP}{y_adjust} ; # adjustment if paper not y centered
604 1         1 my $lbor = $self->{SETUP}{printable_left} ; # left border
605 1         2 my $rbor = $self->{SETUP}{printable_right} ; # right border
606 1         2 $rbor = $paperwidth - $rbor;
607 1         2 my $tbor = $self->{SETUP}{printable_top} ; # top border
608 1         3 my $bbor = $self->{SETUP}{printable_bot} ; # bottom border
609              
610 1         1 my $fontsize = $self->{SETUP}{fontsize};
611 1         6 my $font = $self->{SETUP}{font};
612            
613             # Can I fit all the rows desired onto the page?
614              
615 1 50       4 if (($rows*$boxheight + $by) > ($paperheight - $bbor)) {
616 1         2 $rows--; # not enough room, drop the last row.
617             }
618              
619             # Build arrays of sx, y, and width that define the locations and widths
620             # of all the labels on a page. They are numbered starting at the top left,
621             # going across and then down.
622            
623 1         3 my @y_arr = qw/0/;
624 1         1 my @x_arr = qw/0/;
625 1         2 my @w_arr = qw/0/;
626              
627 1         13 my ($sx,$ex); # start x, end x for each label
628 1         3 my $bx = ($paperwidth - ($cols-1)*$xgap - $boxwidth*$cols)/2; # begin x
629 1         2 my $y = $paperheight - $by - $yadjust; # initial y position
630 1         3 for (my $r=1;$r<=$rows;$r++) {
631 9         7 my $x = $bx;
632 9         16 for (my $c=1;$c<=$cols;$c++) {
633 27 100       36 if ($x < $lbor) { $sx = $lbor;} # adjust leftmost label
  9         11  
  18         16  
634             else {$sx = $x;}
635 27         25 $x += $boxwidth;
636 27 100       37 if ( $x > $rbor) {$ex = $rbor} # adjust rightmost labelh
  9         9  
  18         13  
637             else {$ex = $x;}
638 27         50 $x += $xgap;
639 27         27 my $width = $ex - $sx;
640 27         24 $sx += $xadjust;
641 27         37 push @y_arr,$y;
642 27         37 push @x_arr,$sx+$xadjust+5;
643 27         59 push @w_arr,$width-10; # leave 5 points slop on both ends.
644             }
645 9         19 $y = $y - $boxheight - $ygap;
646             }
647              
648             # set the desired font and size
649             #The following lines have been modified to account for Portuguese characters
650             # Nuno Faria, 2000/Mars/03
651             # $postscript .= "/$font findfont\n".
652             # "dup length dict begin\n".
653             # "{1 index /FID ne {def} {pop pop} ifelse} forall\n".
654             # "/Encoding $self->{SETUP}{encoding} def\n".
655             # "currentdict\n".
656             # "end\n".
657             # "/$font exch definefont pop\n";
658             #End of modifications
659             # new mods by Juan Manuel Calvo to generalize the process (Nov 2005)
660             ##### make a list of fonts
661              
662 1         2 my %fontlist;
663 1         2 foreach (@{$addrs}){
  1         3  
664 7         8 foreach my $line (@{$self->{LABELDEF}}) {
  7         10  
665 28         26 foreach my $comp (@{$line}) {
  28         37  
666 49         113 $fontlist{$self->{COMPONENTS}{$comp}->{font}}++;
667             }
668             }
669             }
670 1         2 $fontlist{$font}++;
671             ####### define enconding for all fonts
672 1         4 for my $k (keys %fontlist ) {
673 2         22 $postscript .= "% encoding for $k\n" .
674             "/$k findfont\n".
675             "dup length dict begin\n".
676             "{1 index /FID ne {def} {pop pop} ifelse} forall\n".
677             "/Encoding $self->{SETUP}{encoding} def\n".
678             "currentdict\n".
679             "end\n".
680             "/$k exch definefont pop\n";
681             }
682             #####
683              
684 1         5 $postscript .= "/$font findfont $fontsize scalefont setfont\n".
685             "/fontsize $fontsize def\n";
686              
687             # scroll through the address array, building the commands
688             # to print. Test each field to see if it will fit on the
689             # label. If it is too long, do some "intelligent" shortening.
690              
691 1         3 my $lab = $self->{SETUP}{firstlabel};
692 1         2 foreach (@{$addrs}){
  1         2  
693 7         7 my $linenum = 1;
694 7         6 foreach my $line (@{$self->{LABELDEF}}) {
  7         11  
695 28         53 my @text = prepare_text($self, $_, $line, $w_arr[$lab]);
696 28 50       68 next if length(join('',@text)) == 0; # data-less line
697 28         159 $postscript .= "/sx $x_arr[$lab] def /y $y_arr[$lab] def\n";
698 28         50 $postscript .= "/cury y fontsize $linenum mul sub def\n";
699 28         29 $postscript .= "sx cury moveto\n";
700 28         25 foreach my $comp (@{$line}) {
  28         47  
701 49         2159 $postscript .= "/$self->{COMPONENTS}{$comp}->{font} findfont $fontsize scalefont setfont\n";
702 49         78 my $text = shift @text;
703 49 100       112 if ($self->{COMPONENTS}{$comp}->{'type'} eq 'bar') { # barcode
704             #$postscript .= "gsave\n";
705 7         8 $postscript .= "/fontsize 12 def\n";
706 7         14 $postscript .= "/PostNetJHC findfont fontsize scalefont setfont\n";
707 7         13 $postscript .= "/cury cury 15 sub def\n";
708 7         12 $postscript .= "/cury y fontsize $linenum mul sub def\n";
709 7         7 $postscript .= "sx cury moveto\n";
710 7         10 $postscript .= "($text) show\n";
711 7         22 $postscript .= "/$font findfont $fontsize scalefont setfont\n".
712             "/fontsize $fontsize def\n";
713             #$postscript .= "grestore\n";
714             }
715             else { # not barcode
716 42         88 $postscript .= "($text) show\n";
717             }
718             }
719 28         52 $linenum++;
720             }
721 7         8 $lab++;
722 7 50       20 if ($lab > $rows*$cols) { # end of page
723             # Add a free label, like page number, where ever user wishes
724             # token %page% is replaced with page number
725 0 0       0 if ($self->{FREETEXT}{Text} ne '') {
726 0         0 my $freetext = $self->{FREETEXT}{Text};
727 0         0 $freetext =~ s/%page%/$pageno/;
728 0         0 $postscript .= $self->{FREETEXT}{X}." ".$self->{FREETEXT}{Y}." moveto ($freetext) show\n";
729             }
730 0         0 $postscript .= "showpage\n% ------- start new page\n\n";
731 0         0 $pageno++;
732 0         0 $lab = 1;
733             }
734             }
735 1 50       6 if ($self->{FREETEXT}{Text} ne '') {
736 0         0 my $freetext = $self->{FREETEXT}{Text};
737 0         0 $freetext =~ s/%page%/$pageno/;
738 0         0 $postscript .= $self->{FREETEXT}{X}." ".$self->{FREETEXT}{Y}." moveto ($freetext) show\n";
739             }
740 1 50       4 $postscript .= "showpage\n% ------- end of data\n" unless $lab == 1;
741              
742 1         2 $postscript .= "grestore\n";
743              
744 1         24 return $postscript;
745             }
746              
747             sub prepare_text {
748 28     28 0 33 my $self = shift;
749 28         27 my $addrs = shift;
750 28         25 my $line = shift;
751 28         28 my $width = shift;
752              
753 28         27 my @text ; # array to be returned
754              
755             # If barcode, handle and return
756              
757 28 100       79 if ($self->{COMPONENTS}{$line->[0]}->{'type'} eq 'bar') { # trim barcode
758 7         17 my $text = $_->[$self->{COMPONENTS}{$line->[0]}->{'index'}];
759 7         18 $text = trimbar($self,$text,
760             $addrs->[$self->{COMPONENTS}{'street'}->{'index'}], $width);
761 7         21 return $text;
762             }
763              
764             # If it's not Standard Encoding, then I don't know how to calculate
765             # the string length, so I can't do any trimming for you. Maybe later...
766              
767 21 50       43 if ($self->{SETUP}{encoding} ne 'StandardEncoding') {
768 0         0 foreach my $comp (@{$line}) {
  0         0  
769 0         0 my $text = $addrs->[$self->{COMPONENTS}{$comp}->{'index'}] . " ";
770 0         0 push @text, escape($text);
771             }
772 0         0 chop $text[-1];
773 0         0 return @text;
774             }
775              
776             # Find longest adjustable string
777              
778 21         26 my $fontsize = $self->{SETUP}{fontsize};
779 21         31 my ($type, $adjcomp, $maxlen, $totlen) = (0,0,0,0);
780 21         23 my $strlen = $width;
781 21         21 foreach my $comp (@{$line}) {
  21         29  
782 42 50       107 if (!defined $addrs->[$self->{COMPONENTS}{$comp}->{'index'}]) {
783 0         0 print STDERR "Empty address field encountered. Use a blank in empty fields.\n";
784 0         0 $addrs->[$self->{COMPONENTS}{$comp}->{'index'}] = " ";
785             }
786 42         83 my $text = $addrs->[$self->{COMPONENTS}{$comp}->{'index'}] . " ";
787 42         125 my $length = stringlen($self,"$text",$self->{COMPONENTS}{$comp}->{font}, $fontsize);
788 42         53 $totlen += $length;
789 42 100       99 if ($self->{COMPONENTS}{$comp}->{'adj'} eq 'yes') {
790 28 100       50 if ($maxlen < $length) {
791 22         20 $maxlen = $length;
792 22         20 $adjcomp = $comp;
793 22         44 $type = $self->{COMPONENTS}{$comp}->{'type'};
794             }
795             }
796 42         57 push @text, escape($text);
797             }
798              
799             # trim back the longest adjustable string, if necessary
800              
801 21 50 33     99 if ($maxlen == 0 || $totlen <= $width) { # trimming not possible / needed
802 21         28 chop $text[-1];
803 21         68 return @text;
804             }
805              
806 0         0 $strlen -= ($totlen - $maxlen); # how much space is left?
807 0 0       0 if ($type eq 'name') {
    0          
    0          
808 0         0 $addrs->[$self->{COMPONENTS}{$adjcomp}->{'index'}] =
809             trimname ($self,$addrs->[$self->{COMPONENTS}{$adjcomp}->{'index'}],$strlen);
810             }
811             elsif ($type eq 'road') {
812 0         0 $addrs->[$self->{COMPONENTS}{$adjcomp}->{'index'}] =
813             trimaddr ($self,$addrs->[$self->{COMPONENTS}{$adjcomp}->{'index'}],$strlen);
814             }
815             elsif ($type eq 'place') {
816 0         0 $addrs->[$self->{COMPONENTS}{$adjcomp}->{'index'}] =
817             trimcity ($self,$addrs->[$self->{COMPONENTS}{$adjcomp}->{'index'}],$strlen);
818             }
819              
820             # build output array
821 0         0 @text = ();
822 0         0 foreach my $comp (@{$line}) {
  0         0  
823 0         0 my $text = $addrs->[$self->{COMPONENTS}{$comp}->{'index'}] . " ";
824 0         0 push @text, escape($text);
825             }
826 0         0 chop $text[-1];
827 0         0 return @text;
828             }
829              
830             sub escape {
831             # escape special characters
832 42     42 0 49 my $text = shift;
833 42         53 $text =~ s/\(/\\(/g;
834 42         45 $text =~ s/\)/\\)/g;
835 42         55 $text =~ s/([\200-\377])/sprintf "\\%3.3o", ord $1/eg;
  0         0  
836 42         169 return $text;
837             }
838              
839             # ****************************************************************
840             # Intelligently trim back name if needed
841             sub trimname {
842 0     0 0 0 my $self = shift;
843 0         0 my $name = shift;
844 0         0 my $width = shift;
845              
846 0         0 $name =~ s/^\s*//;
847 0         0 $name =~ s/\s*$//;
848              
849 0         0 my $strwidth = stringwidth($self,"$name");
850            
851 0 0       0 if ($strwidth > $width) {
852 0         0 my $nchar = (($strwidth-$width)/$strwidth)*length($name); # approx # extra chars
853              
854 0 0       0 if ($name =~ / and / ) {
855 0         0 $name =~ s/ and / & /;
856 0         0 $nchar -= 2;
857             }
858 0 0       0 if ($nchar > 0) {
859             # Trim first names, leaving last name intact
860 0         0 $name = substr($name,0,(length($name)-$nchar));
861             }
862 0         0 $strwidth = stringwidth($self,$name);
863 0 0       0 if ($strwidth > $width) {chop $name;}
  0         0  
864             }
865 0         0 return $name;
866             }
867              
868             # ****************************************************************
869             # Intelligently trim back street address if needed
870             sub trimaddr {
871 0     0 0 0 my ($self, $addr, $width) = @_;
872              
873 0         0 $addr =~ s/^\s*//;
874 0         0 $addr =~ s/\s*$//;
875              
876 0         0 my $strwidth = stringwidth($self,$addr);
877            
878 0 0       0 if ($strwidth > $width) {
879 0         0 $addr =~ s/\.//g;
880 0         0 $addr =~ s/\s*rd$//i;
881 0         0 $addr =~ s/\s*ave$//i;
882 0         0 $addr =~ s/\s*st$//i;
883 0         0 $addr =~ s/\s*ln$//i;
884 0         0 $strwidth = stringwidth($self,$addr);
885 0 0       0 if ($strwidth > $width) {
886 0         0 my $nchar = (($strwidth-$width)/$strwidth)*length($addr); # approx # extra chars
887 0         0 $addr = substr($addr,0,(length($addr)-$nchar));
888             }
889             }
890 0         0 return $addr;
891             }
892              
893             # ****************************************************************
894             # Intelligently trim back city if needed
895             sub trimcity {
896 0     0 0 0 my ($self, $city, $width) = @_;
897              
898 0         0 $city =~ s/^\s*//;
899 0         0 $city =~ s/\s*$//;
900              
901 0         0 my $strwidth = stringwidth($self,$city);
902            
903 0 0       0 if ($strwidth > $width) {
904 0         0 my $nchar = (($strwidth-$width)/$strwidth)*length($city); # approx # extra chars
905 0         0 $city = substr($city,0,(length($city)-$nchar));
906             }
907 0         0 return $city;
908             }
909              
910             # ****************************************************************
911             # Intelligently set up for barcode
912             sub trimbar {
913 7     7 0 10 my ($self, $zip, $street, $width) = @_;
914              
915 7 50 33     30 if (!defined $zip || length($zip) < 5) {
916 0         0 return ' ';
917             }
918              
919 7         25 $zip =~ s/^\s*//;
920 7         26 $zip =~ s/\s*$//;
921 7         16 $street =~ s/^\s*//;
922 7         31 $street =~ s/\s*$//;
923              
924 7         10 $zip =~ s/\-//;
925 7 50       16 if ($zip =~ /[^0-9]/) {
926 0         0 print STDERR "not a US zipcode : $zip\n";
927 0         0 return ' ';
928             }
929              
930 7         17 my $keepfont = $self->{SETUP}{font};
931 7         8 my $keepfontsize = $self->{SETUP}{fontsize};
932 7         10 $self->{SETUP}{font} = 'PostNetJHC';
933 7         8 $self->{SETUP}{fontsize} = 12;
934              
935 7         13 my $zip5 = substr($zip,0,5);
936 7         12 my $zipcode = 'I' . $zip5 . chksum($zip5) . "I";
937              
938 7         25 my $strwidth = stringwidth($self,$zipcode);
939 7 50       17 if ($strwidth > $width) { # can't make it short enough
940 0         0 $self->{SETUP}{font} = $keepfont;
941 0         0 $self->{SETUP}{fontsize} = $keepfontsize;
942 0         0 return ' ';
943             }
944              
945 7 100       15 if (length($zip) == 5) {
946 6         9 $self->{SETUP}{font} = $keepfont;
947 6         9 $self->{SETUP}{fontsize} = $keepfontsize;
948 6         11 return $zipcode;
949             }
950              
951 1 50       4 if (length($zip) != 9) {
952 0         0 print STDERR "error in zipcode $zip\n";
953 0         0 $self->{SETUP}{font} = $keepfont;
954 0         0 $self->{SETUP}{fontsize} = $keepfontsize;
955 0         0 return $zipcode;
956             }
957            
958 1         4 my $zip_plus = 'I' . $zip . chksum($zip) . "I";
959 1         5 $strwidth = stringwidth($self,$zip_plus);
960 1         4 $self->{SETUP}{font} = $keepfont;
961 1         2 $self->{SETUP}{fontsize} = $keepfontsize;
962 1 50       6 if ($strwidth > $width) {
963 1         3 return $zipcode;
964             }
965 0         0 return $zip_plus;
966             }
967              
968             sub chksum {
969 8     8 0 8 my $num = shift;
970 8         482 return (10 - eval(join('+',(split(//,$num))))%10)%10;
971             }
972              
973             # ****************************************************************
974             # return label description : Output_Left Output_Top Output_Width Output_Height
975              
976             sub labeldata {
977 0     0 0 0 my $self = shift;
978              
979 0         0 return [ $self->{SETUP}{output_left},
980             $self->{SETUP}{output_top},
981             $self->{SETUP}{output_width},
982             $self->{SETUP}{output_height},
983             ];
984             }
985              
986             # ****************************************************************
987             # return the avery layout code given a product code
988             sub averycode {
989 1     1 0 6 my $self = shift;
990 1         1 my $product = shift;
991              
992             # layout=>[paper-size,[list of product codes], description,
993             # number per sheet, left-offset, top-offset, width, height]
994             # distances measured in points
995              
996 1         2 foreach (keys %{$self->{DATA}{AVERY}}) {
  1         11  
997 45 100       53 if (grep /$product/,@{$self->{DATA}{AVERY}{$_}->[1]}) {
  45         233  
998 1         6 return $_;
999             }
1000             }
1001              
1002 0         0 return 0;
1003             }
1004              
1005             # ****************************************************************
1006             # return the avery data
1007             sub averydata {
1008 0     0 0 0 my $self = shift;
1009              
1010             # layout=>[paper-size,[list of product codes], description,
1011             # number per sheet, left-offset, top-offset, width, height,
1012             # x-gap, y-gap]
1013             # distances measured in points
1014              
1015 0         0 return $self->{DATA}{AVERY};
1016             }
1017              
1018             # ****************************************************************
1019             # return the dymo layout code given a product code
1020             sub dymocode {
1021 0     0 0 0 my $self = shift;
1022 0         0 my $product = shift;
1023              
1024             # layout=>[paper-size,[list of product codes], description,
1025             # number per sheet, left-offset, top-offset, width, height]
1026             # distances measured in points
1027              
1028 0         0 foreach (keys %{$self->{DATA}{DYMO}}) {
  0         0  
1029 0 0       0 if (grep /$product/,@{$self->{DATA}{DYMO}{$_}->[1]}) {
  0         0  
1030 0         0 return $_;
1031             }
1032             }
1033              
1034 0         0 return 0;
1035             }
1036              
1037             # ****************************************************************
1038             # return the dymo data
1039             sub dymodata {
1040 0     0 0 0 my $self = shift;
1041              
1042             # layout=>[paper-size,[list of product codes], description,
1043             # number per sheet, left-offset, top-offset, width, height,
1044             # x-gap, y-gap]
1045             # distances measured in points
1046              
1047 0         0 return $self->{DATA}{DYMO};
1048             }
1049              
1050             # ****************************************************************
1051             # Return width & height of paper
1052             sub papersize {
1053 14     14 0 19 my $self = shift;
1054 14         18 my $logical = shift;
1055              
1056 14         33 my($width) = $self->{DATA}->{WIDTH}{$self->{SETUP}{papersize}};
1057 14         23 my($height) = $self->{DATA}->{HEIGHT}{$self->{SETUP}{papersize}};
1058              
1059 14 50 66     45 if (!$logical || $self->{SETUP}{orientation} eq 'portrait') {
1060 14         44 return [$width, $height];
1061             }
1062             else {
1063 0         0 return [$height, $width];
1064             }
1065             }
1066              
1067             # ****************************************************************
1068             # Return paper names
1069             sub papers {
1070 0     0 0 0 my $self = shift;
1071              
1072 0         0 return $self->{DATA}->{PAPER};
1073             }
1074              
1075              
1076             sub stringlen {
1077 42     42 0 60 my ($self,$string,$fontname,$fontsize) = @_;
1078 42         36 my $returnval = 0;
1079            
1080 42         86 foreach my $char (unpack("C*",$string)) {
1081 407         621 $returnval+=$self->{DATA}->{FONTS}{$fontname}->[$char-32];
1082             }
1083 42         93 return ($returnval*$fontsize/1000);
1084              
1085             }
1086              
1087             sub stringwidth {
1088 8     8 0 11 my ($self,$string,) = @_;
1089 8         10 my $returnval = 0;
1090 8         15 my $fontname = $self->{SETUP}{font};
1091 8         9 my $fontsize = $self->{SETUP}{fontsize};
1092            
1093 8         19 foreach my $char (unpack("C*",$string)) {
1094 68         111 $returnval+=$self->{DATA}->{FONTS}{$fontname}->[$char-32];
1095             }
1096 8         19 return ($returnval*$fontsize/1000);
1097              
1098             }
1099              
1100              
1101             sub ListFonts {
1102 2     2 0 4 my $self = shift;
1103 2         2 my @tmp = %{$self->{DATA}->{FONTS}};
  2         71  
1104 2         11 my @returnval =();
1105 2         6 while (@tmp) {
1106 160         181 push @returnval, shift(@tmp);
1107 160         232 shift @tmp;
1108             }
1109 2         10 return sort( {$a cmp $b;} @returnval);
  820         708  
1110             }
1111              
1112             1;
1113             __END__