kohabug 2475 [3/?] Adding splitting algorithm for fiction call numbers
[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 #            Something like this, perhaps, but this also causes problems because we need more fields sometimes.
770 #            SELECT i.barcode, i.itemcallnumber, i.itype, bi.isbn, bi.issn, b.title, b.author
771            "SELECT bi.*, i.*, b.*
772             FROM items AS i, biblioitems AS bi ,biblio AS b
773             WHERE itemnumber=? AND i.biblioitemnumber=bi.biblioitemnumber AND bi.biblionumber=b.biblionumber";
774         my $sth1 = $dbh->prepare($query1);
775         $sth1->execute( $data->{'itemnumber'} );
776
777         my $data1 = $sth1->fetchrow_hashref();
778         $data1->{'labelno'}  = $i1;
779         $data1->{'labelid'}  = $data->{'labelid'};
780         $data1->{'batch_id'} = $batch_id;
781         $data1->{'summary'} = "$data1->{'barcode'}, $data1->{'title'}, $data1->{'isbn'}";
782
783         push( @resultsloop, $data1 );
784         $sth1->finish;
785
786         $i1++;
787     }
788     $sth->finish;
789     return @resultsloop;
790
791 }
792
793 sub GetItemFields {
794     my @fields = qw (
795       barcode           title
796       isbn              issn
797       author            itemtype
798       itemcallnumber
799     );
800     return @fields;
801 }
802
803 =head GetBarcodeData
804
805 =over 4
806 Parse labels_conf.formatstring value
807 (one value of the csv, which has already been split)
808 and return string from koha tables or MARC record.
809 =back
810 =cut
811 #'
812 sub GetBarcodeData {
813     my ( $f, $item, $record ) = @_;
814     my $kohatables = &_descKohaTables();
815     my $datastring = '';
816     my $match_kohatable = join(
817         '|',
818         (
819             @{ $kohatables->{biblio} },
820             @{ $kohatables->{biblioitems} },
821             @{ $kohatables->{items} }
822         )
823     );
824     while ($f) {
825         $f =~ s/^\s?//;
826         if ( $f =~ /^'(.*)'.*/ ) {
827             # single quotes indicate a static text string.
828             $datastring .= $1;
829             $f = $';
830         }
831         elsif ( $f =~ /^($match_kohatable).*/ ) {
832             $datastring .= $item->{$f};
833             $f = $';
834         }
835         elsif ( $f =~ /^([0-9a-z]{3})(\w)(\W?).*?/ ) {
836             my $marc_field = $1;
837             foreach my $subfield ($record->field($marc_field)) {
838                 if ( $subfield->subfield('9') eq $item->{'itemnumber'} ) {
839                     $datastring .= $subfield->subfield($2 ) . $3;
840                     last;
841                 }
842             }
843             $f = $';
844         }
845         else {
846             last;    # Failed to match
847         }
848     }
849     return $datastring;
850 }
851
852 =head descKohaTables
853 Return a hashref of an array of hashes,
854 with name,type keys.
855 =cut
856
857 sub _descKohaTables {
858         my $dbh = C4::Context->dbh();
859         my $kohatables;
860         for my $table ( 'biblio','biblioitems','items' ) {
861                 my $sth = $dbh->column_info(undef,undef,$table,'%');
862                 while (my $info = $sth->fetchrow_hashref()){
863                         push @{$kohatables->{$table}} , $info->{'COLUMN_NAME'} ;
864                 }
865                 $sth->finish;
866         }
867         return $kohatables;
868 }
869
870 sub GetPatronCardItems {
871
872     my ( $batch_id ) = @_;
873     my @resultsloop;
874     
875     my $dbh = C4::Context->dbh;
876 #    my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY borrowernumber";
877     my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY cardid";
878     my $sth = $dbh->prepare($query);
879     $sth->execute($batch_id);
880     my $cardno = 1;
881     while ( my $data = $sth->fetchrow_hashref ) {
882         my $patron_data = GetMember( $data->{'borrowernumber'} );
883         $patron_data->{'branchname'} = GetBranchName( $patron_data->{'branchcode'} );
884         $patron_data->{'cardno'} = $cardno;
885         $patron_data->{'cardid'} = $data->{'cardid'};
886         $patron_data->{'batch_id'} = $batch_id;
887         push( @resultsloop, $patron_data );
888         $cardno++;
889     }
890     $sth->finish;
891     return @resultsloop;
892
893 }
894
895 sub deduplicate_batch {
896         my ( $batch_id, $batch_type ) = @_;
897         my $query = "
898         SELECT DISTINCT
899                         batch_id," . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . ",
900                         count(". (($batch_type eq 'labels') ? 'labelid' : 'cardid') . ") as count 
901         FROM $batch_type 
902         WHERE batch_id = ?
903         GROUP BY " . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . ",batch_id
904         HAVING count > 1
905         ORDER BY batch_id,
906         count DESC  ";
907         my $sth = C4::Context->dbh->prepare($query);
908         $sth->execute($batch_id);
909         warn $sth->errstr if $sth->errstr;
910         $sth->rows or return undef, $sth->errstr;
911
912         my $del_query = "
913         DELETE 
914         FROM     $batch_type
915         WHERE    batch_id = ?
916         AND      " . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . " = ?
917         ORDER BY timestamp ASC
918         ";
919         my $killed = 0;
920         while (my $data = $sth->fetchrow_hashref()) {
921                 my $itemnumber = $data->{(($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber')} or next;
922                 my $limit      = $data->{count} - 1  or next;
923                 my $sth2 = C4::Context->dbh->prepare("$del_query  LIMIT $limit");
924                 # die sprintf "$del_query LIMIT %s\n (%s, %s)", $limit, $batch_id, $itemnumber;
925                 # $sth2->execute($batch_id, C4::Context->dbh->quote($data->{itemnumber}), $data->{count} - 1)
926                 $sth2->execute($batch_id, $itemnumber) and
927                         $killed += ($data->{count} - 1);
928                 warn $sth2->errstr if $sth2->errstr;
929         }
930         return $killed, undef;
931 }
932
933 sub split_lccn {
934     my ($lccn) = @_;    
935     my ($ll, $wnl, $dec, $cutter, $pubdate) = (0, 0, 0, 0, 0);
936     $_ = $lccn;
937     # lccn example 'HE8700.7 .P6T44 1983';
938     my    @splits   = m/
939         (^[a-zA-Z]+)            # HE
940         ([0-9]+\.*[0-9]*)             # 8700.7
941         \s*
942         (\.*[a-zA-Z0-9]*)       # P6T44
943         \s*
944         ([0-9]*)                # 1983
945         /x;  
946
947     # strip something occuring spaces too
948     $splits[0] =~ s/\s+$//;
949     $splits[1] =~ s/\s+$//;
950     $splits[2] =~ s/\s+$//;
951
952     return @splits;
953 }
954
955 sub split_ddcn {
956     my ($ddcn) = @_;
957     $ddcn =~ s/\///g;   # in theory we should be able to simply remove all segmentation markers and arrive at the correct call number...
958     $_ = $ddcn;
959     # ddcn example R220.3 H2793Z H32 c.2
960     my @splits = m/^([A-Z]{0,3})                # R (OS, REF, etc. up do three letters)
961                     ([0-9]+\.[0-9]*)            # 220.3
962                     \s?                         # space (not requiring anything beyond the call number)
963                     ([a-zA-Z0-9]*\.?[a-zA-Z0-9])# cutter number... maybe, but if so it is in this position (Z indicates literary criticism)
964                     \s?                         # space if it exists
965                     ([a-zA-Z]*\.?[0-9]*)        # other indicators such as cutter for author of literary criticism in this example if it exists
966                     \s?                         # space if ie exists
967                     ([a-zA-Z]*\.?[0-9]*)        # other indicators such as volume number, copy number, edition date, etc. if it exists
968                     /x;
969     return @splits;
970 }
971
972 sub split_fcn {
973     my ($fcn) = @_;
974     my @fcn_split = ();
975     # Split fiction call numbers based on spaces
976     SPLIT_FCN:
977     while ($fcn) {
978         if ($fcn =~ m/([A-Za-z0-9]+)(\W?).*?/x) {
979             push (@fcn_split, $1);
980             $fcn = $';
981         }
982         else {
983             last SPLIT_FCN;     # No match, break out of the loop
984         }
985     }
986     return @fcn_split;
987 }
988
989 sub DrawSpineText {
990
991     my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
992         $text_wrap_cols, $item, $conf_data, $printingtype ) = @_;
993     
994     # Replaced item's itemtype with the more user-friendly description...
995     my $dbh = C4::Context->dbh;
996     my $sth = $dbh->prepare("SELECT itemtype,description FROM itemtypes");
997     $sth->execute();
998     while ( my $data = $sth->fetchrow_hashref ) {
999         $$item->{'itemtype'} = $data->{'description'} if ($$item->{'itemtype'} eq $data->{'itemtype'});
1000         $$item->{'itype'} = $data->{'description'} if ($$item->{'itype'} eq $data->{'itemtype'});
1001     }
1002
1003     my $str = '';
1004
1005     my $top_text_margin = ( $fontsize + 3 );    #FIXME: This should be a template parameter and passed in...
1006     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.).
1007
1008     my $layout_id = $$conf_data->{'id'};
1009
1010     my $vPos = ( $y_pos + ( $label_height - $top_text_margin ) );
1011
1012     my @str_fields = get_text_fields($layout_id, 'codes' );
1013     my $record = GetMarcBiblio($$item->{biblionumber});
1014     # FIXME - returns all items, so you can't get data from an embedded holdings field.
1015     # TODO - add a GetMarcBiblio1item(bibnum,itemnum) or a GetMarcItem(itemnum).
1016
1017     my $old_fontname = $fontname; # We need to keep track of the original font passed in...
1018
1019     # Grab the cn_source and if that is NULL, the DefaultClassificationSource syspref
1020     my $cn_source = ($$item->{'cn_source'} ? $$item->{'cn_source'} : C4::Context->preference('DefaultClassificationSource'));
1021
1022     for my $field (@str_fields) {
1023         $field->{'code'} or warn "get_text_fields($layout_id, 'codes') element missing 'code' field";
1024         if ($$conf_data->{'formatstring'}) {
1025             $field->{'data'} =  GetBarcodeData($field->{'code'},$$item,$record) ;
1026         }
1027         elsif ($field->{'code'} eq 'itemtype') {
1028             $field->{'data'} = C4::Context->preference('item-level_itypes') ? $$item->{'itype'} : $$item->{'itemtype'};
1029         }
1030         else {
1031             $field->{data} =   $$item->{$field->{'code'}}  ;
1032         }
1033         # This allows us to print the title in italic (oblique) type... (Times Roman has a different nomenclature.)
1034         # It seems there should be a better way to handle fonts in the label/patron card tool altogether -fbcit
1035         ($field->{code} eq 'title') ? (($old_fontname =~ /T/) ? ($fontname = 'TI') : ($fontname = ($old_fontname . 'O'))) : ($fontname = $old_fontname);
1036         my $font = prFont($fontname);
1037         # if the display option for this field is selected in the DB,
1038         # and the item record has some values for this field, display it.
1039         # Or if there is a csv list of fields to display, display them.
1040         if ( ($$conf_data->{'formatstring'}) || ( $$conf_data->{$field->{code}} && $$item->{$field->{code}} ) ) {
1041             # get the string
1042             my $str = $field->{data} ;
1043             # strip out naughty existing nl/cr's
1044             $str =~ s/\n//g;
1045             $str =~ s/\r//g;
1046             my @strings;
1047             my @callnumber_list = ('itemcallnumber', '050a', '050b', '082a', '952o'); # Fields which hold call number data
1048             if ((grep {$field->{code} =~ m/$_/} @callnumber_list) and ($printingtype eq 'BIB')) { # If the field contains the call number, we do some sp
1049                 if ($cn_source eq 'lcc') {
1050                     @strings = split_lccn($str);
1051                     @strings = split_fcn($str) if !@strings;    # If it was not a true lccn, try it as a fiction call number
1052                     push (@strings, $str) if !@strings;         # If it was not that, send it on unsplit
1053                 } elsif ($cn_source eq 'ddc') {
1054                     @strings = split_ddcn($str);
1055                     @strings = split_fcn($str) if !@strings;
1056                     push (@strings, $str) if !@strings;
1057                 } else {
1058                     # FIXME Need error trapping here; something to be informative to the user perhaps -crn
1059                     push @strings, $str;
1060                 }
1061             } else {
1062                 $str =~ s/\/$//g;       # Here we will strip out all trailing '/' in fields other than the call number...
1063                 $str =~ s/\(/\\\(/g;    # Escape '(' and ')' for the postscript stream...
1064                 $str =~ s/\)/\\\)/g;
1065                 # Wrap text lines exceeding $text_wrap_cols length...
1066                 $Text::Wrap::columns = $text_wrap_cols;
1067                 my @line = split(/\n/ ,wrap('', '', $str));
1068                 # If this is a title field, limit to two lines; all others limit to one...
1069                 if ($field->{code} eq 'title' && scalar(@line) >= 2) {
1070                     while (scalar(@line) > 2) {
1071                         pop @line;
1072                     }
1073                 } else {
1074                     while (scalar(@line) > 1) {
1075                         pop @line;
1076                     }
1077                 }
1078                 push(@strings, @line);
1079             }
1080             # loop for each string line
1081             foreach my $str (@strings) {
1082                 my $hPos = 0;
1083                 if ( $printingtype eq 'BIB' ) { #FIXME: This is a hack and needs to be implimented as a text justification option in the template...
1084                     # some code to try and center each line on the label based on font size and string point width...
1085                     my $stringwidth = prStrWidth($str, $fontname, $fontsize);
1086                     my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
1087                     $hPos = ( ( $whitespace  / 2 ) + $x_pos + $left_text_margin );
1088                     #warn "\$label_width=$label_width \$stringwidth=$stringwidth \$whitespace=$whitespace \$left_text_margin=$left_text_margin for $str\n";
1089                 } else {
1090                     $hPos = ( $x_pos + $left_text_margin );
1091                 }
1092                 PrintText( $hPos, $vPos, $font, $fontsize, $str );
1093                 $vPos = $vPos - $line_spacer;
1094             }
1095         }
1096     }   #foreach field
1097 }
1098
1099 sub PrintText {
1100     my ( $hPos, $vPos, $font, $fontsize, $text ) = @_;
1101     my $str = "BT /$font $fontsize Tf $hPos $vPos Td ($text) Tj ET";
1102     prAdd($str);
1103 }
1104
1105 sub DrawPatronCardText {
1106
1107     my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
1108         $text_wrap_cols, $text, $printingtype )
1109       = @_;
1110
1111     my $top_text_margin = 25;    #FIXME: This should be a template parameter and passed in...
1112
1113     my $vPos   = ( $y_pos + ( $label_height - $top_text_margin ) );
1114     my $font = prFont($fontname);
1115
1116     my $hPos = 0;
1117
1118     foreach my $line (keys %$text) {
1119         $debug and warn "Current text is \"$line\" and font size for \"$line\" is $text->{$line} points";
1120         # some code to try and center each line on the label based on font size and string point width...
1121         my $stringwidth = prStrWidth($line, $fontname, $text->{$line});
1122         my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
1123         $hPos = ( ( $whitespace  / 2 ) + $x_pos + $left_text_margin );
1124
1125         PrintText( $hPos, $vPos, $font, $text->{$line}, $line );
1126         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.).
1127         $vPos = $vPos - ($line_spacer + $text->{$line});   # Linefeed equiv: leading + font size
1128     }
1129 }
1130
1131 # Not used anywhere.
1132
1133 #sub SetFontSize {
1134 #
1135 #    my ($fontsize) = @_;
1136 #### fontsize
1137 #    my $str = "BT/F13 30 Tf288 720 Td( AAAAAAAAAA ) TjET";
1138 #    prAdd($str);
1139 #}
1140
1141 sub DrawBarcode {
1142
1143     # x and y are from the top-left :)
1144     my ( $x_pos, $y_pos, $height, $width, $barcode, $barcodetype ) = @_;
1145     my $num_of_bars = length($barcode);
1146     my $bar_width   = $width * .8;        # %80 of length of label width
1147     my $tot_bar_length = 0;
1148     my $bar_length = 0;
1149     my $guard_length = 10;
1150     my $xsize_ratio = 0;
1151
1152     if ( $barcodetype eq 'CODE39' ) {
1153         $bar_length = '17.5';
1154         $tot_bar_length =
1155           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1156         $xsize_ratio = ( $bar_width / $tot_bar_length );
1157         eval {
1158             PDF::Reuse::Barcode::Code39(
1159                 x => ( $x_pos + ( $width / 10 ) ),
1160                 y => ( $y_pos + ( $height / 10 ) ),
1161                 value         => "*$barcode*",
1162                 ySize         => ( .02 * $height ),
1163                 xSize         => $xsize_ratio,
1164                 hide_asterisk => 1,
1165             );
1166         };
1167         if ($@) {
1168             warn "$barcodetype, $barcode FAILED:$@";
1169         }
1170     }
1171
1172     elsif ( $barcodetype eq 'CODE39MOD' ) {
1173
1174         # get modulo43 checksum
1175         my $c39 = CheckDigits('code_39');
1176         $barcode = $c39->complete($barcode);
1177
1178         $bar_length = '19';
1179         $tot_bar_length =
1180           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1181         $xsize_ratio = ( $bar_width / $tot_bar_length );
1182         eval {
1183             PDF::Reuse::Barcode::Code39(
1184                 x => ( $x_pos + ( $width / 10 ) ),
1185                 y => ( $y_pos + ( $height / 10 ) ),
1186                 value         => "*$barcode*",
1187                 ySize         => ( .02 * $height ),
1188                 xSize         => $xsize_ratio,
1189                 hide_asterisk => 1,
1190             );
1191         };
1192
1193         if ($@) {
1194             warn "$barcodetype, $barcode FAILED:$@";
1195         }
1196     }
1197     elsif ( $barcodetype eq 'CODE39MOD10' ) {
1198  
1199         # get modulo43 checksum
1200         my $c39_10 = CheckDigits('visa');
1201         $barcode = $c39_10->complete($barcode);
1202
1203         $bar_length = '19';
1204         $tot_bar_length =
1205           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1206         $xsize_ratio = ( $bar_width / $tot_bar_length );
1207         eval {
1208             PDF::Reuse::Barcode::Code39(
1209                 x => ( $x_pos + ( $width / 10 ) ),
1210                 y => ( $y_pos + ( $height / 10 ) ),
1211                 value         => "*$barcode*",
1212                 ySize         => ( .02 * $height ),
1213                 xSize         => $xsize_ratio,
1214                 hide_asterisk => 1,
1215                                 text         => 0, 
1216             );
1217         };
1218
1219         if ($@) {
1220             warn "$barcodetype, $barcode FAILED:$@";
1221         }
1222     }
1223
1224  
1225     elsif ( $barcodetype eq 'COOP2OF5' ) {
1226         $bar_length = '9.43333333333333';
1227         $tot_bar_length =
1228           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1229         $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1230         eval {
1231             PDF::Reuse::Barcode::COOP2of5(
1232                 x => ( $x_pos + ( $width / 10 ) ),
1233                 y => ( $y_pos + ( $height / 10 ) ),
1234                 value => $barcode,
1235                 ySize => ( .02 * $height ),
1236                 xSize => $xsize_ratio,
1237             );
1238         };
1239         if ($@) {
1240             warn "$barcodetype, $barcode FAILED:$@";
1241         }
1242     }
1243
1244     elsif ( $barcodetype eq 'INDUSTRIAL2OF5' ) {
1245         $bar_length = '13.1333333333333';
1246         $tot_bar_length =
1247           ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1248         $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1249         eval {
1250             PDF::Reuse::Barcode::Industrial2of5(
1251                 x => ( $x_pos + ( $width / 10 ) ),
1252                 y => ( $y_pos + ( $height / 10 ) ),
1253                 value => $barcode,
1254                 ySize => ( .02 * $height ),
1255                 xSize => $xsize_ratio,
1256             );
1257         };
1258         if ($@) {
1259             warn "$barcodetype, $barcode FAILED:$@";
1260         }
1261     }
1262
1263     my $moo2 = $tot_bar_length * $xsize_ratio;
1264
1265     warn "$x_pos, $y_pos, $barcode, $barcodetype" if $debug;
1266     warn "BAR_WDTH = $bar_width, TOT.BAR.LGHT=$tot_bar_length  R*TOT.BAR =$moo2" if $debug;
1267 }
1268
1269 =item build_circ_barcode;
1270
1271   build_circ_barcode( $x_pos, $y_pos, $barcode,
1272                 $barcodetype, \$item);
1273
1274 $item is the result of a previous call to GetLabelItems();
1275
1276 =cut
1277
1278 #'
1279 sub build_circ_barcode {
1280     my ( $x_pos_circ, $y_pos, $value, $barcodetype, $item ) = @_;
1281
1282     #warn Dumper \$item;
1283
1284     #warn "value = $value\n";
1285
1286     #$DB::single = 1;
1287
1288     if ( $barcodetype eq 'EAN13' ) {
1289
1290         #testing EAN13 barcodes hack
1291         $value = $value . '000000000';
1292         $value =~ s/-//;
1293         $value = substr( $value, 0, 12 );
1294
1295         #warn $value;
1296         eval {
1297             PDF::Reuse::Barcode::EAN13(
1298                 x     => ( $x_pos_circ + 27 ),
1299                 y     => ( $y_pos + 15 ),
1300                 value => $value,
1301
1302                 #            prolong => 2.96,
1303                 #            xSize   => 1.5,
1304
1305                 # ySize   => 1.2,
1306
1307 # added for xpdf compat. doesnt use type3 fonts., but increases filesize from 20k to 200k
1308 # i think its embedding extra fonts in the pdf file.
1309 #  mode => 'graphic',
1310             );
1311         };
1312         if ($@) {
1313             $item->{'barcodeerror'} = 1;
1314
1315             #warn "EAN13BARCODE FAILED:$@";
1316         }
1317
1318         #warn $barcodetype;
1319
1320     }
1321     elsif ( $barcodetype eq 'Code39' ) {
1322
1323         eval {
1324             PDF::Reuse::Barcode::Code39(
1325                 x     => ( $x_pos_circ + 9 ),
1326                 y     => ( $y_pos + 15 ),
1327                 value => $value,
1328
1329                 #           prolong => 2.96,
1330                 xSize => .85,
1331
1332                 ySize => 1.3,
1333             );
1334         };
1335         if ($@) {
1336             $item->{'barcodeerror'} = 1;
1337
1338             #warn "CODE39BARCODE $value FAILED:$@";
1339         }
1340
1341         #warn $barcodetype;
1342
1343     }
1344
1345     elsif ( $barcodetype eq 'Matrix2of5' ) {
1346
1347         #warn "MATRIX ELSE:";
1348
1349         #testing MATRIX25  barcodes hack
1350         #    $value = $value.'000000000';
1351         $value =~ s/-//;
1352
1353         #    $value = substr( $value, 0, 12 );
1354         #warn $value;
1355
1356         eval {
1357             PDF::Reuse::Barcode::Matrix2of5(
1358                 x     => ( $x_pos_circ + 27 ),
1359                 y     => ( $y_pos + 15 ),
1360                 value => $value,
1361
1362                 #        prolong => 2.96,
1363                 #       xSize   => 1.5,
1364
1365                 # ySize   => 1.2,
1366             );
1367         };
1368         if ($@) {
1369             $item->{'barcodeerror'} = 1;
1370
1371             #warn "BARCODE FAILED:$@";
1372         }
1373
1374         #warn $barcodetype;
1375
1376     }
1377
1378     elsif ( $barcodetype eq 'EAN8' ) {
1379
1380         #testing ean8 barcodes hack
1381         $value = $value . '000000000';
1382         $value =~ s/-//;
1383         $value = substr( $value, 0, 8 );
1384
1385         #warn $value;
1386
1387         #warn "EAN8 ELSEIF";
1388         eval {
1389             PDF::Reuse::Barcode::EAN8(
1390                 x       => ( $x_pos_circ + 42 ),
1391                 y       => ( $y_pos + 15 ),
1392                 value   => $value,
1393                 prolong => 2.96,
1394                 xSize   => 1.5,
1395
1396                 # ySize   => 1.2,
1397             );
1398         };
1399
1400         if ($@) {
1401             $item->{'barcodeerror'} = 1;
1402
1403             #warn "BARCODE FAILED:$@";
1404         }
1405
1406         #warn $barcodetype;
1407
1408     }
1409
1410     elsif ( $barcodetype eq 'UPC-E' ) {
1411         eval {
1412             PDF::Reuse::Barcode::UPCE(
1413                 x       => ( $x_pos_circ + 27 ),
1414                 y       => ( $y_pos + 15 ),
1415                 value   => $value,
1416                 prolong => 2.96,
1417                 xSize   => 1.5,
1418
1419                 # ySize   => 1.2,
1420             );
1421         };
1422
1423         if ($@) {
1424             $item->{'barcodeerror'} = 1;
1425
1426             #warn "BARCODE FAILED:$@";
1427         }
1428
1429         #warn $barcodetype;
1430
1431     }
1432     elsif ( $barcodetype eq 'NW7' ) {
1433         eval {
1434             PDF::Reuse::Barcode::NW7(
1435                 x       => ( $x_pos_circ + 27 ),
1436                 y       => ( $y_pos + 15 ),
1437                 value   => $value,
1438                 prolong => 2.96,
1439                 xSize   => 1.5,
1440
1441                 # ySize   => 1.2,
1442             );
1443         };
1444
1445         if ($@) {
1446             $item->{'barcodeerror'} = 1;
1447
1448             #warn "BARCODE FAILED:$@";
1449         }
1450
1451         #warn $barcodetype;
1452
1453     }
1454     elsif ( $barcodetype eq 'ITF' ) {
1455         eval {
1456             PDF::Reuse::Barcode::ITF(
1457                 x       => ( $x_pos_circ + 27 ),
1458                 y       => ( $y_pos + 15 ),
1459                 value   => $value,
1460                 prolong => 2.96,
1461                 xSize   => 1.5,
1462
1463                 # ySize   => 1.2,
1464             );
1465         };
1466
1467         if ($@) {
1468             $item->{'barcodeerror'} = 1;
1469
1470             #warn "BARCODE FAILED:$@";
1471         }
1472
1473         #warn $barcodetype;
1474
1475     }
1476     elsif ( $barcodetype eq 'Industrial2of5' ) {
1477         eval {
1478             PDF::Reuse::Barcode::Industrial2of5(
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     elsif ( $barcodetype eq 'IATA2of5' ) {
1498         eval {
1499             PDF::Reuse::Barcode::IATA2of5(
1500                 x       => ( $x_pos_circ + 27 ),
1501                 y       => ( $y_pos + 15 ),
1502                 value   => $value,
1503                 prolong => 2.96,
1504                 xSize   => 1.5,
1505
1506                 # ySize   => 1.2,
1507             );
1508         };
1509         if ($@) {
1510             $item->{'barcodeerror'} = 1;
1511
1512             #warn "BARCODE FAILED:$@";
1513         }
1514
1515         #warn $barcodetype;
1516
1517     }
1518
1519     elsif ( $barcodetype eq 'COOP2of5' ) {
1520         eval {
1521             PDF::Reuse::Barcode::COOP2of5(
1522                 x       => ( $x_pos_circ + 27 ),
1523                 y       => ( $y_pos + 15 ),
1524                 value   => $value,
1525                 prolong => 2.96,
1526                 xSize   => 1.5,
1527
1528                 # ySize   => 1.2,
1529             );
1530         };
1531         if ($@) {
1532             $item->{'barcodeerror'} = 1;
1533
1534             #warn "BARCODE FAILED:$@";
1535         }
1536
1537         #warn $barcodetype;
1538
1539     }
1540     elsif ( $barcodetype eq 'UPC-A' ) {
1541
1542         eval {
1543             PDF::Reuse::Barcode::UPCA(
1544                 x       => ( $x_pos_circ + 27 ),
1545                 y       => ( $y_pos + 15 ),
1546                 value   => $value,
1547                 prolong => 2.96,
1548                 xSize   => 1.5,
1549
1550                 # ySize   => 1.2,
1551             );
1552         };
1553         if ($@) {
1554             $item->{'barcodeerror'} = 1;
1555
1556             #warn "BARCODE FAILED:$@";
1557         }
1558
1559         #warn $barcodetype;
1560
1561     }
1562
1563 }
1564
1565 =item draw_boundaries
1566
1567  sub draw_boundaries ($x_pos_spine, $x_pos_circ1, $x_pos_circ2,
1568                 $y_pos, $spine_width, $label_height, $circ_width)  
1569
1570 This sub draws boundary lines where the label outlines are, to aid in printer testing, and debugging.
1571
1572 =cut
1573
1574 #'
1575 sub draw_boundaries {
1576
1577     my (
1578         $x_pos_spine, $x_pos_circ1,  $x_pos_circ2, $y_pos,
1579         $spine_width, $label_height, $circ_width
1580     ) = @_;
1581
1582     my $y_pos_initial = ( ( 792 - 36 ) - 90 );
1583     $y_pos            = $y_pos_initial; # FIXME - why are we ignoring the y_pos parameter by redefining it?
1584     my $i             = 1;
1585
1586     for ( $i = 1 ; $i <= 8 ; $i++ ) {
1587
1588         &drawbox( $x_pos_spine, $y_pos, ($spine_width), ($label_height) );
1589
1590    #warn "OLD BOXES  x=$x_pos_spine, y=$y_pos, w=$spine_width, h=$label_height";
1591         &drawbox( $x_pos_circ1, $y_pos, ($circ_width), ($label_height) );
1592         &drawbox( $x_pos_circ2, $y_pos, ($circ_width), ($label_height) );
1593
1594         $y_pos = ( $y_pos - $label_height );
1595
1596     }
1597 }
1598
1599 =item drawbox
1600
1601         sub drawbox {   $lower_left_x, $lower_left_y, 
1602                         $upper_right_x, $upper_right_y )
1603
1604 this is a low level sub, that draws a pdf box, it is called by draw_boxes
1605
1606 FYI: the  $upper_right_x and $upper_right_y values are RELATIVE to  $lower_left_x and $lower_left_y
1607
1608 and $lower_left_x, $lower_left_y are ABSOLUTE, this caught me out!
1609
1610 =cut
1611
1612 #'
1613 sub drawbox {
1614     my ( $llx, $lly, $urx, $ury ) = @_;
1615
1616     #    warn "llx,y= $llx,$lly  ,   urx,y=$urx,$ury \n";
1617
1618     my $str = "q\n";    # save the graphic state
1619     $str .= "0.5 w\n";              # border color red
1620     $str .= "1.0 0.0 0.0  RG\n";    # border color red
1621          #   $str .= "0.5 0.75 1.0 rg\n";           # fill color blue
1622     $str .= "1.0 1.0 1.0  rg\n";    # fill color white
1623
1624     $str .= "$llx $lly $urx $ury re\n";    # a rectangle
1625     $str .= "B\n";                         # fill (and a little more)
1626     $str .= "Q\n";                         # save the graphic state
1627
1628     prAdd($str);
1629
1630 }
1631
1632 END { }    # module clean-up code here (global destructor)
1633
1634 1;
1635 __END__
1636
1637 =back
1638
1639 =head1 AUTHOR
1640
1641 Mason James <mason@katipo.co.nz>
1642
1643 =cut
1644