3 # Copyright 2006 Katipo Communications.
5 # This file is part of Koha.
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
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.
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
21 use vars qw($VERSION @ISA @EXPORT);
25 use Algorithm::CheckDigits;
29 # use Smart::Comments;
36 &get_label_options &get_label_items
37 &build_circ_barcode &draw_boundaries
38 &drawbox &GetActiveLabelTemplate
39 &GetAllLabelTemplates &DeleteTemplate
40 &GetSingleLabelTemplate &SaveTemplate
41 &CreateTemplate &SetActiveTemplate
42 &SaveConf &DrawSpineText &GetTextWrapCols
43 &GetUnitsValue &DrawBarcode &DrawPatronCardText
44 &get_printingtypes &GetPatronCardItems
47 &get_batches &delete_batch
51 get_layout &save_layout &add_layout
52 &set_active_layout &by_order
54 &delete_layout &get_active_layout
57 &GetAllPrinterProfiles &GetSinglePrinterProfile
58 &SaveProfile &CreateProfile &DeleteProfile
59 &GetAssociatedProfile &SetAssociatedProfile
67 C4::Labels - Functions for printing spine labels and barcodes in Koha
73 =item get_label_options;
75 $options = get_label_options()
77 Return a pointer on a hash list containing info from labels_conf table in Koha DB.
82 sub get_label_options {
83 my $dbh = C4::Context->dbh;
84 my $query2 = " SELECT * FROM labels_conf where active = 1";
85 my $sth = $dbh->prepare($query2);
87 my $conf_data = $sth->fetchrow_hashref;
94 ## FIXME: this if/else could be compacted...
95 my $dbh = C4::Context->dbh;
97 my $query = " Select * from labels_conf";
98 my $sth = $dbh->prepare($query);
101 while ( my $data = $sth->fetchrow_hashref ) {
103 $data->{'fieldlist'} = get_text_fields( $data->{'id'} );
104 push( @resultsloop, $data );
114 my ($layout_id) = @_;
115 my $dbh = C4::Context->dbh;
117 # get the actual items to be printed.
118 my $query = " Select * from labels_conf where id = ?";
119 my $sth = $dbh->prepare($query);
120 $sth->execute($layout_id);
121 my $data = $sth->fetchrow_hashref;
126 sub get_active_layout {
127 my ($layout_id) = @_;
128 my $dbh = C4::Context->dbh;
130 # get the actual items to be printed.
131 my $query = " Select * from labels_conf where active = 1";
132 my $sth = $dbh->prepare($query);
134 my $data = $sth->fetchrow_hashref;
140 my ($layout_id) = @_;
141 my $dbh = C4::Context->dbh;
143 # get the actual items to be printed.
144 my $query = "delete from labels_conf where id = ?";
145 my $sth = $dbh->prepare($query);
146 $sth->execute($layout_id);
150 sub get_printingtypes {
151 my ($layout_id) = @_;
154 push( @printtypes, { code => 'BAR', desc => "barcode" } );
155 push( @printtypes, { code => 'BIB', desc => "biblio" } );
156 push( @printtypes, { code => 'BARBIB', desc => "barcode / biblio" } );
157 push( @printtypes, { code => 'BIBBAR', desc => "biblio / barcode" } );
158 push( @printtypes, { code => 'ALT', desc => "alternating labels" } );
160 my $conf = get_layout($layout_id);
161 my $active_printtype = $conf->{'printingtype'};
163 # lop thru layout, insert selected to hash
165 foreach my $printtype (@printtypes) {
166 if ( $printtype->{'code'} eq $active_printtype ) {
167 $printtype->{'active'} = 'MOO';
173 sub build_text_dropbox {
176 # my @fields = get_text_fields();
177 # my $field_count = scalar @fields;
178 my $field_count = 10; # <----------- FIXME hard coded
182 ? push( @lines, { num => '', selected => '1' } )
183 : push( @lines, { num => '' } );
184 for ( my $i = 1 ; $i <= $field_count ; $i++ ) {
185 my $line = { num => "$i" };
186 $line->{'selected'} = 1 if $i eq $order;
187 push( @lines, $line );
190 # add a blank row too
195 sub get_text_fields {
196 my ($layout_id, $sorttype) = @_;
198 my ( $a, $b, $c, $d, $e, $f, $g, $h, $i ,$j, $k );
200 my $sortorder = get_layout($layout_id);
207 order => $sortorder->{'itemtype'}
212 order => $sortorder->{'dewey'}
214 $c = { code => 'issn', desc => "ISSN",
215 order => $sortorder->{'issn'} };
216 $d = { code => 'isbn', desc => "ISBN",
217 order => $sortorder->{'isbn'} };
220 desc => "Classification",
221 order => $sortorder->{'class'}
226 order => $sortorder->{'subclass'}
231 order => $sortorder->{'barcode'}
234 { code => 'author', desc => "Author", order => $sortorder->{'author'} };
235 $i = { code => 'title', desc => "Title", order => $sortorder->{'title'} };
236 $j = { code => 'itemcallnumber', desc => "Call Number", order => $sortorder->{'itemcallnumber'} };
237 $k = { code => 'subtitle', desc => "Subtitle", order => $sortorder->{'subtitle'} };
239 my @text_fields = ( $a, $b, $c, $d, $e, $f, $g, $h, $i ,$j, $k );
242 foreach my $field (@text_fields) {
243 push( @new_fields, $field ) if $field->{'order'} > 0;
246 my @sorted_fields = sort by_order @new_fields;
248 foreach my $field (@sorted_fields) {
249 $sorttype eq 'codes' ? $active_fields .= "$field->{'code'} " :
250 $active_fields .= "$field->{'desc'} ";
252 return $active_fields;
257 $$a{order} <=> $$b{order};
261 my ( $batch_type ) = @_;
263 my $dbh = C4::Context->dbh;
265 "SELECT DISTINCT batch_id FROM $batch_type ORDER BY batch_id desc LIMIT 1";
266 my $sth = $dbh->prepare($q);
268 my $data = $sth->fetchrow_hashref;
271 if ( !$data->{'batch_id'} ) {
275 $new_batch = ( $data->{'batch_id'} + 1 );
281 #FIXME: Needs to be ported to receive $batch_type
282 sub get_highest_batch {
284 my $dbh = C4::Context->dbh;
286 "select distinct batch_id from labels order by batch_id desc limit 1";
287 my $sth = $dbh->prepare($q);
289 my $data = $sth->fetchrow_hashref;
292 if ( !$data->{'batch_id'} ) {
296 $new_batch = $data->{'batch_id'};
303 #FIXME: Needs to be ported to receive $batch_type
305 my $dbh = C4::Context->dbh;
306 my $q = "select batch_id, count(*) as num from labels group by batch_id";
307 my $sth = $dbh->prepare($q);
310 while ( my $data = $sth->fetchrow_hashref ) {
311 push( @resultsloop, $data );
315 # Not sure why we are doing this rather than simply telling the user that no batches are currently defined.
316 # So I'm commenting this out and modifying label-manager.tmpl to properly inform the user as stated. -fbcit
317 # adding a dummy batch=1 value , if none exists in the db
318 # if ( !scalar(@resultsloop) ) {
319 # push( @resultsloop, { batch_id => '1' , num => '0' } );
325 my ($batch_id, $batch_type) = @_;
326 warn "Deleteing batch of type $batch_type";
327 my $dbh = C4::Context->dbh;
328 my $q = "DELETE FROM $batch_type WHERE batch_id = ?";
329 my $sth = $dbh->prepare($q);
330 $sth->execute($batch_id);
334 sub get_barcode_types {
335 my ($layout_id) = @_;
336 my $layout = get_layout($layout_id);
337 my $barcode = $layout->{'barcodetype'};
340 push( @array, { code => 'CODE39', desc => 'Code 39' } );
341 push( @array, { code => 'CODE39MOD', desc => 'Code39 + Modulo43' } );
342 push( @array, { code => 'CODE39MOD10', desc => 'Code39 + Modulo10' } );
343 push( @array, { code => 'ITF', desc => 'Interleaved 2 of 5' } );
345 foreach my $line (@array) {
346 if ( $line->{'code'} eq $barcode ) {
347 $line->{'active'} = 1;
358 $unitvalue = '1' if ( $units eq 'POINT' );
359 $unitvalue = '2.83464567' if ( $units eq 'MM' );
360 $unitvalue = '28.3464567' if ( $units eq 'CM' );
361 $unitvalue = 72 if ( $units eq 'INCH' );
365 sub GetTextWrapCols {
366 my ( $font, $fontsize, $label_width, $left_text_margin ) = @_;
370 # my $textlimit = $label_width - ($left_text_margin);
371 my $textlimit = $label_width - ( 2* $left_text_margin);
373 while ( $strwidth < $textlimit ) {
374 $strwidth = prStrWidth( $string, $font, $fontsize );
375 $string = $string . '0';
376 #warn "strwidth:$strwidth, textlimit:$textlimit, count:$count string:$string";
382 sub GetActiveLabelTemplate {
383 my $dbh = C4::Context->dbh;
384 my $query = " SELECT * FROM labels_templates where active = 1 limit 1";
385 my $sth = $dbh->prepare($query);
387 my $active_tmpl = $sth->fetchrow_hashref;
392 sub GetSingleLabelTemplate {
394 my $dbh = C4::Context->dbh;
395 my $query = " SELECT * FROM labels_templates where tmpl_id = ?";
396 my $sth = $dbh->prepare($query);
397 $sth->execute($tmpl_id);
398 my $template = $sth->fetchrow_hashref;
403 sub SetActiveTemplate {
407 my $dbh = C4::Context->dbh;
408 my $query = " UPDATE labels_templates SET active = NULL";
409 my $sth = $dbh->prepare($query);
412 $query = "UPDATE labels_templates SET active = 1 WHERE tmpl_id = ?";
413 $sth = $dbh->prepare($query);
414 $sth->execute($tmpl_id);
418 sub set_active_layout {
420 my ($layout_id) = @_;
421 my $dbh = C4::Context->dbh;
422 my $query = " UPDATE labels_conf SET active = NULL";
423 my $sth = $dbh->prepare($query);
426 $query = "UPDATE labels_conf SET active = 1 WHERE id = ?";
427 $sth = $dbh->prepare($query);
428 $sth->execute($layout_id);
434 my $dbh = C4::Context->dbh;
435 my $query = " DELETE FROM labels_templates where tmpl_id = ?";
436 my $sth = $dbh->prepare($query);
437 $sth->execute($tmpl_id);
443 $tmpl_id, $tmpl_code, $tmpl_desc, $page_width,
444 $page_height, $label_width, $label_height, $topmargin,
445 $leftmargin, $cols, $rows, $colgap,
446 $rowgap, $font, $fontsize, $units
448 warn "Passed \$font:$font";
449 my $dbh = C4::Context->dbh;
451 " UPDATE labels_templates SET tmpl_code=?, tmpl_desc=?, page_width=?,
452 page_height=?, label_width=?, label_height=?, topmargin=?,
453 leftmargin=?, cols=?, rows=?, colgap=?, rowgap=?, font=?, fontsize=?,
457 my $sth = $dbh->prepare($query);
459 $tmpl_code, $tmpl_desc, $page_width, $page_height,
460 $label_width, $label_height, $topmargin, $leftmargin,
461 $cols, $rows, $colgap, $rowgap,
462 $font, $fontsize, $units, $tmpl_id
464 my $dberror = $sth->errstr;
472 $tmpl_code, $tmpl_desc, $page_width, $page_height,
473 $label_width, $label_height, $topmargin, $leftmargin,
474 $cols, $rows, $colgap, $rowgap,
475 $font, $fontsize, $units
478 my $dbh = C4::Context->dbh;
480 my $query = "INSERT INTO labels_templates (tmpl_code, tmpl_desc, page_width,
481 page_height, label_width, label_height, topmargin,
482 leftmargin, cols, rows, colgap, rowgap, font, fontsize, units)
483 VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
485 my $sth = $dbh->prepare($query);
487 $tmpl_code, $tmpl_desc, $page_width, $page_height,
488 $label_width, $label_height, $topmargin, $leftmargin,
489 $cols, $rows, $colgap, $rowgap,
490 $font, $fontsize, $units
492 my $dberror = $sth->errstr;
497 sub GetAllLabelTemplates {
498 my $dbh = C4::Context->dbh;
500 # get the actual items to be printed.
502 my $query = " Select * from labels_templates ";
503 my $sth = $dbh->prepare($query);
506 while ( my $data = $sth->fetchrow_hashref ) {
507 push( @resultsloop, $data );
511 #warn Dumper @resultsloop;
519 $barcodetype, $title, $subtitle, $isbn, $issn,
520 $itemtype, $bcn, $dcn, $classif,
521 $subclass, $itemcallnumber, $author, $tmpl_id,
522 $printingtype, $guidebox, $startlabel, $layoutname
525 my $dbh = C4::Context->dbh;
526 my $query2 = "update labels_conf set active = NULL";
527 my $sth2 = $dbh->prepare($query2);
529 $query2 = "INSERT INTO labels_conf
530 ( barcodetype, title, subtitle, isbn,issn, itemtype, barcode,
531 dewey, class, subclass, itemcallnumber, author, printingtype,
532 guidebox, startlabel, layoutname, active )
533 values ( ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?,?,?, 1 )";
534 $sth2 = $dbh->prepare($query2);
536 $barcodetype, $title, $subtitle, $isbn, $issn,
538 $itemtype, $bcn, $dcn, $classif,
539 $subclass, $itemcallnumber, $author, $printingtype,
540 $guidebox, $startlabel, $layoutname
544 SetActiveTemplate($tmpl_id);
551 $barcodetype, $title, $subtitle, $isbn, $issn,
552 $itemtype, $bcn, $dcn, $classif,
553 $subclass, $itemcallnumber, $author, $tmpl_id,
554 $printingtype, $guidebox, $startlabel, $layoutname,
560 my $dbh = C4::Context->dbh;
561 my $query2 = "update labels_conf set
562 barcodetype=?, title=?, subtitle=?, isbn=?,issn=?,
563 itemtype=?, barcode=?, dewey=?, class=?,
564 subclass=?, itemcallnumber=?, author=?, printingtype=?,
565 guidebox=?, startlabel=?, layoutname=? where id = ?";
566 my $sth2 = $dbh->prepare($query2);
568 $barcodetype, $title, $subtitle, $isbn, $issn,
569 $itemtype, $bcn, $dcn, $classif,
570 $subclass, $itemcallnumber, $author, $printingtype,
571 $guidebox, $startlabel, $layoutname, $layout_id
578 =item GetAllPrinterProfiles;
580 @profiles = GetAllPrinterProfiles()
582 Returns an array of references-to-hash, whos keys are .....
586 sub GetAllPrinterProfiles {
588 my $dbh = C4::Context->dbh;
590 my $query = "SELECT * FROM printers_profile AS pp INNER JOIN labels_templates AS lt ON pp.tmpl_id = lt.tmpl_id; ";
591 my $sth = $dbh->prepare($query);
594 while ( my $data = $sth->fetchrow_hashref ) {
595 push( @resultsloop, $data );
602 =item GetSinglePrinterProfile;
604 $profile = GetSinglePrinterProfile()
606 Returns a hashref whos keys are...
610 sub GetSinglePrinterProfile {
612 my $dbh = C4::Context->dbh;
613 my $query = " SELECT * FROM printers_profile WHERE prof_id = ?; ";
614 my $sth = $dbh->prepare($query);
615 $sth->execute($prof_id);
616 my $template = $sth->fetchrow_hashref;
623 SaveProfile('parameters')
625 When passed a set of parameters, this function updates the given profile with the new parameters.
631 $prof_id, $offset_horz, $offset_vert, $creep_horz, $creep_vert, $units
633 my $dbh = C4::Context->dbh;
635 " UPDATE printers_profile
636 SET offset_horz=?, offset_vert=?, creep_horz=?, creep_vert=?, unit=?
638 my $sth = $dbh->prepare($query);
640 $offset_horz, $offset_vert, $creep_horz, $creep_vert, $units, $prof_id
647 CreateProfile('parameters')
649 When passed a set of parameters, this function creates a new profile containing those parameters
650 and returns any errors.
656 $prof_id, $printername, $paper_bin, $tmpl_id, $offset_horz,
657 $offset_vert, $creep_horz, $creep_vert, $units
659 my $dbh = C4::Context->dbh;
661 " INSERT INTO printers_profile (prof_id, printername, paper_bin, tmpl_id,
662 offset_horz, offset_vert, creep_horz, creep_vert, unit)
663 VALUES(?,?,?,?,?,?,?,?,?) ";
664 my $sth = $dbh->prepare($query);
666 $prof_id, $printername, $paper_bin, $tmpl_id, $offset_horz,
667 $offset_vert, $creep_horz, $creep_vert, $units
669 my $error = $sth->errstr;
676 DeleteProfile(prof_id)
678 When passed a profile id, this function deletes that profile from the database and returns any errors.
684 my $dbh = C4::Context->dbh;
685 my $query = " DELETE FROM printers_profile WHERE prof_id = ?";
686 my $sth = $dbh->prepare($query);
687 $sth->execute($prof_id);
688 my $error = $sth->errstr;
693 =item GetAssociatedProfile;
695 $assoc_prof = GetAssociatedProfile(tmpl_id)
697 When passed a template id, this function returns the parameters from the currently associated printer profile
698 in a hashref where key=fieldname and value=fieldvalue.
702 sub GetAssociatedProfile {
704 my $dbh = C4::Context->dbh;
705 # First we find out the prof_id for the associated profile...
706 my $query = "SELECT * FROM labels_profile WHERE tmpl_id = ?";
707 my $sth = $dbh->prepare($query);
708 $sth->execute($tmpl_id);
709 my $assoc_prof = $sth->fetchrow_hashref;
711 # Then we retrieve that profile and return it to the caller...
712 $assoc_prof = GetSinglePrinterProfile($assoc_prof->{'prof_id'});
716 =item SetAssociatedProfile;
718 SetAssociatedProfile($prof_id, $tmpl_id)
720 When passed both a profile id and template id, this function establishes an association between the two. No more
721 than one profile may be associated with any given template at the same time.
725 sub SetAssociatedProfile {
727 my ($prof_id, $tmpl_id) = @_;
729 my $dbh = C4::Context->dbh;
730 my $query = "INSERT INTO labels_profile (prof_id, tmpl_id) VALUES (?,?) ON DUPLICATE KEY UPDATE prof_id = ?";
731 my $sth = $dbh->prepare($query);
732 $sth->execute($prof_id, $tmpl_id, $prof_id);
736 =item get_label_items;
738 $options = get_label_items()
740 Returns an array of references-to-hash, whos keys are the field from the biblio, biblioitems, items and labels tables in the Koha database.
745 sub get_label_items {
747 my $dbh = C4::Context->dbh;
749 my @resultsloop = ();
755 my $query3 = "Select * from labels where batch_id = ? order by labelid ";
756 $sth = $dbh->prepare($query3);
757 $sth->execute($batch_id);
762 my $query3 = "Select * from labels";
763 $sth = $dbh->prepare($query3);
766 my $cnt = $sth->rows;
768 while ( my $data = $sth->fetchrow_hashref ) {
770 # lets get some summary info from each item
772 select i.*, bi.*, b.* from items i,biblioitems bi,biblio b
773 where itemnumber=? and i.biblioitemnumber=bi.biblioitemnumber and
774 bi.biblionumber=b.biblionumber";
776 my $sth1 = $dbh->prepare($query1);
777 $sth1->execute( $data->{'itemnumber'} );
779 my $data1 = $sth1->fetchrow_hashref();
780 $data1->{'labelno'} = $i1;
781 $data1->{'labelid'} = $data->{'labelid'};
782 $data1->{'batch_id'} = $batch_id;
783 $data1->{'summary'} =
784 "$data1->{'barcode'}, $data1->{'title'}, $data1->{'isbn'}";
786 push( @resultsloop, $data1 );
798 barcode title subtitle
799 dewey isbn issn author class
800 itemtype subclass itemcallnumber
806 sub GetPatronCardItems {
808 my ( $batch_id ) = @_;
811 warn "Received batch id: $batch_id";
812 my $dbh = C4::Context->dbh;
813 my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY borrowernumber";
814 my $sth = $dbh->prepare($query);
815 warn "Executing query...\n";
816 $sth->execute($batch_id);
817 warn "Parsing results...\n";
818 while ( my $data = $sth->fetchrow_hashref ) {
819 warn "for borrowernumber $data->{'borrowernumber'}\n";
820 my $patron_data = GetMember( $data->{'borrowernumber'} );
821 $patron_data->{'branchname'} = GetBranchName( $patron_data->{'branchcode'} );
822 push( @resultsloop, $patron_data );
829 sub deduplicate_batch {
830 my $batch_id = shift or return undef;
834 count(labelid) as count
837 GROUP BY itemnumber,batch_id
841 my $sth = C4::Context->dbh->prepare($query);
842 $sth->execute($batch_id);
843 $sth->rows or return undef;
850 ORDER BY timestamp ASC
853 while (my $data = $sth->fetchrow_hashref()) {
854 my $itemnumber = $data->{itemnumber} or next;
855 my $limit = $data->{count} - 1 or next;
856 my $sth2 = C4::Context->dbh->prepare("$del_query LIMIT $limit");
857 # die sprintf "$del_query LIMIT %s\n (%s, %s)", $limit, $batch_id, $itemnumber;
858 # $sth2->execute($batch_id, C4::Context->dbh->quote($data->{itemnumber}), $data->{count} - 1)
859 $sth2->execute($batch_id, $itemnumber) and
860 $killed += ($data->{count} - 1);
867 my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
868 $text_wrap_cols, $item, $conf_data, $printingtype )
870 # hack to fix column name mismatch betwen labels_conf.class, and bibitems.classification
871 $$item->{'class'} = $$item->{'classification'};
873 $Text::Wrap::columns = $text_wrap_cols;
874 $Text::Wrap::separator = "\n";
878 my $top_text_margin = ( $fontsize + 3 ); #FIXME: This should be a template parameter and passed in...
879 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.).
881 # add your printable fields manually in here
883 my $layout_id = $$conf_data->{'id'};
885 # my @fields = GetItemFields();
887 my $str_fields = get_text_fields($layout_id, 'codes' );
888 my @fields = split(/ /, $str_fields);
889 #warn Dumper(@fields);
891 my $vPos = ( $y_pos + ( $label_height - $top_text_margin ) );
892 my $font = prFont($fontname);
894 # warn Dumper $conf_data;
897 foreach my $field (@fields) {
900 # $$item->{"$field"} = $field . ": " . $$item->{"$field"};
902 # if the display option for this field is selected in the DB,
903 # and the item record has some values for this field, display it.
904 if ( $$conf_data->{"$field"} && $$item->{"$field"} ) {
906 # warn "CONF_TYPE = $field";
909 $str = $$item->{"$field"};
910 # strip out naughty existing nl/cr's
913 # wrap lines based on call number dividers '/'
916 while ( $str =~ /\// ) {
917 $str =~ /^(.*)\/(.*)$/;
920 unshift @strings, $2;
924 unshift @strings, $str;
926 # strip out division slashes
928 #warn "\$str after striping division marks: $str";
929 # chop the string up into _upto_ 12 chunks
930 # and seperate the chunks with newlines
932 #$str = wrap( "", "", "$str" );
933 #$str = wrap( "", "", "$str" );
935 # split the chunks between newline's, into an array
936 #my @strings = split /\n/, $str;
938 # then loop for each string line
939 foreach my $str (@strings) {
941 if ( $printingtype eq 'BIB' ) { #FIXME: This is a hack and needs to be implimented as a text justification option in the template...
942 # some code to try and center each line on the label based on font size and string point width...
943 my $stringwidth = prStrWidth($str, $fontname, $fontsize);
944 my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
945 $hPos = ( ( $whitespace / 2 ) + $x_pos + $left_text_margin );
946 #warn "\$label_width=$label_width \$stringwidth=$stringwidth \$whitespace=$whitespace \$left_text_margin=$left_text_margin for $str\n";
948 $hPos = ( $x_pos + $left_text_margin );
950 PrintText( $hPos, $vPos, $font, $fontsize, $str );
951 $vPos = $vPos - $line_spacer;
959 my ( $hPos, $vPos, $font, $fontsize, $text ) = @_;
960 my $str = "BT /$font $fontsize Tf $hPos $vPos Td ($text) Tj ET";
965 sub DrawPatronCardText {
967 my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
968 $text_wrap_cols, $text, $printingtype )
971 my $top_text_margin = 25; #FIXME: This should be a template parameter and passed in...
973 my $vPos = ( $y_pos + ( $label_height - $top_text_margin ) );
974 my $font = prFont($fontname);
978 foreach my $line (keys %$text) {
979 warn "Current text is \"$line\" and font size for \"$line\" is $text->{$line} points";
980 # some code to try and center each line on the label based on font size and string point width...
981 my $stringwidth = prStrWidth($line, $fontname, $text->{$line});
982 my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
983 $hPos = ( ( $whitespace / 2 ) + $x_pos + $left_text_margin );
985 PrintText( $hPos, $vPos, $font, $text->{$line}, $line );
986 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.).
987 $vPos = $vPos - ($line_spacer + $text->{$line}); # Linefeed equiv: leading + font size
995 # my ($fontsize) = @_;
997 # my $str = "BT/F13 30 Tf288 720 Td( AAAAAAAAAA ) TjET";
1003 # x and y are from the top-left :)
1004 my ( $x_pos, $y_pos, $height, $width, $barcode, $barcodetype ) = @_;
1005 my $num_of_bars = length($barcode);
1006 my $bar_width = $width * .8; # %80 of length of label width
1009 my $guard_length = 10;
1012 if ( $barcodetype eq 'CODE39' ) {
1013 $bar_length = '17.5';
1015 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1016 $xsize_ratio = ( $bar_width / $tot_bar_length );
1018 PDF::Reuse::Barcode::Code39(
1019 x => ( $x_pos + ( $width / 10 ) ),
1020 y => ( $y_pos + ( $height / 10 ) ),
1021 value => "*$barcode*",
1022 ySize => ( .02 * $height ),
1023 xSize => $xsize_ratio,
1028 warn "$barcodetype, $barcode FAILED:$@";
1032 elsif ( $barcodetype eq 'CODE39MOD' ) {
1034 # get modulo43 checksum
1035 my $c39 = CheckDigits('code_39');
1036 $barcode = $c39->complete($barcode);
1040 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1041 $xsize_ratio = ( $bar_width / $tot_bar_length );
1043 PDF::Reuse::Barcode::Code39(
1044 x => ( $x_pos + ( $width / 10 ) ),
1045 y => ( $y_pos + ( $height / 10 ) ),
1046 value => "*$barcode*",
1047 ySize => ( .02 * $height ),
1048 xSize => $xsize_ratio,
1054 warn "$barcodetype, $barcode FAILED:$@";
1057 elsif ( $barcodetype eq 'CODE39MOD10' ) {
1059 # get modulo43 checksum
1060 my $c39_10 = CheckDigits('visa');
1061 $barcode = $c39_10->complete($barcode);
1065 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1066 $xsize_ratio = ( $bar_width / $tot_bar_length );
1068 PDF::Reuse::Barcode::Code39(
1069 x => ( $x_pos + ( $width / 10 ) ),
1070 y => ( $y_pos + ( $height / 10 ) ),
1071 value => "*$barcode*",
1072 ySize => ( .02 * $height ),
1073 xSize => $xsize_ratio,
1080 warn "$barcodetype, $barcode FAILED:$@";
1085 elsif ( $barcodetype eq 'COOP2OF5' ) {
1086 $bar_length = '9.43333333333333';
1088 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1089 $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1091 PDF::Reuse::Barcode::COOP2of5(
1092 x => ( $x_pos + ( $width / 10 ) ),
1093 y => ( $y_pos + ( $height / 10 ) ),
1095 ySize => ( .02 * $height ),
1096 xSize => $xsize_ratio,
1100 warn "$barcodetype, $barcode FAILED:$@";
1104 elsif ( $barcodetype eq 'INDUSTRIAL2OF5' ) {
1105 $bar_length = '13.1333333333333';
1107 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1108 $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1110 PDF::Reuse::Barcode::Industrial2of5(
1111 x => ( $x_pos + ( $width / 10 ) ),
1112 y => ( $y_pos + ( $height / 10 ) ),
1114 ySize => ( .02 * $height ),
1115 xSize => $xsize_ratio,
1119 warn "$barcodetype, $barcode FAILED:$@";
1123 my $moo2 = $tot_bar_length * $xsize_ratio;
1125 warn "$x_pos, $y_pos, $barcode, $barcodetype" if $DEBUG;
1126 warn "BAR_WDTH = $bar_width, TOT.BAR.LGHT=$tot_bar_length R*TOT.BAR =$moo2" if $DEBUG;
1129 =item build_circ_barcode;
1131 build_circ_barcode( $x_pos, $y_pos, $barcode,
1132 $barcodetype, \$item);
1134 $item is the result of a previous call to get_label_items();
1139 sub build_circ_barcode {
1140 my ( $x_pos_circ, $y_pos, $value, $barcodetype, $item ) = @_;
1142 #warn Dumper \$item;
1144 #warn "value = $value\n";
1148 if ( $barcodetype eq 'EAN13' ) {
1150 #testing EAN13 barcodes hack
1151 $value = $value . '000000000';
1153 $value = substr( $value, 0, 12 );
1157 PDF::Reuse::Barcode::EAN13(
1158 x => ( $x_pos_circ + 27 ),
1159 y => ( $y_pos + 15 ),
1167 # added for xpdf compat. doesnt use type3 fonts., but increases filesize from 20k to 200k
1168 # i think its embedding extra fonts in the pdf file.
1169 # mode => 'graphic',
1173 $item->{'barcodeerror'} = 1;
1175 #warn "EAN13BARCODE FAILED:$@";
1181 elsif ( $barcodetype eq 'Code39' ) {
1184 PDF::Reuse::Barcode::Code39(
1185 x => ( $x_pos_circ + 9 ),
1186 y => ( $y_pos + 15 ),
1196 $item->{'barcodeerror'} = 1;
1198 #warn "CODE39BARCODE $value FAILED:$@";
1205 elsif ( $barcodetype eq 'Matrix2of5' ) {
1207 #warn "MATRIX ELSE:";
1209 #testing MATRIX25 barcodes hack
1210 # $value = $value.'000000000';
1213 # $value = substr( $value, 0, 12 );
1217 PDF::Reuse::Barcode::Matrix2of5(
1218 x => ( $x_pos_circ + 27 ),
1219 y => ( $y_pos + 15 ),
1229 $item->{'barcodeerror'} = 1;
1231 #warn "BARCODE FAILED:$@";
1238 elsif ( $barcodetype eq 'EAN8' ) {
1240 #testing ean8 barcodes hack
1241 $value = $value . '000000000';
1243 $value = substr( $value, 0, 8 );
1247 #warn "EAN8 ELSEIF";
1249 PDF::Reuse::Barcode::EAN8(
1250 x => ( $x_pos_circ + 42 ),
1251 y => ( $y_pos + 15 ),
1261 $item->{'barcodeerror'} = 1;
1263 #warn "BARCODE FAILED:$@";
1270 elsif ( $barcodetype eq 'UPC-E' ) {
1272 PDF::Reuse::Barcode::UPCE(
1273 x => ( $x_pos_circ + 27 ),
1274 y => ( $y_pos + 15 ),
1284 $item->{'barcodeerror'} = 1;
1286 #warn "BARCODE FAILED:$@";
1292 elsif ( $barcodetype eq 'NW7' ) {
1294 PDF::Reuse::Barcode::NW7(
1295 x => ( $x_pos_circ + 27 ),
1296 y => ( $y_pos + 15 ),
1306 $item->{'barcodeerror'} = 1;
1308 #warn "BARCODE FAILED:$@";
1314 elsif ( $barcodetype eq 'ITF' ) {
1316 PDF::Reuse::Barcode::ITF(
1317 x => ( $x_pos_circ + 27 ),
1318 y => ( $y_pos + 15 ),
1328 $item->{'barcodeerror'} = 1;
1330 #warn "BARCODE FAILED:$@";
1336 elsif ( $barcodetype eq 'Industrial2of5' ) {
1338 PDF::Reuse::Barcode::Industrial2of5(
1339 x => ( $x_pos_circ + 27 ),
1340 y => ( $y_pos + 15 ),
1349 $item->{'barcodeerror'} = 1;
1351 #warn "BARCODE FAILED:$@";
1357 elsif ( $barcodetype eq 'IATA2of5' ) {
1359 PDF::Reuse::Barcode::IATA2of5(
1360 x => ( $x_pos_circ + 27 ),
1361 y => ( $y_pos + 15 ),
1370 $item->{'barcodeerror'} = 1;
1372 #warn "BARCODE FAILED:$@";
1379 elsif ( $barcodetype eq 'COOP2of5' ) {
1381 PDF::Reuse::Barcode::COOP2of5(
1382 x => ( $x_pos_circ + 27 ),
1383 y => ( $y_pos + 15 ),
1392 $item->{'barcodeerror'} = 1;
1394 #warn "BARCODE FAILED:$@";
1400 elsif ( $barcodetype eq 'UPC-A' ) {
1403 PDF::Reuse::Barcode::UPCA(
1404 x => ( $x_pos_circ + 27 ),
1405 y => ( $y_pos + 15 ),
1414 $item->{'barcodeerror'} = 1;
1416 #warn "BARCODE FAILED:$@";
1425 =item draw_boundaries
1427 sub draw_boundaries ($x_pos_spine, $x_pos_circ1, $x_pos_circ2,
1428 $y_pos, $spine_width, $label_height, $circ_width)
1430 This sub draws boundary lines where the label outlines are, to aid in printer testing, and debugging.
1435 sub draw_boundaries {
1438 $x_pos_spine, $x_pos_circ1, $x_pos_circ2, $y_pos,
1439 $spine_width, $label_height, $circ_width
1442 my $y_pos_initial = ( ( 792 - 36 ) - 90 );
1443 $y_pos = $y_pos_initial; # FIXME - why are we ignoring the y_pos parameter by redefining it?
1446 for ( $i = 1 ; $i <= 8 ; $i++ ) {
1448 &drawbox( $x_pos_spine, $y_pos, ($spine_width), ($label_height) );
1450 #warn "OLD BOXES x=$x_pos_spine, y=$y_pos, w=$spine_width, h=$label_height";
1451 &drawbox( $x_pos_circ1, $y_pos, ($circ_width), ($label_height) );
1452 &drawbox( $x_pos_circ2, $y_pos, ($circ_width), ($label_height) );
1454 $y_pos = ( $y_pos - $label_height );
1461 sub drawbox { $lower_left_x, $lower_left_y,
1462 $upper_right_x, $upper_right_y )
1464 this is a low level sub, that draws a pdf box, it is called by draw_boxes
1466 FYI: the $upper_right_x and $upper_right_y values are RELATIVE to $lower_left_x and $lower_left_y
1468 and $lower_left_x, $lower_left_y are ABSOLUTE, this caught me out!
1474 my ( $llx, $lly, $urx, $ury ) = @_;
1476 # warn "llx,y= $llx,$lly , urx,y=$urx,$ury \n";
1478 my $str = "q\n"; # save the graphic state
1479 $str .= "0.5 w\n"; # border color red
1480 $str .= "1.0 0.0 0.0 RG\n"; # border color red
1481 # $str .= "0.5 0.75 1.0 rg\n"; # fill color blue
1482 $str .= "1.0 1.0 1.0 rg\n"; # fill color white
1484 $str .= "$llx $lly $urx $ury re\n"; # a rectangle
1485 $str .= "B\n"; # fill (and a little more)
1486 $str .= "Q\n"; # save the graphic state
1492 END { } # module clean-up code here (global destructor)
1501 Mason James <mason@katipo.co.nz>