kohabug 2475 [2/2] Porting LCCN splitting code to Labels.pm
[koha.git] / C4 / Labels.pm
1 package C4::Labels;
2
3 # Copyright 2006 Katipo Communications.
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 use strict;
21 use vars qw($VERSION @ISA @EXPORT);
22
23 use PDF::Reuse;
24 use Text::Wrap;
25 use Algorithm::CheckDigits;
26 use C4::Members;
27 use C4::Branch;
28 use C4::Debug;
29 use C4::Biblio;
30 use Text::CSV_XS;
31 use Data::Dumper;
32
33 BEGIN {
34         $VERSION = 0.03;
35         require Exporter;
36         @ISA    = qw(Exporter);
37         @EXPORT = qw(
38                 &get_label_options &GetLabelItems
39                 &build_circ_barcode &draw_boundaries
40                 &drawbox &GetActiveLabelTemplate
41                 &GetAllLabelTemplates &DeleteTemplate
42                 &GetSingleLabelTemplate &SaveTemplate
43                 &CreateTemplate &SetActiveTemplate
44                 &SaveConf &DrawSpineText &GetTextWrapCols
45                 &GetUnitsValue &DrawBarcode &DrawPatronCardText
46                 &get_printingtypes &GetPatronCardItems
47                 &get_layouts
48                 &get_barcode_types
49                 &get_batches &delete_batch
50                 &add_batch &printText
51                 &GetItemFields
52                 &get_text_fields
53                 get_layout &save_layout &add_layout
54                 &set_active_layout
55                 &build_text_dropbox
56                 &delete_layout &get_active_layout
57                 &get_highest_batch
58                 &deduplicate_batch
59                 &GetAllPrinterProfiles &GetSinglePrinterProfile
60                 &SaveProfile &CreateProfile &DeleteProfile
61                 &GetAssociatedProfile &SetAssociatedProfile
62         );
63 }
64
65
66 =head1 NAME
67
68 C4::Labels - Functions for printing spine labels and barcodes in Koha
69
70 =head1 FUNCTIONS
71
72 =over 2
73
74 =item get_label_options;
75
76         $options = get_label_options()
77
78 Return a pointer on a hash list containing info from labels_conf table in Koha DB.
79
80 =cut
81
82 sub get_label_options {
83     my $query2 = " SELECT * FROM labels_conf where active = 1";         # FIXME: exact same as get_active_layout
84     my $sth    = C4::Context->dbh->prepare($query2);
85     $sth->execute();
86     return $sth->fetchrow_hashref;
87 }
88
89 sub get_layouts {
90     my $dbh = C4::Context->dbh;
91     my @data;
92     my $query = " Select * from labels_conf";
93     my $sth   = $dbh->prepare($query);
94     $sth->execute();
95     my @resultsloop;
96     while ( my $data = $sth->fetchrow_hashref ) {
97
98         $data->{'fieldlist'} = get_text_fields( $data->{'id'} );
99         push( @resultsloop, $data );
100     }
101     $sth->finish;
102     return @resultsloop;
103 }
104
105 sub get_layout {
106     my ($layout_id) = @_;
107     my $dbh = C4::Context->dbh;
108
109     # get the actual items to be printed.
110     my $query = " Select * from labels_conf where id = ?";
111     my $sth   = $dbh->prepare($query);
112     $sth->execute($layout_id);
113     my $data = $sth->fetchrow_hashref;
114     $sth->finish;
115     return $data;
116 }
117
118 sub get_active_layout {
119     my $query = " Select * from labels_conf where active = 1";          # FIXME: exact same as get_label_options
120     my $sth   = C4::Context->dbh->prepare($query);
121     $sth->execute();
122     return $sth->fetchrow_hashref;
123 }
124
125 sub delete_layout {
126     my ($layout_id) = @_;
127     my $dbh = C4::Context->dbh;
128
129     # get the actual items to be printed.
130     my $query = "delete from  labels_conf where id = ?";
131     my $sth   = $dbh->prepare($query);
132     $sth->execute($layout_id);
133     $sth->finish;
134 }
135
136 sub get_printingtypes {
137     my ($layout_id) = @_;
138     my @printtypes;
139 # FIXME hard coded print types
140     push( @printtypes, { code => 'BAR',    desc => "barcode only" } );
141     push( @printtypes, { code => 'BIB',    desc => "biblio only" } );
142     push( @printtypes, { code => 'BARBIB', desc => "barcode / biblio" } );
143     push( @printtypes, { code => 'BIBBAR', desc => "biblio / barcode" } );
144     push( @printtypes, { code => 'ALT',    desc => "alternating labels" } );
145     push( @printtypes, { code => 'CSV',    desc => "csv output" } );
146     push( @printtypes, { code => 'PATCRD', desc => "patron cards" } );
147
148     my $conf             = get_layout($layout_id);
149     my $active_printtype = $conf->{'printingtype'};
150
151     # lop thru layout, insert selected to hash
152
153     foreach my $printtype (@printtypes) {
154         if ( $printtype->{'code'} eq $active_printtype ) {
155             $printtype->{'active'} = 1;
156         }
157     }
158     return @printtypes;
159 }
160
161 # this sub (build_text_dropbox) is deprecated and should be deleted. 
162 # rch 2008.04.15
163 #
164 sub build_text_dropbox {
165     my ($order) = @_;
166     my $field_count = 7;    # <-----------       FIXME hard coded
167     my @lines;
168     !$order
169       ? push( @lines, { num => '', selected => '1' } )
170       : push( @lines, { num => '' } );
171     for ( my $i = 1 ; $i <= $field_count ; $i++ ) {
172         my $line = { num => "$i" };
173         $line->{'selected'} = 1 if $i eq $order;
174         push( @lines, $line );
175     }
176     return @lines;
177 }
178
179 sub get_text_fields {
180     my ( $layout_id, $sorttype ) = @_;
181     my @sorted_fields;
182     my $error;
183     my $sortorder = get_layout($layout_id);
184     if ( $sortorder->{formatstring} ) {
185         if ( !$sorttype ) {
186             return $sortorder->{formatstring};
187         }
188         else {
189             my $csv    = Text::CSV_XS->new( { allow_whitespace => 1 } );
190             my $line   = $sortorder->{formatstring};
191             my $status = $csv->parse($line);
192             @sorted_fields =
193               map { { 'code' => $_, desc => $_ } } $csv->fields();
194             $error = $csv->error_input();
195             warn $error if $error;    # TODO - do more with this.
196         }
197     }
198     else {
199
200      # These fields are hardcoded based on the template for label-edit-layout.pl
201         my @text_fields = (
202             {
203                 code  => 'itemtype',
204                 desc  => "Item Type",
205                 order => $sortorder->{'itemtype'}
206             },
207             {
208                 code  => 'issn',
209                 desc  => "ISSN",
210                 order => $sortorder->{'issn'}
211             },
212             {
213                 code  => 'isbn',
214                 desc  => "ISBN",
215                 order => $sortorder->{'isbn'}
216             },
217             {
218                 code  => 'barcode',
219                 desc  => "Barcode",
220                 order => $sortorder->{'barcode'}
221             },
222             {
223                 code  => 'author',
224                 desc  => "Author",
225                 order => $sortorder->{'author'}
226             },
227             {
228                 code  => 'title',
229                 desc  => "Title",
230                 order => $sortorder->{'title'}
231             },
232             {
233                 code  => 'itemcallnumber',
234                 desc  => "Call Number",
235                 order => $sortorder->{'itemcallnumber'}
236             },
237         );
238
239         my @new_fields = ();
240         foreach my $field (@text_fields) {
241             push( @new_fields, $field ) if $field->{'order'} > 0;
242         }
243
244         @sorted_fields = sort { $$a{order} <=> $$b{order} } @new_fields;
245     }
246
247     # if we have a 'formatstring', then we ignore these hardcoded fields.
248     my $active_fields;
249
250     if ( $sorttype eq 'codes' )
251     { # FIXME: This sub should really always return the array of hashrefs and let the caller take what he wants from that -fbcit
252         return @sorted_fields;
253     }
254     else {
255         foreach my $field (@sorted_fields) {
256             $active_fields .= "$field->{'desc'} ";
257         }
258         return $active_fields;
259     }
260
261 }
262
263 =head2 sub add_batch
264 =over 4
265  add_batch($batch_type,\@batch_list);
266  if $batch_list is supplied,
267    create a new batch with those items.
268  else, return the next available batch_id.
269 =return
270 =cut
271
272 sub add_batch ($;$) {
273         my $table = (@_ and 'patroncards' eq shift) ? 'patroncards' : 'labels';
274     my $batch_list = (@_) ? shift : undef;
275     my $dbh = C4::Context->dbh;
276     my $q ="SELECT MAX(DISTINCT batch_id) FROM $table";
277     my $sth = $dbh->prepare($q);
278     $sth->execute();
279     my ($batch_id) = $sth->fetchrow_array || 0;
280         $batch_id++;
281         if ($batch_list) {
282                 if ($table eq 'patroncards') {
283                         $sth = $dbh->prepare("INSERT INTO $table (`batch_id`,`borrowernumber`) VALUES (?,?)"); 
284                 } else {
285                         $sth = $dbh->prepare("INSERT INTO $table (`batch_id`,`itemnumber`    ) VALUES (?,?)"); 
286                 }
287                 for (@$batch_list) {
288                         $sth->execute($batch_id,$_);
289                 }
290         }
291         return $batch_id;
292 }
293
294 #FIXME: Needs to be ported to receive $batch_type
295 # ... this looks eerily like add_batch() ...
296 sub get_highest_batch {
297         my $table = (@_ and 'patroncards' eq shift) ? 'patroncards' : 'labels';
298     my $q =
299       "select distinct batch_id from $table order by batch_id desc limit 1";
300     my $sth = C4::Context->dbh->prepare($q);
301     $sth->execute();
302     my $data = $sth->fetchrow_hashref or return 1;
303         return ($data->{'batch_id'} || 1);
304 }
305
306
307 sub get_batches (;$) {
308         my $table = (@_ and 'patroncards' eq shift) ? 'patroncards' : 'labels';
309     my $q   = "SELECT batch_id, COUNT(*) AS num FROM $table GROUP BY batch_id";
310     my $sth = C4::Context->dbh->prepare($q);
311     $sth->execute();
312         my $batches = $sth->fetchall_arrayref({});
313         return @$batches;
314 }
315
316 sub delete_batch {
317     my ($batch_id, $batch_type) = @_;
318     warn "Deleteing batch of type $batch_type";
319     my $dbh        = C4::Context->dbh;
320     my $q          = "DELETE FROM $batch_type WHERE batch_id  = ?";
321     my $sth        = $dbh->prepare($q);
322     $sth->execute($batch_id);
323     $sth->finish;
324 }
325
326 sub get_barcode_types {
327     my ($layout_id) = @_;
328     my $layout      = get_layout($layout_id);
329     my $barcode     = $layout->{'barcodetype'};
330     my @array;
331
332     push( @array, { code => 'CODE39',      desc => 'Code 39' } );
333     push( @array, { code => 'CODE39MOD',   desc => 'Code39 + Modulo43' } );
334     push( @array, { code => 'CODE39MOD10', desc => 'Code39 + Modulo10' } ); 
335     push( @array, { code => 'ITF',         desc => 'Interleaved 2 of 5' } );
336
337     foreach my $line (@array) {
338         if ( $line->{'code'} eq $barcode ) {
339             $line->{'active'} = 1;
340         }
341
342     }
343     return @array;
344 }
345
346 sub GetUnitsValue {
347     my ($units) = @_;
348     my $unitvalue;
349
350     $unitvalue = '1'          if ( $units eq 'POINT' );
351     $unitvalue = '2.83464567' if ( $units eq 'MM' );
352     $unitvalue = '28.3464567' if ( $units eq 'CM' );
353     $unitvalue = 72           if ( $units eq 'INCH' );
354     return $unitvalue;
355 }
356
357 sub GetTextWrapCols {
358     my ( $font, $fontsize, $label_width, $left_text_margin ) = @_;
359     my $string = '0';
360     my $strwidth;
361     my $count = 0;
362 #    my $textlimit = $label_width - ($left_text_margin);
363     my $textlimit = $label_width - ( 3 * $left_text_margin);
364
365     while ( $strwidth < $textlimit ) {
366         $strwidth = prStrWidth( $string, $font, $fontsize );
367         $string = $string . '0';
368         #warn "strwidth:$strwidth, textlimit:$textlimit, count:$count string:$string";
369         $count++;
370     }
371     return $count;
372 }
373
374 sub GetActiveLabelTemplate {
375     my $dbh   = C4::Context->dbh;
376     my $query = " SELECT * FROM labels_templates where active = 1 limit 1";
377     my $sth   = $dbh->prepare($query);
378     $sth->execute();
379     my $active_tmpl = $sth->fetchrow_hashref;
380     $sth->finish;
381     return $active_tmpl;
382 }
383
384 sub GetSingleLabelTemplate {
385     my ($tmpl_id) = @_;
386     my $dbh       = C4::Context->dbh;
387     my $query     = " SELECT * FROM labels_templates where tmpl_id = ?";
388     my $sth       = $dbh->prepare($query);
389     $sth->execute($tmpl_id);
390     my $template = $sth->fetchrow_hashref;
391     $sth->finish;
392     return $template;
393 }
394
395 sub SetActiveTemplate {
396
397     my ($tmpl_id) = @_;
398   
399     my $dbh   = C4::Context->dbh;
400     my $query = " UPDATE labels_templates SET active = NULL";
401     my $sth   = $dbh->prepare($query);
402     $sth->execute();
403
404     $query = "UPDATE labels_templates SET active = 1 WHERE tmpl_id = ?";
405     $sth   = $dbh->prepare($query);
406     $sth->execute($tmpl_id);
407     $sth->finish;
408 }
409
410 sub set_active_layout {
411
412     my ($layout_id) = @_;
413     my $dbh         = C4::Context->dbh;
414     my $query       = " UPDATE labels_conf SET active = NULL";
415     my $sth         = $dbh->prepare($query);
416     $sth->execute();
417
418     $query = "UPDATE labels_conf SET active = 1 WHERE id = ?";
419     $sth   = $dbh->prepare($query);
420     $sth->execute($layout_id);
421     $sth->finish;
422 }
423
424 sub DeleteTemplate {
425     my ($tmpl_id) = @_;
426     my $dbh       = C4::Context->dbh;
427     my $query     = " DELETE  FROM labels_templates where tmpl_id = ?";
428     my $sth       = $dbh->prepare($query);
429     $sth->execute($tmpl_id);
430     $sth->finish;
431 }
432
433 sub SaveTemplate {
434     my (
435         $tmpl_id,     $tmpl_code,   $tmpl_desc,    $page_width,
436         $page_height, $label_width, $label_height, $topmargin,
437         $leftmargin,  $cols,        $rows,         $colgap,
438         $rowgap,      $font,        $fontsize,     $units
439     ) = @_;
440     $debug and warn "Passed \$font:$font";
441     my $dbh = C4::Context->dbh;
442     my $query =
443       " UPDATE labels_templates SET tmpl_code=?, tmpl_desc=?, page_width=?,
444                page_height=?, label_width=?, label_height=?, topmargin=?,
445                leftmargin=?, cols=?, rows=?, colgap=?, rowgap=?, font=?, fontsize=?,
446                            units=? 
447                   WHERE tmpl_id = ?";
448
449     my $sth = $dbh->prepare($query);
450     $sth->execute(
451         $tmpl_code,   $tmpl_desc,    $page_width, $page_height,
452         $label_width, $label_height, $topmargin,  $leftmargin,
453         $cols,        $rows,         $colgap,     $rowgap,
454         $font,        $fontsize,     $units,      $tmpl_id
455     );
456     my $dberror = $sth->errstr;
457     $sth->finish;
458     return $dberror;
459 }
460
461 sub CreateTemplate {
462     my $tmpl_id;
463     my (
464         $tmpl_code,   $tmpl_desc,    $page_width, $page_height,
465         $label_width, $label_height, $topmargin,  $leftmargin,
466         $cols,        $rows,         $colgap,     $rowgap,
467         $font,        $fontsize,     $units
468     ) = @_;
469
470     my $dbh = C4::Context->dbh;
471
472     my $query = "INSERT INTO labels_templates (tmpl_code, tmpl_desc, page_width,
473                          page_height, label_width, label_height, topmargin,
474                          leftmargin, cols, rows, colgap, rowgap, font, fontsize, units)
475                          VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
476
477     my $sth = $dbh->prepare($query);
478     $sth->execute(
479         $tmpl_code,   $tmpl_desc,    $page_width, $page_height,
480         $label_width, $label_height, $topmargin,  $leftmargin,
481         $cols,        $rows,         $colgap,     $rowgap,
482         $font,        $fontsize,    $units
483     );
484     my $dberror = $sth->errstr;
485     $sth->finish;
486     return $dberror;
487 }
488
489 sub GetAllLabelTemplates {
490     my $dbh = C4::Context->dbh;
491
492     # get the actual items to be printed.
493     my @data;
494     my $query = " Select * from labels_templates ";
495     my $sth   = $dbh->prepare($query);
496     $sth->execute();
497     my @resultsloop;
498     while ( my $data = $sth->fetchrow_hashref ) {
499         push( @resultsloop, $data );
500     }
501     $sth->finish;
502
503     #warn Dumper @resultsloop;
504     return @resultsloop;
505 }
506
507 #sub SaveConf {
508 sub add_layout {
509
510     my (
511         $barcodetype,  $title,          $subtitle,      $isbn,       $issn,
512         $itemtype,     $bcn,            $dcn,        $classif,
513         $subclass,     $itemcallnumber, $author,     $tmpl_id,
514         $printingtype, $guidebox,       $startlabel, $layoutname, $formatstring
515     ) = @_;
516
517     my $dbh    = C4::Context->dbh;
518     my $query2 = "update labels_conf set active = NULL";
519     my $sth2   = $dbh->prepare($query2);
520     $sth2->execute();
521     $query2 = "INSERT INTO labels_conf
522             ( barcodetype, title, subtitle, isbn,issn, itemtype, barcode,
523               dewey, classification, subclass, itemcallnumber, author, printingtype,
524                 guidebox, startlabel, layoutname, formatstring, active )
525                values ( ?, ?,?, ?, ?, ?, ?, ?,  ?,?, ?, ?, ?, ?, ?,?,?, 1 )";
526     $sth2 = $dbh->prepare($query2);
527     $sth2->execute(
528         $barcodetype, $title, $subtitle, $isbn, $issn,
529
530         $itemtype, $bcn,            $dcn,    $classif,
531         $subclass, $itemcallnumber, $author, $printingtype,
532         $guidebox, $startlabel,     $layoutname, $formatstring
533     );
534     $sth2->finish;
535
536     SetActiveTemplate($tmpl_id);
537     return;
538 }
539
540 sub save_layout {
541
542     my (
543         $barcodetype,  $title,          $subtitle,      $isbn,       $issn,
544         $itemtype,     $bcn,            $dcn,        $classif,
545         $subclass,     $itemcallnumber, $author,     $tmpl_id,
546         $printingtype, $guidebox,       $startlabel, $layoutname, $formatstring,
547         $layout_id
548     ) = @_;
549 ### $layoutname
550 ### $layout_id
551
552     my $dbh    = C4::Context->dbh;
553     my $query2 = "update labels_conf set 
554              barcodetype=?, title=?, subtitle=?, isbn=?,issn=?, 
555             itemtype=?, barcode=?,    dewey=?, classification=?,
556              subclass=?, itemcallnumber=?, author=?,  printingtype=?,  
557                guidebox=?, startlabel=?, layoutname=?, formatstring=? where id = ?";
558     my $sth2 = $dbh->prepare($query2);
559     $sth2->execute(
560         $barcodetype, $title,          $subtitle,       $isbn,       $issn,
561         $itemtype,    $bcn,            $dcn,        $classif,
562         $subclass,    $itemcallnumber, $author,     $printingtype,
563         $guidebox,    $startlabel,     $layoutname, $formatstring,  $layout_id
564     );
565     $sth2->finish;
566
567     return;
568 }
569
570 =item GetAllPrinterProfiles;
571
572     @profiles = GetAllPrinterProfiles()
573
574 Returns an array of references-to-hash, whos keys are .....
575
576 =cut
577
578 sub GetAllPrinterProfiles {
579
580     my $dbh = C4::Context->dbh;
581     my @data;
582     my $query = "SELECT * FROM printers_profile AS pp INNER JOIN labels_templates AS lt ON pp.tmpl_id = lt.tmpl_id; ";
583     my $sth = $dbh->prepare($query);
584     $sth->execute();
585     my @resultsloop;
586     while ( my $data = $sth->fetchrow_hashref ) {
587         push( @resultsloop, $data );
588     }
589     $sth->finish;
590
591     return @resultsloop;
592 }
593
594 =item GetSinglePrinterProfile;
595
596     $profile = GetSinglePrinterProfile()
597
598 Returns a hashref whos keys are...
599
600 =cut
601
602 sub GetSinglePrinterProfile {
603     my ($prof_id) = @_;
604     my $dbh       = C4::Context->dbh;
605     my $query     = " SELECT * FROM printers_profile WHERE prof_id = ?; ";
606     my $sth       = $dbh->prepare($query);
607     $sth->execute($prof_id);
608     my $template = $sth->fetchrow_hashref;
609     $sth->finish;
610     return $template;
611 }
612
613 =item SaveProfile;
614
615     SaveProfile('parameters')
616
617 When passed a set of parameters, this function updates the given profile with the new parameters.
618
619 =cut
620
621 sub SaveProfile {
622     my (
623         $prof_id,       $offset_horz,   $offset_vert,   $creep_horz,    $creep_vert,    $units
624     ) = @_;
625     my $dbh = C4::Context->dbh;
626     my $query =
627       " UPDATE printers_profile
628         SET offset_horz=?, offset_vert=?, creep_horz=?, creep_vert=?, unit=? 
629         WHERE prof_id = ? ";
630     my $sth = $dbh->prepare($query);
631     $sth->execute(
632         $offset_horz,   $offset_vert,   $creep_horz,    $creep_vert,    $units,         $prof_id
633     );
634     $sth->finish;
635 }
636
637 =item CreateProfile;
638
639     CreateProfile('parameters')
640
641 When passed a set of parameters, this function creates a new profile containing those parameters
642 and returns any errors.
643
644 =cut
645
646 sub CreateProfile {
647     my (
648         $prof_id,       $printername,   $paper_bin,     $tmpl_id,     $offset_horz,
649         $offset_vert,   $creep_horz,    $creep_vert,    $units
650     ) = @_;
651     my $dbh = C4::Context->dbh;
652     my $query = 
653         " INSERT INTO printers_profile (prof_id, printername, paper_bin, tmpl_id,
654                                         offset_horz, offset_vert, creep_horz, creep_vert, unit)
655           VALUES(?,?,?,?,?,?,?,?,?) ";
656     my $sth = $dbh->prepare($query);
657     $sth->execute(
658         $prof_id,       $printername,   $paper_bin,     $tmpl_id,     $offset_horz,
659         $offset_vert,   $creep_horz,    $creep_vert,    $units
660     );
661     my $error =  $sth->errstr;
662     $sth->finish;
663     return $error;
664 }
665
666 =item DeleteProfile;
667
668     DeleteProfile(prof_id)
669
670 When passed a profile id, this function deletes that profile from the database and returns any errors.
671
672 =cut
673
674 sub DeleteProfile {
675     my ($prof_id) = @_;
676     my $dbh       = C4::Context->dbh;
677     my $query     = " DELETE FROM printers_profile WHERE prof_id = ?";
678     my $sth       = $dbh->prepare($query);
679     $sth->execute($prof_id);
680     my $error = $sth->errstr;
681     $sth->finish;
682     return $error;
683 }
684
685 =item GetAssociatedProfile;
686
687     $assoc_prof = GetAssociatedProfile(tmpl_id)
688
689 When passed a template id, this function returns the parameters from the currently associated printer profile
690 in a hashref where key=fieldname and value=fieldvalue.
691
692 =cut
693
694 sub GetAssociatedProfile {
695     my ($tmpl_id) = @_;
696     my $dbh   = C4::Context->dbh;
697     # First we find out the prof_id for the associated profile...
698     my $query = "SELECT * FROM labels_profile WHERE tmpl_id = ?";
699     my $sth   = $dbh->prepare($query);
700     $sth->execute($tmpl_id);
701     my $assoc_prof = $sth->fetchrow_hashref;
702     $sth->finish;
703     # Then we retrieve that profile and return it to the caller...
704     $assoc_prof = GetSinglePrinterProfile($assoc_prof->{'prof_id'});
705     return $assoc_prof;
706 }
707
708 =item SetAssociatedProfile;
709
710     SetAssociatedProfile($prof_id, $tmpl_id)
711
712 When passed both a profile id and template id, this function establishes an association between the two. No more
713 than one profile may be associated with any given template at the same time.
714
715 =cut
716
717 sub SetAssociatedProfile {
718
719     my ($prof_id, $tmpl_id) = @_;
720   
721     my $dbh = C4::Context->dbh;
722     my $query = "INSERT INTO labels_profile (prof_id, tmpl_id) VALUES (?,?) ON DUPLICATE KEY UPDATE prof_id = ?";
723     my $sth = $dbh->prepare($query);
724     $sth->execute($prof_id, $tmpl_id, $prof_id);
725     $sth->finish;
726 }
727
728
729 =item GetLabelItems;
730
731         $options = GetLabelItems()
732
733 Returns an array of references-to-hash, whos keys are the fields from the biblio, biblioitems, items and labels tables in the Koha database.
734
735 =cut
736
737 sub GetLabelItems {
738     my ($batch_id) = @_;
739     my $dbh = C4::Context->dbh;
740
741     my @resultsloop = ();
742     my $count;
743     my @data;
744     my $sth;
745     
746     if ($batch_id) {
747         my $query3 = "
748             SELECT *
749             FROM labels
750             WHERE batch_id = ?
751             ORDER BY labelid";
752         $sth = $dbh->prepare($query3);
753         $sth->execute($batch_id);
754     }
755     else {
756         my $query3 = "
757             SELECT *
758             FROM labels";
759         $sth = $dbh->prepare($query3);
760         $sth->execute();
761     }
762     my $cnt = $sth->rows;
763     my $i1  = 1;
764     while ( my $data = $sth->fetchrow_hashref ) {
765
766         # lets get some summary info from each item
767         my $query1 =
768 #            FIXME This makes for a very bulky data structure; data from tables w/duplicate col names also gets overwritten...
769 #            SELECT i.barcode, i.itemcallnumber, i.itype, bi.isbn, bi.issn, b.title, b.author
770            "SELECT i.*, bi.*, b.*
771             FROM items AS i, biblioitems AS bi ,biblio AS b
772             WHERE itemnumber=? AND i.biblioitemnumber=bi.biblioitemnumber AND bi.biblionumber=b.biblionumber";
773         my $sth1 = $dbh->prepare($query1);
774         $sth1->execute( $data->{'itemnumber'} );
775
776         my $data1 = $sth1->fetchrow_hashref();
777         $data1->{'labelno'}  = $i1;
778         $data1->{'labelid'}  = $data->{'labelid'};
779         $data1->{'batch_id'} = $batch_id;
780         $data1->{'summary'} = "$data1->{'barcode'}, $data1->{'title'}, $data1->{'isbn'}";
781
782         push( @resultsloop, $data1 );
783         $sth1->finish;
784
785         $i1++;
786     }
787     $sth->finish;
788     return @resultsloop;
789
790 }
791
792 sub GetItemFields {
793     my @fields = qw (
794       barcode           title
795       isbn              issn
796       author            itemtype
797       itemcallnumber
798     );
799     return @fields;
800 }
801
802 =head GetBarcodeData
803
804 =over 4
805 Parse labels_conf.formatstring value
806 (one value of the csv, which has already been split)
807 and return string from koha tables or MARC record.
808 =back
809 =cut
810 #'
811 sub GetBarcodeData {
812     my ( $f, $item, $record ) = @_;
813     my $kohatables = &_descKohaTables();
814     my $datastring = '';
815     my $match_kohatable = join(
816         '|',
817         (
818             @{ $kohatables->{biblio} },
819             @{ $kohatables->{biblioitems} },
820             @{ $kohatables->{items} }
821         )
822     );
823     while ($f) {
824         $f =~ s/^\s?//;
825         if ( $f =~ /^'(.*)'.*/ ) {
826             # single quotes indicate a static text string.
827             $datastring .= $1;
828             $f = $';
829         }
830         elsif ( $f =~ /^($match_kohatable).*/ ) {
831             $datastring .= $item->{$f};
832             $f = $';
833         }
834         elsif ( $f =~ /^([0-9a-z]{3})(\w)(\W?).*?/ ) {
835             my $marc_field = $1;
836             foreach my $subfield ($record->field($marc_field)) {
837                 if ( $subfield->subfield('9') eq $item->{'itemnumber'} ) {
838                     $datastring .= $subfield->subfield($2 ) . $3;
839                     last;
840                 }
841             }
842             $f = $';
843         }
844         else {
845             last;    # Failed to match
846         }
847     }
848     return $datastring;
849 }
850
851 =head descKohaTables
852 Return a hashref of an array of hashes,
853 with name,type keys.
854 =cut
855
856 sub _descKohaTables {
857         my $dbh = C4::Context->dbh();
858         my $kohatables;
859         for my $table ( 'biblio','biblioitems','items' ) {
860                 my $sth = $dbh->column_info(undef,undef,$table,'%');
861                 while (my $info = $sth->fetchrow_hashref()){
862                         push @{$kohatables->{$table}} , $info->{'COLUMN_NAME'} ;
863                 }
864                 $sth->finish;
865         }
866         return $kohatables;
867 }
868
869 sub GetPatronCardItems {
870
871     my ( $batch_id ) = @_;
872     my @resultsloop;
873     
874     my $dbh = C4::Context->dbh;
875 #    my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY borrowernumber";
876     my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY cardid";
877     my $sth = $dbh->prepare($query);
878     $sth->execute($batch_id);
879     my $cardno = 1;
880     while ( my $data = $sth->fetchrow_hashref ) {
881         my $patron_data = GetMember( $data->{'borrowernumber'} );
882         $patron_data->{'branchname'} = GetBranchName( $patron_data->{'branchcode'} );
883         $patron_data->{'cardno'} = $cardno;
884         $patron_data->{'cardid'} = $data->{'cardid'};
885         $patron_data->{'batch_id'} = $batch_id;
886         push( @resultsloop, $patron_data );
887         $cardno++;
888     }
889     $sth->finish;
890     return @resultsloop;
891
892 }
893
894 sub deduplicate_batch {
895         my ( $batch_id, $batch_type ) = @_;
896         my $query = "
897         SELECT DISTINCT
898                         batch_id," . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . ",
899                         count(". (($batch_type eq 'labels') ? 'labelid' : 'cardid') . ") as count 
900         FROM $batch_type 
901         WHERE batch_id = ?
902         GROUP BY " . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . ",batch_id
903         HAVING count > 1
904         ORDER BY batch_id,
905         count DESC  ";
906         my $sth = C4::Context->dbh->prepare($query);
907         $sth->execute($batch_id);
908         warn $sth->errstr if $sth->errstr;
909         $sth->rows or return undef, $sth->errstr;
910
911         my $del_query = "
912         DELETE 
913         FROM     $batch_type
914         WHERE    batch_id = ?
915         AND      " . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . " = ?
916         ORDER BY timestamp ASC
917         ";
918         my $killed = 0;
919         while (my $data = $sth->fetchrow_hashref()) {
920                 my $itemnumber = $data->{(($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber')} or next;
921                 my $limit      = $data->{count} - 1  or next;
922                 my $sth2 = C4::Context->dbh->prepare("$del_query  LIMIT $limit");
923                 # die sprintf "$del_query LIMIT %s\n (%s, %s)", $limit, $batch_id, $itemnumber;
924                 # $sth2->execute($batch_id, C4::Context->dbh->quote($data->{itemnumber}), $data->{count} - 1)
925                 $sth2->execute($batch_id, $itemnumber) and
926                         $killed += ($data->{count} - 1);
927                 warn $sth2->errstr if $sth2->errstr;
928         }
929         return $killed, undef;
930 }
931
932 sub split_lccn {
933     my ($lccn) = @_;    
934     my ($ll, $wnl, $dec, $cutter, $pubdate) = (0, 0, 0, 0, 0);
935     $_ = $lccn;
936     # lccn example 'HE8700.7 .P6T44 1983';
937     my    @splits   = m/
938         (^[a-zA-Z]+)            # HE
939         ([0-9]+\.*[0-9]*)             # 8700.7
940         \s*
941         (\.*[a-zA-Z0-9]*)       # P6T44
942         \s*
943         ([0-9]*)                # 1983
944         /x;  
945
946     # strip something occuring spaces too
947     $splits[0] =~ s/\s+$//;
948     $splits[1] =~ s/\s+$//;
949     $splits[2] =~ s/\s+$//;
950
951     # if the regex fails, then just return the whole string, 
952     # better than nothing
953     # FIXME It seems we should handle all cases, have some graceful error handling, or at least inform the caller of the failure to split
954     $splits[0] = $lccn if  $splits[0]  eq '' ;
955     return @splits;
956 }
957
958 sub split_ddcn {
959     my ($ddcn) = @_;
960     $ddcn =~ s/\///g;   # in theory we should be able to simply remove all segmentation markers and arrive at the correct call number...
961     $_ = $ddcn;
962     # ddcn example R220.3 H2793Z H32 c.2
963     my @splits = m/^([A-Z]{0,3})                # R (OS, REF, etc. up do three letters)
964                     ([0-9]+\.[0-9]*)            # 220.3
965                     \s?                         # space (not requiring anything beyond the call number)
966                     ([a-zA-Z0-9]*\.?[a-zA-Z0-9])# cutter number... maybe, but if so it is in this position (Z indicates literary criticism)
967                     \s?                         # space if it exists
968                     ([a-zA-Z]*\.?[0-9]*)        # other indicators such as cutter for author of literary criticism in this example if it exists
969                     \s?                         # space if ie exists
970                     ([a-zA-Z]*\.?[0-9]*)        # other indicators such as volume number, copy number, edition date, etc. if it exists
971                     /x;
972     return @splits;
973 }
974
975 sub DrawSpineText {
976
977     my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
978         $text_wrap_cols, $item, $conf_data, $printingtype ) = @_;
979     
980     # Replaced item's itemtype with the more user-friendly description...
981     my $dbh = C4::Context->dbh;
982     my $sth = $dbh->prepare("SELECT itemtype,description FROM itemtypes");
983     $sth->execute();
984     while ( my $data = $sth->fetchrow_hashref ) {
985         $$item->{'itemtype'} = $data->{'description'} if ($$item->{'itemtype'} eq $data->{'itemtype'});
986         $$item->{'itype'} = $data->{'description'} if ($$item->{'itype'} eq $data->{'itemtype'});
987     }
988
989     my $str = '';
990
991     my $top_text_margin = ( $fontsize + 3 );    #FIXME: This should be a template parameter and passed in...
992     my $line_spacer = ( $fontsize * 1 );    # number of pixels between text rows (This is actually leading: baseline to baseline minus font size. Recommended starting point is 20% of font size.).
993
994     my $layout_id = $$conf_data->{'id'};
995
996     my $vPos = ( $y_pos + ( $label_height - $top_text_margin ) );
997
998     my @str_fields = get_text_fields($layout_id, 'codes' );
999     my $record = GetMarcBiblio($$item->{biblionumber});
1000     # FIXME - returns all items, so you can't get data from an embedded holdings field.
1001     # TODO - add a GetMarcBiblio1item(bibnum,itemnum) or a GetMarcItem(itemnum).
1002
1003     my $old_fontname = $fontname; # We need to keep track of the original font passed in...
1004     my $cn_source = $record->subfield('952','2');
1005     for my $field (@str_fields) {
1006         $field->{'code'} or warn "get_text_fields($layout_id, 'codes') element missing 'code' field";
1007         if ($$conf_data->{'formatstring'}) {
1008                 $field->{'data'} =  GetBarcodeData($field->{'code'},$$item,$record) ;
1009         }
1010         elsif ($field->{'code'} eq 'itemtype') {
1011             $field->{'data'} = C4::Context->preference('item-level_itypes') ? $$item->{'itype'} : $$item->{'itemtype'};
1012         }
1013         else {
1014                 $field->{data} =   $$item->{$field->{'code'}}  ;
1015         }
1016         # This allows us to print the title in italic (oblique) type... (Times Roman has a different nomenclature.)
1017         # It seems there should be a better way to handle fonts in the label/patron card tool altogether -fbcit
1018         ($field->{code} eq 'title') ? (($old_fontname =~ /T/) ? ($fontname = 'TI') : ($fontname = ($old_fontname . 'O'))) : ($fontname = $old_fontname);
1019         my $font = prFont($fontname);
1020         # if the display option for this field is selected in the DB,
1021         # and the item record has some values for this field, display it.
1022         # Or if there is a csv list of fields to display, display them.
1023         if ( ($$conf_data->{'formatstring'}) || ( $$conf_data->{$field->{code}} && $$item->{$field->{code}} ) ) {
1024             # get the string
1025             my $str = $field->{data} ;
1026             # strip out naughty existing nl/cr's
1027             $str =~ s/\n//g;
1028             $str =~ s/\r//g;
1029             my @strings;
1030             my @callnumber_list = ('itemcallnumber', '050a', '050b', '082a', '952o'); # Fields which hold call number data
1031             if ((grep {$field->{code} =~ m/$_/} @callnumber_list) and ($printingtype eq 'BIB')) { # If the field contains the call number, we do some sp
1032                 if ($cn_source eq 'lcc') {
1033                     @strings = split_lccn($str);
1034                 } elsif ($cn_source eq 'ddc') {
1035                     @strings = split_ddcn($str);
1036                 } else {
1037                     # FIXME Need error trapping here; something to be informative to the user perhaps -crn
1038                     push @strings, $str;
1039                 }
1040             } else {
1041                 $str =~ s/\/$//g;       # Here we will strip out all trailing '/' in fields other than the call number...
1042                 $str =~ s/\(/\\\(/g;    # Escape '(' and ')' for the postscript stream...
1043                 $str =~ s/\)/\\\)/g;
1044                 # Wrap text lines exceeding $text_wrap_cols length...
1045                 $Text::Wrap::columns = $text_wrap_cols;
1046                 my @line = split(/\n/ ,wrap('', '', $str));
1047                 # If this is a title field, limit to two lines; all others limit to one...
1048                 if ($field->{code} eq 'title' && scalar(@line) >= 2) {
1049                     while (scalar(@line) > 2) {
1050                         pop @line;
1051                     }
1052                 } else {
1053                     while (scalar(@line) > 1) {
1054                         pop @line;
1055                     }
1056                 }
1057                 push(@strings, @line);
1058             }
1059             # loop for each string line
1060             foreach my $str (@strings) {
1061                 my $hPos = 0;
1062                 if ( $printingtype eq 'BIB' ) { #FIXME: This is a hack and needs to be implimented as a text justification option in the template...
1063                     # some code to try and center each line on the label based on font size and string point width...
1064                     my $stringwidth = prStrWidth($str, $fontname, $fontsize);
1065                     my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
1066                     $hPos = ( ( $whitespace  / 2 ) + $x_pos + $left_text_margin );
1067                     #warn "\$label_width=$label_width \$stringwidth=$stringwidth \$whitespace=$whitespace \$left_text_margin=$left_text_margin for $str\n";
1068                 } else {
1069                     $hPos = ( $x_pos + $left_text_margin );
1070                 }
1071                 PrintText( $hPos, $vPos, $font, $fontsize, $str );
1072                 $vPos = $vPos - $line_spacer;
1073             }
1074         }
1075     }   #foreach field
1076 }
1077
1078 sub PrintText {
1079     my ( $hPos, $vPos, $font, $fontsize, $text ) = @_;
1080     my $str = "BT /$font $fontsize Tf $hPos $vPos Td ($text) Tj ET";
1081     prAdd($str);
1082 }
1083
1084 sub DrawPatronCardText {
1085
1086     my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
1087         $text_wrap_cols, $text, $printingtype )
1088       = @_;
1089
1090     my $top_text_margin = 25;    #FIXME: This should be a template parameter and passed in...
1091
1092     my $vPos   = ( $y_pos + ( $label_height - $top_text_margin ) );
1093     my $font = prFont($fontname);
1094
1095     my $hPos = 0;
1096
1097     foreach my $line (keys %$text) {
1098         $debug and warn "Current text is \"$line\" and font size for \"$line\" is $text->{$line} points";
1099         # some code to try and center each line on the label based on font size and string point width...
1100         my $stringwidth = prStrWidth($line, $fontname, $text->{$line});
1101         my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
1102         $hPos = ( ( $whitespace  / 2 ) + $x_pos + $left_text_margin );
1103
1104         PrintText( $hPos, $vPos, $font, $text->{$line}, $line );
1105         my $line_spacer = ( $text->{$line} * 1 );    # number of pixels between text rows (This is actually leading: baseline to baseline minus font size. Recommended starting point is 20% (0.20) of font size.).
1106         $vPos = $vPos - ($line_spacer + $text->{$line});   # Linefeed equiv: leading + font size
1107     }
1108 }
1109
1110 # Not used anywhere.
1111
1112 #sub SetFontSize {
1113 #
1114 #    my ($fontsize) = @_;
1115 #### fontsize
1116 #    my $str = "BT/F13 30 Tf288 720 Td( AAAAAAAAAA ) TjET";
1117 #    prAdd($str);
1118 #}
1119
1120 sub DrawBarcode {
1121
1122     # x and y are from the top-left :)
1123     my ( $x_pos, $y_pos, $height, $width, $barcode, $barcodetype ) = @_;
1124     my $num_of_bars = length($barcode);
1125     my $bar_width   = $width * .8;        # %80 of length of label width
1126     my $tot_bar_length = 0;
1127     my $bar_length = 0;
1128     my $guard_length = 10;
1129     my $xsize_ratio = 0;
1130
1131     if ( $barcodetype eq 'CODE39' ) {
1132         $bar_length = '17.5';
1133         $tot_bar_length =
1134           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1135         $xsize_ratio = ( $bar_width / $tot_bar_length );
1136         eval {
1137             PDF::Reuse::Barcode::Code39(
1138                 x => ( $x_pos + ( $width / 10 ) ),
1139                 y => ( $y_pos + ( $height / 10 ) ),
1140                 value         => "*$barcode*",
1141                 ySize         => ( .02 * $height ),
1142                 xSize         => $xsize_ratio,
1143                 hide_asterisk => 1,
1144             );
1145         };
1146         if ($@) {
1147             warn "$barcodetype, $barcode FAILED:$@";
1148         }
1149     }
1150
1151     elsif ( $barcodetype eq 'CODE39MOD' ) {
1152
1153         # get modulo43 checksum
1154         my $c39 = CheckDigits('code_39');
1155         $barcode = $c39->complete($barcode);
1156
1157         $bar_length = '19';
1158         $tot_bar_length =
1159           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1160         $xsize_ratio = ( $bar_width / $tot_bar_length );
1161         eval {
1162             PDF::Reuse::Barcode::Code39(
1163                 x => ( $x_pos + ( $width / 10 ) ),
1164                 y => ( $y_pos + ( $height / 10 ) ),
1165                 value         => "*$barcode*",
1166                 ySize         => ( .02 * $height ),
1167                 xSize         => $xsize_ratio,
1168                 hide_asterisk => 1,
1169             );
1170         };
1171
1172         if ($@) {
1173             warn "$barcodetype, $barcode FAILED:$@";
1174         }
1175     }
1176     elsif ( $barcodetype eq 'CODE39MOD10' ) {
1177  
1178         # get modulo43 checksum
1179         my $c39_10 = CheckDigits('visa');
1180         $barcode = $c39_10->complete($barcode);
1181
1182         $bar_length = '19';
1183         $tot_bar_length =
1184           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1185         $xsize_ratio = ( $bar_width / $tot_bar_length );
1186         eval {
1187             PDF::Reuse::Barcode::Code39(
1188                 x => ( $x_pos + ( $width / 10 ) ),
1189                 y => ( $y_pos + ( $height / 10 ) ),
1190                 value         => "*$barcode*",
1191                 ySize         => ( .02 * $height ),
1192                 xSize         => $xsize_ratio,
1193                 hide_asterisk => 1,
1194                                 text         => 0, 
1195             );
1196         };
1197
1198         if ($@) {
1199             warn "$barcodetype, $barcode FAILED:$@";
1200         }
1201     }
1202
1203  
1204     elsif ( $barcodetype eq 'COOP2OF5' ) {
1205         $bar_length = '9.43333333333333';
1206         $tot_bar_length =
1207           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1208         $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1209         eval {
1210             PDF::Reuse::Barcode::COOP2of5(
1211                 x => ( $x_pos + ( $width / 10 ) ),
1212                 y => ( $y_pos + ( $height / 10 ) ),
1213                 value => $barcode,
1214                 ySize => ( .02 * $height ),
1215                 xSize => $xsize_ratio,
1216             );
1217         };
1218         if ($@) {
1219             warn "$barcodetype, $barcode FAILED:$@";
1220         }
1221     }
1222
1223     elsif ( $barcodetype eq 'INDUSTRIAL2OF5' ) {
1224         $bar_length = '13.1333333333333';
1225         $tot_bar_length =
1226           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1227         $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1228         eval {
1229             PDF::Reuse::Barcode::Industrial2of5(
1230                 x => ( $x_pos + ( $width / 10 ) ),
1231                 y => ( $y_pos + ( $height / 10 ) ),
1232                 value => $barcode,
1233                 ySize => ( .02 * $height ),
1234                 xSize => $xsize_ratio,
1235             );
1236         };
1237         if ($@) {
1238             warn "$barcodetype, $barcode FAILED:$@";
1239         }
1240     }
1241
1242     my $moo2 = $tot_bar_length * $xsize_ratio;
1243
1244     warn "$x_pos, $y_pos, $barcode, $barcodetype" if $debug;
1245     warn "BAR_WDTH = $bar_width, TOT.BAR.LGHT=$tot_bar_length  R*TOT.BAR =$moo2" if $debug;
1246 }
1247
1248 =item build_circ_barcode;
1249
1250   build_circ_barcode( $x_pos, $y_pos, $barcode,
1251                 $barcodetype, \$item);
1252
1253 $item is the result of a previous call to GetLabelItems();
1254
1255 =cut
1256
1257 #'
1258 sub build_circ_barcode {
1259     my ( $x_pos_circ, $y_pos, $value, $barcodetype, $item ) = @_;
1260
1261     #warn Dumper \$item;
1262
1263     #warn "value = $value\n";
1264
1265     #$DB::single = 1;
1266
1267     if ( $barcodetype eq 'EAN13' ) {
1268
1269         #testing EAN13 barcodes hack
1270         $value = $value . '000000000';
1271         $value =~ s/-//;
1272         $value = substr( $value, 0, 12 );
1273
1274         #warn $value;
1275         eval {
1276             PDF::Reuse::Barcode::EAN13(
1277                 x     => ( $x_pos_circ + 27 ),
1278                 y     => ( $y_pos + 15 ),
1279                 value => $value,
1280
1281                 #            prolong => 2.96,
1282                 #            xSize   => 1.5,
1283
1284                 # ySize   => 1.2,
1285
1286 # added for xpdf compat. doesnt use type3 fonts., but increases filesize from 20k to 200k
1287 # i think its embedding extra fonts in the pdf file.
1288 #  mode => 'graphic',
1289             );
1290         };
1291         if ($@) {
1292             $item->{'barcodeerror'} = 1;
1293
1294             #warn "EAN13BARCODE FAILED:$@";
1295         }
1296
1297         #warn $barcodetype;
1298
1299     }
1300     elsif ( $barcodetype eq 'Code39' ) {
1301
1302         eval {
1303             PDF::Reuse::Barcode::Code39(
1304                 x     => ( $x_pos_circ + 9 ),
1305                 y     => ( $y_pos + 15 ),
1306                 value => $value,
1307
1308                 #           prolong => 2.96,
1309                 xSize => .85,
1310
1311                 ySize => 1.3,
1312             );
1313         };
1314         if ($@) {
1315             $item->{'barcodeerror'} = 1;
1316
1317             #warn "CODE39BARCODE $value FAILED:$@";
1318         }
1319
1320         #warn $barcodetype;
1321
1322     }
1323
1324     elsif ( $barcodetype eq 'Matrix2of5' ) {
1325
1326         #warn "MATRIX ELSE:";
1327
1328         #testing MATRIX25  barcodes hack
1329         #    $value = $value.'000000000';
1330         $value =~ s/-//;
1331
1332         #    $value = substr( $value, 0, 12 );
1333         #warn $value;
1334
1335         eval {
1336             PDF::Reuse::Barcode::Matrix2of5(
1337                 x     => ( $x_pos_circ + 27 ),
1338                 y     => ( $y_pos + 15 ),
1339                 value => $value,
1340
1341                 #        prolong => 2.96,
1342                 #       xSize   => 1.5,
1343
1344                 # ySize   => 1.2,
1345             );
1346         };
1347         if ($@) {
1348             $item->{'barcodeerror'} = 1;
1349
1350             #warn "BARCODE FAILED:$@";
1351         }
1352
1353         #warn $barcodetype;
1354
1355     }
1356
1357     elsif ( $barcodetype eq 'EAN8' ) {
1358
1359         #testing ean8 barcodes hack
1360         $value = $value . '000000000';
1361         $value =~ s/-//;
1362         $value = substr( $value, 0, 8 );
1363
1364         #warn $value;
1365
1366         #warn "EAN8 ELSEIF";
1367         eval {
1368             PDF::Reuse::Barcode::EAN8(
1369                 x       => ( $x_pos_circ + 42 ),
1370                 y       => ( $y_pos + 15 ),
1371                 value   => $value,
1372                 prolong => 2.96,
1373                 xSize   => 1.5,
1374
1375                 # ySize   => 1.2,
1376             );
1377         };
1378
1379         if ($@) {
1380             $item->{'barcodeerror'} = 1;
1381
1382             #warn "BARCODE FAILED:$@";
1383         }
1384
1385         #warn $barcodetype;
1386
1387     }
1388
1389     elsif ( $barcodetype eq 'UPC-E' ) {
1390         eval {
1391             PDF::Reuse::Barcode::UPCE(
1392                 x       => ( $x_pos_circ + 27 ),
1393                 y       => ( $y_pos + 15 ),
1394                 value   => $value,
1395                 prolong => 2.96,
1396                 xSize   => 1.5,
1397
1398                 # ySize   => 1.2,
1399             );
1400         };
1401
1402         if ($@) {
1403             $item->{'barcodeerror'} = 1;
1404
1405             #warn "BARCODE FAILED:$@";
1406         }
1407
1408         #warn $barcodetype;
1409
1410     }
1411     elsif ( $barcodetype eq 'NW7' ) {
1412         eval {
1413             PDF::Reuse::Barcode::NW7(
1414                 x       => ( $x_pos_circ + 27 ),
1415                 y       => ( $y_pos + 15 ),
1416                 value   => $value,
1417                 prolong => 2.96,
1418                 xSize   => 1.5,
1419
1420                 # ySize   => 1.2,
1421             );
1422         };
1423
1424         if ($@) {
1425             $item->{'barcodeerror'} = 1;
1426
1427             #warn "BARCODE FAILED:$@";
1428         }
1429
1430         #warn $barcodetype;
1431
1432     }
1433     elsif ( $barcodetype eq 'ITF' ) {
1434         eval {
1435             PDF::Reuse::Barcode::ITF(
1436                 x       => ( $x_pos_circ + 27 ),
1437                 y       => ( $y_pos + 15 ),
1438                 value   => $value,
1439                 prolong => 2.96,
1440                 xSize   => 1.5,
1441
1442                 # ySize   => 1.2,
1443             );
1444         };
1445
1446         if ($@) {
1447             $item->{'barcodeerror'} = 1;
1448
1449             #warn "BARCODE FAILED:$@";
1450         }
1451
1452         #warn $barcodetype;
1453
1454     }
1455     elsif ( $barcodetype eq 'Industrial2of5' ) {
1456         eval {
1457             PDF::Reuse::Barcode::Industrial2of5(
1458                 x       => ( $x_pos_circ + 27 ),
1459                 y       => ( $y_pos + 15 ),
1460                 value   => $value,
1461                 prolong => 2.96,
1462                 xSize   => 1.5,
1463
1464                 # ySize   => 1.2,
1465             );
1466         };
1467         if ($@) {
1468             $item->{'barcodeerror'} = 1;
1469
1470             #warn "BARCODE FAILED:$@";
1471         }
1472
1473         #warn $barcodetype;
1474
1475     }
1476     elsif ( $barcodetype eq 'IATA2of5' ) {
1477         eval {
1478             PDF::Reuse::Barcode::IATA2of5(
1479                 x       => ( $x_pos_circ + 27 ),
1480                 y       => ( $y_pos + 15 ),
1481                 value   => $value,
1482                 prolong => 2.96,
1483                 xSize   => 1.5,
1484
1485                 # ySize   => 1.2,
1486             );
1487         };
1488         if ($@) {
1489             $item->{'barcodeerror'} = 1;
1490
1491             #warn "BARCODE FAILED:$@";
1492         }
1493
1494         #warn $barcodetype;
1495
1496     }
1497
1498     elsif ( $barcodetype eq 'COOP2of5' ) {
1499         eval {
1500             PDF::Reuse::Barcode::COOP2of5(
1501                 x       => ( $x_pos_circ + 27 ),
1502                 y       => ( $y_pos + 15 ),
1503                 value   => $value,
1504                 prolong => 2.96,
1505                 xSize   => 1.5,
1506
1507                 # ySize   => 1.2,
1508             );
1509         };
1510         if ($@) {
1511             $item->{'barcodeerror'} = 1;
1512
1513             #warn "BARCODE FAILED:$@";
1514         }
1515
1516         #warn $barcodetype;
1517
1518     }
1519     elsif ( $barcodetype eq 'UPC-A' ) {
1520
1521         eval {
1522             PDF::Reuse::Barcode::UPCA(
1523                 x       => ( $x_pos_circ + 27 ),
1524                 y       => ( $y_pos + 15 ),
1525                 value   => $value,
1526                 prolong => 2.96,
1527                 xSize   => 1.5,
1528
1529                 # ySize   => 1.2,
1530             );
1531         };
1532         if ($@) {
1533             $item->{'barcodeerror'} = 1;
1534
1535             #warn "BARCODE FAILED:$@";
1536         }
1537
1538         #warn $barcodetype;
1539
1540     }
1541
1542 }
1543
1544 =item draw_boundaries
1545
1546  sub draw_boundaries ($x_pos_spine, $x_pos_circ1, $x_pos_circ2,
1547                 $y_pos, $spine_width, $label_height, $circ_width)  
1548
1549 This sub draws boundary lines where the label outlines are, to aid in printer testing, and debugging.
1550
1551 =cut
1552
1553 #'
1554 sub draw_boundaries {
1555
1556     my (
1557         $x_pos_spine, $x_pos_circ1,  $x_pos_circ2, $y_pos,
1558         $spine_width, $label_height, $circ_width
1559     ) = @_;
1560
1561     my $y_pos_initial = ( ( 792 - 36 ) - 90 );
1562     $y_pos            = $y_pos_initial; # FIXME - why are we ignoring the y_pos parameter by redefining it?
1563     my $i             = 1;
1564
1565     for ( $i = 1 ; $i <= 8 ; $i++ ) {
1566
1567         &drawbox( $x_pos_spine, $y_pos, ($spine_width), ($label_height) );
1568
1569    #warn "OLD BOXES  x=$x_pos_spine, y=$y_pos, w=$spine_width, h=$label_height";
1570         &drawbox( $x_pos_circ1, $y_pos, ($circ_width), ($label_height) );
1571         &drawbox( $x_pos_circ2, $y_pos, ($circ_width), ($label_height) );
1572
1573         $y_pos = ( $y_pos - $label_height );
1574
1575     }
1576 }
1577
1578 =item drawbox
1579
1580         sub drawbox {   $lower_left_x, $lower_left_y, 
1581                         $upper_right_x, $upper_right_y )
1582
1583 this is a low level sub, that draws a pdf box, it is called by draw_boxes
1584
1585 FYI: the  $upper_right_x and $upper_right_y values are RELATIVE to  $lower_left_x and $lower_left_y
1586
1587 and $lower_left_x, $lower_left_y are ABSOLUTE, this caught me out!
1588
1589 =cut
1590
1591 #'
1592 sub drawbox {
1593     my ( $llx, $lly, $urx, $ury ) = @_;
1594
1595     #    warn "llx,y= $llx,$lly  ,   urx,y=$urx,$ury \n";
1596
1597     my $str = "q\n";    # save the graphic state
1598     $str .= "0.5 w\n";              # border color red
1599     $str .= "1.0 0.0 0.0  RG\n";    # border color red
1600          #   $str .= "0.5 0.75 1.0 rg\n";           # fill color blue
1601     $str .= "1.0 1.0 1.0  rg\n";    # fill color white
1602
1603     $str .= "$llx $lly $urx $ury re\n";    # a rectangle
1604     $str .= "B\n";                         # fill (and a little more)
1605     $str .= "Q\n";                         # save the graphic state
1606
1607     prAdd($str);
1608
1609 }
1610
1611 END { }    # module clean-up code here (global destructor)
1612
1613 1;
1614 __END__
1615
1616 =back
1617
1618 =head1 AUTHOR
1619
1620 Mason James <mason@katipo.co.nz>
1621
1622 =cut
1623