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 &GetLabelItems
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) = @_;
153 # FIXME: hard coded print types
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" } );
159 push( @printtypes, { code => 'PATCRD', desc => "patron cards" } );
161 my $conf = get_layout($layout_id);
162 my $active_printtype = $conf->{'printingtype'};
164 # lop thru layout, insert selected to hash
166 foreach my $printtype (@printtypes) {
167 if ( $printtype->{'code'} eq $active_printtype ) {
168 $printtype->{'active'} = 'MOO';
174 sub build_text_dropbox {
177 # my @fields = get_text_fields();
178 # my $field_count = scalar @fields;
179 my $field_count = 10; # <----------- FIXME hard coded
183 ? push( @lines, { num => '', selected => '1' } )
184 : push( @lines, { num => '' } );
185 for ( my $i = 1 ; $i <= $field_count ; $i++ ) {
186 my $line = { num => "$i" };
187 $line->{'selected'} = 1 if $i eq $order;
188 push( @lines, $line );
191 # add a blank row too
196 sub get_text_fields {
197 my ($layout_id, $sorttype) = @_;
199 my ( $a, $b, $c, $d, $e, $f, $g, $h, $i ,$j, $k );
201 my $sortorder = get_layout($layout_id);
203 # FIXME: This is all hardcoded and should be user selectable I think or are these the only text fields? -fbcit
207 order => $sortorder->{'itemtype'}
213 order => $sortorder->{'dewey'}
219 order => $sortorder->{'issn'}
225 order => $sortorder->{'isbn'}
230 desc => "Classification",
231 order => $sortorder->{'class'}
237 order => $sortorder->{'subclass'}
243 order => $sortorder->{'barcode'}
249 order => $sortorder->{'author'}
255 order => $sortorder->{'title'}
259 code => 'itemcallnumber',
260 desc => "Call Number",
261 order => $sortorder->{'itemcallnumber'}
267 order => $sortorder->{'subtitle'}
270 my @text_fields = ( $a, $b, $c, $d, $e, $f, $g, $h, $i ,$j, $k );
273 foreach my $field (@text_fields) {
274 push( @new_fields, $field ) if $field->{'order'} > 0;
277 my @sorted_fields = sort by_order @new_fields;
281 if ($sorttype eq 'codes') { # FIXME: This sub should really always return the array of hashrefs and let the caller take what he wants from that -fbcit
282 return @sorted_fields;
284 foreach my $field (@sorted_fields) {
285 $active_fields .= "$field->{'desc'} ";
287 return $active_fields;
293 $$a{order} <=> $$b{order};
298 add_batch($batch_type,\@batch_list);
299 if $batch_list is supplied,
300 create a new batch with those items.
301 else, return the next available batch_id.
305 my ( $batch_type,$batch_list ) = @_;
307 my $dbh = C4::Context->dbh;
308 my $q ="SELECT MAX(DISTINCT batch_id) FROM $batch_type";
309 my $sth = $dbh->prepare($q);
311 my ($batch_id) = $sth->fetchrow_array;
318 # TODO: let this block use $batch_type
319 if(ref($batch_list) && ($batch_type eq 'labels') ) {
320 my $sth = $dbh->prepare("INSERT INTO labels (`batch_id`,`itemnumber`) VALUES (?,?)");
321 for my $item (@$batch_list) {
322 $sth->execute($batch_id,$item);
328 #FIXME: Needs to be ported to receive $batch_type
329 # ... this looks eerily like add_batch() ...
330 sub get_highest_batch {
332 my $dbh = C4::Context->dbh;
334 "select distinct batch_id from labels order by batch_id desc limit 1";
335 my $sth = $dbh->prepare($q);
337 my $data = $sth->fetchrow_hashref;
340 if ( !$data->{'batch_id'} ) {
344 $new_batch = $data->{'batch_id'};
352 my ( $batch_type ) = @_;
353 my $dbh = C4::Context->dbh;
354 my $q = "SELECT batch_id, COUNT(*) AS num FROM $batch_type GROUP BY batch_id";
355 my $sth = $dbh->prepare($q);
358 while ( my $data = $sth->fetchrow_hashref ) {
359 push( @resultsloop, $data );
363 # Not sure why we are doing this rather than simply telling the user that no batches are currently defined.
364 # So I'm commenting this out and modifying label-manager.tmpl to properly inform the user as stated. -fbcit
365 # adding a dummy batch=1 value , if none exists in the db
366 # if ( !scalar(@resultsloop) ) {
367 # push( @resultsloop, { batch_id => '1' , num => '0' } );
373 my ($batch_id, $batch_type) = @_;
374 warn "Deleteing batch of type $batch_type";
375 my $dbh = C4::Context->dbh;
376 my $q = "DELETE FROM $batch_type WHERE batch_id = ?";
377 my $sth = $dbh->prepare($q);
378 $sth->execute($batch_id);
382 sub get_barcode_types {
383 my ($layout_id) = @_;
384 my $layout = get_layout($layout_id);
385 my $barcode = $layout->{'barcodetype'};
388 push( @array, { code => 'CODE39', desc => 'Code 39' } );
389 push( @array, { code => 'CODE39MOD', desc => 'Code39 + Modulo43' } );
390 push( @array, { code => 'CODE39MOD10', desc => 'Code39 + Modulo10' } );
391 push( @array, { code => 'ITF', desc => 'Interleaved 2 of 5' } );
393 foreach my $line (@array) {
394 if ( $line->{'code'} eq $barcode ) {
395 $line->{'active'} = 1;
406 $unitvalue = '1' if ( $units eq 'POINT' );
407 $unitvalue = '2.83464567' if ( $units eq 'MM' );
408 $unitvalue = '28.3464567' if ( $units eq 'CM' );
409 $unitvalue = 72 if ( $units eq 'INCH' );
413 sub GetTextWrapCols {
414 my ( $font, $fontsize, $label_width, $left_text_margin ) = @_;
418 # my $textlimit = $label_width - ($left_text_margin);
419 my $textlimit = $label_width - ( 2* $left_text_margin);
421 while ( $strwidth < $textlimit ) {
422 $strwidth = prStrWidth( $string, $font, $fontsize );
423 $string = $string . '0';
424 #warn "strwidth:$strwidth, textlimit:$textlimit, count:$count string:$string";
430 sub GetActiveLabelTemplate {
431 my $dbh = C4::Context->dbh;
432 my $query = " SELECT * FROM labels_templates where active = 1 limit 1";
433 my $sth = $dbh->prepare($query);
435 my $active_tmpl = $sth->fetchrow_hashref;
440 sub GetSingleLabelTemplate {
442 my $dbh = C4::Context->dbh;
443 my $query = " SELECT * FROM labels_templates where tmpl_id = ?";
444 my $sth = $dbh->prepare($query);
445 $sth->execute($tmpl_id);
446 my $template = $sth->fetchrow_hashref;
451 sub SetActiveTemplate {
455 my $dbh = C4::Context->dbh;
456 my $query = " UPDATE labels_templates SET active = NULL";
457 my $sth = $dbh->prepare($query);
460 $query = "UPDATE labels_templates SET active = 1 WHERE tmpl_id = ?";
461 $sth = $dbh->prepare($query);
462 $sth->execute($tmpl_id);
466 sub set_active_layout {
468 my ($layout_id) = @_;
469 my $dbh = C4::Context->dbh;
470 my $query = " UPDATE labels_conf SET active = NULL";
471 my $sth = $dbh->prepare($query);
474 $query = "UPDATE labels_conf SET active = 1 WHERE id = ?";
475 $sth = $dbh->prepare($query);
476 $sth->execute($layout_id);
482 my $dbh = C4::Context->dbh;
483 my $query = " DELETE FROM labels_templates where tmpl_id = ?";
484 my $sth = $dbh->prepare($query);
485 $sth->execute($tmpl_id);
491 $tmpl_id, $tmpl_code, $tmpl_desc, $page_width,
492 $page_height, $label_width, $label_height, $topmargin,
493 $leftmargin, $cols, $rows, $colgap,
494 $rowgap, $font, $fontsize, $units
496 warn "Passed \$font:$font";
497 my $dbh = C4::Context->dbh;
499 " UPDATE labels_templates SET tmpl_code=?, tmpl_desc=?, page_width=?,
500 page_height=?, label_width=?, label_height=?, topmargin=?,
501 leftmargin=?, cols=?, rows=?, colgap=?, rowgap=?, font=?, fontsize=?,
505 my $sth = $dbh->prepare($query);
507 $tmpl_code, $tmpl_desc, $page_width, $page_height,
508 $label_width, $label_height, $topmargin, $leftmargin,
509 $cols, $rows, $colgap, $rowgap,
510 $font, $fontsize, $units, $tmpl_id
512 my $dberror = $sth->errstr;
520 $tmpl_code, $tmpl_desc, $page_width, $page_height,
521 $label_width, $label_height, $topmargin, $leftmargin,
522 $cols, $rows, $colgap, $rowgap,
523 $font, $fontsize, $units
526 my $dbh = C4::Context->dbh;
528 my $query = "INSERT INTO labels_templates (tmpl_code, tmpl_desc, page_width,
529 page_height, label_width, label_height, topmargin,
530 leftmargin, cols, rows, colgap, rowgap, font, fontsize, units)
531 VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
533 my $sth = $dbh->prepare($query);
535 $tmpl_code, $tmpl_desc, $page_width, $page_height,
536 $label_width, $label_height, $topmargin, $leftmargin,
537 $cols, $rows, $colgap, $rowgap,
538 $font, $fontsize, $units
540 my $dberror = $sth->errstr;
545 sub GetAllLabelTemplates {
546 my $dbh = C4::Context->dbh;
548 # get the actual items to be printed.
550 my $query = " Select * from labels_templates ";
551 my $sth = $dbh->prepare($query);
554 while ( my $data = $sth->fetchrow_hashref ) {
555 push( @resultsloop, $data );
559 #warn Dumper @resultsloop;
567 $barcodetype, $title, $subtitle, $isbn, $issn,
568 $itemtype, $bcn, $dcn, $classif,
569 $subclass, $itemcallnumber, $author, $tmpl_id,
570 $printingtype, $guidebox, $startlabel, $layoutname
573 my $dbh = C4::Context->dbh;
574 my $query2 = "update labels_conf set active = NULL";
575 my $sth2 = $dbh->prepare($query2);
577 $query2 = "INSERT INTO labels_conf
578 ( barcodetype, title, subtitle, isbn,issn, itemtype, barcode,
579 dewey, class, subclass, itemcallnumber, author, printingtype,
580 guidebox, startlabel, layoutname, active )
581 values ( ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?,?,?, 1 )";
582 $sth2 = $dbh->prepare($query2);
584 $barcodetype, $title, $subtitle, $isbn, $issn,
586 $itemtype, $bcn, $dcn, $classif,
587 $subclass, $itemcallnumber, $author, $printingtype,
588 $guidebox, $startlabel, $layoutname
592 SetActiveTemplate($tmpl_id);
599 $barcodetype, $title, $subtitle, $isbn, $issn,
600 $itemtype, $bcn, $dcn, $classif,
601 $subclass, $itemcallnumber, $author, $tmpl_id,
602 $printingtype, $guidebox, $startlabel, $layoutname,
608 my $dbh = C4::Context->dbh;
609 my $query2 = "update labels_conf set
610 barcodetype=?, title=?, subtitle=?, isbn=?,issn=?,
611 itemtype=?, barcode=?, dewey=?, class=?,
612 subclass=?, itemcallnumber=?, author=?, printingtype=?,
613 guidebox=?, startlabel=?, layoutname=? where id = ?";
614 my $sth2 = $dbh->prepare($query2);
616 $barcodetype, $title, $subtitle, $isbn, $issn,
617 $itemtype, $bcn, $dcn, $classif,
618 $subclass, $itemcallnumber, $author, $printingtype,
619 $guidebox, $startlabel, $layoutname, $layout_id
626 =item GetAllPrinterProfiles;
628 @profiles = GetAllPrinterProfiles()
630 Returns an array of references-to-hash, whos keys are .....
634 sub GetAllPrinterProfiles {
636 my $dbh = C4::Context->dbh;
638 my $query = "SELECT * FROM printers_profile AS pp INNER JOIN labels_templates AS lt ON pp.tmpl_id = lt.tmpl_id; ";
639 my $sth = $dbh->prepare($query);
642 while ( my $data = $sth->fetchrow_hashref ) {
643 push( @resultsloop, $data );
650 =item GetSinglePrinterProfile;
652 $profile = GetSinglePrinterProfile()
654 Returns a hashref whos keys are...
658 sub GetSinglePrinterProfile {
660 my $dbh = C4::Context->dbh;
661 my $query = " SELECT * FROM printers_profile WHERE prof_id = ?; ";
662 my $sth = $dbh->prepare($query);
663 $sth->execute($prof_id);
664 my $template = $sth->fetchrow_hashref;
671 SaveProfile('parameters')
673 When passed a set of parameters, this function updates the given profile with the new parameters.
679 $prof_id, $offset_horz, $offset_vert, $creep_horz, $creep_vert, $units
681 my $dbh = C4::Context->dbh;
683 " UPDATE printers_profile
684 SET offset_horz=?, offset_vert=?, creep_horz=?, creep_vert=?, unit=?
686 my $sth = $dbh->prepare($query);
688 $offset_horz, $offset_vert, $creep_horz, $creep_vert, $units, $prof_id
695 CreateProfile('parameters')
697 When passed a set of parameters, this function creates a new profile containing those parameters
698 and returns any errors.
704 $prof_id, $printername, $paper_bin, $tmpl_id, $offset_horz,
705 $offset_vert, $creep_horz, $creep_vert, $units
707 my $dbh = C4::Context->dbh;
709 " INSERT INTO printers_profile (prof_id, printername, paper_bin, tmpl_id,
710 offset_horz, offset_vert, creep_horz, creep_vert, unit)
711 VALUES(?,?,?,?,?,?,?,?,?) ";
712 my $sth = $dbh->prepare($query);
714 $prof_id, $printername, $paper_bin, $tmpl_id, $offset_horz,
715 $offset_vert, $creep_horz, $creep_vert, $units
717 my $error = $sth->errstr;
724 DeleteProfile(prof_id)
726 When passed a profile id, this function deletes that profile from the database and returns any errors.
732 my $dbh = C4::Context->dbh;
733 my $query = " DELETE FROM printers_profile WHERE prof_id = ?";
734 my $sth = $dbh->prepare($query);
735 $sth->execute($prof_id);
736 my $error = $sth->errstr;
741 =item GetAssociatedProfile;
743 $assoc_prof = GetAssociatedProfile(tmpl_id)
745 When passed a template id, this function returns the parameters from the currently associated printer profile
746 in a hashref where key=fieldname and value=fieldvalue.
750 sub GetAssociatedProfile {
752 my $dbh = C4::Context->dbh;
753 # First we find out the prof_id for the associated profile...
754 my $query = "SELECT * FROM labels_profile WHERE tmpl_id = ?";
755 my $sth = $dbh->prepare($query);
756 $sth->execute($tmpl_id);
757 my $assoc_prof = $sth->fetchrow_hashref;
759 # Then we retrieve that profile and return it to the caller...
760 $assoc_prof = GetSinglePrinterProfile($assoc_prof->{'prof_id'});
764 =item SetAssociatedProfile;
766 SetAssociatedProfile($prof_id, $tmpl_id)
768 When passed both a profile id and template id, this function establishes an association between the two. No more
769 than one profile may be associated with any given template at the same time.
773 sub SetAssociatedProfile {
775 my ($prof_id, $tmpl_id) = @_;
777 my $dbh = C4::Context->dbh;
778 my $query = "INSERT INTO labels_profile (prof_id, tmpl_id) VALUES (?,?) ON DUPLICATE KEY UPDATE prof_id = ?";
779 my $sth = $dbh->prepare($query);
780 $sth->execute($prof_id, $tmpl_id, $prof_id);
786 $options = GetLabelItems()
788 Returns an array of references-to-hash, whos keys are the field from the biblio, biblioitems, items and labels tables in the Koha database.
795 my $dbh = C4::Context->dbh;
797 my @resultsloop = ();
803 my $query3 = "Select * from labels where batch_id = ? order by labelid ";
804 $sth = $dbh->prepare($query3);
805 $sth->execute($batch_id);
810 my $query3 = "Select * from labels";
811 $sth = $dbh->prepare($query3);
814 my $cnt = $sth->rows;
816 while ( my $data = $sth->fetchrow_hashref ) {
818 # lets get some summary info from each item
820 select i.*, bi.*, b.* from items i,biblioitems bi,biblio b
821 where itemnumber=? and i.biblioitemnumber=bi.biblioitemnumber and
822 bi.biblionumber=b.biblionumber";
824 my $sth1 = $dbh->prepare($query1);
825 $sth1->execute( $data->{'itemnumber'} );
827 my $data1 = $sth1->fetchrow_hashref();
828 $data1->{'labelno'} = $i1;
829 $data1->{'labelid'} = $data->{'labelid'};
830 $data1->{'batch_id'} = $batch_id;
831 $data1->{'summary'} =
832 "$data1->{'barcode'}, $data1->{'title'}, $data1->{'isbn'}";
834 push( @resultsloop, $data1 );
846 barcode title subtitle
847 dewey isbn issn author class
848 itemtype subclass itemcallnumber
854 sub GetPatronCardItems {
856 my ( $batch_id ) = @_;
859 my $dbh = C4::Context->dbh;
860 # my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY borrowernumber";
861 my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY cardid";
862 my $sth = $dbh->prepare($query);
863 $sth->execute($batch_id);
865 while ( my $data = $sth->fetchrow_hashref ) {
866 my $patron_data = GetMember( $data->{'borrowernumber'} );
867 $patron_data->{'branchname'} = GetBranchName( $patron_data->{'branchcode'} );
868 $patron_data->{'cardno'} = $cardno;
869 $patron_data->{'cardid'} = $data->{'cardid'};
870 $patron_data->{'batch_id'} = $batch_id;
871 push( @resultsloop, $patron_data );
879 sub deduplicate_batch {
880 my ( $batch_id, $batch_type ) = @_;
883 batch_id," . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . ",
884 count(". (($batch_type eq 'labels') ? 'labelid' : 'cardid') . ") as count
887 GROUP BY " . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . ",batch_id
891 my $sth = C4::Context->dbh->prepare($query);
892 $sth->execute($batch_id);
893 warn $sth->errstr if $sth->errstr;
894 $sth->rows or return undef, $sth->errstr;
900 AND " . (($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber') . " = ?
901 ORDER BY timestamp ASC
904 while (my $data = $sth->fetchrow_hashref()) {
905 my $itemnumber = $data->{(($batch_type eq 'labels') ? 'itemnumber' : 'borrowernumber')} or next;
906 my $limit = $data->{count} - 1 or next;
907 my $sth2 = C4::Context->dbh->prepare("$del_query LIMIT $limit");
908 # die sprintf "$del_query LIMIT %s\n (%s, %s)", $limit, $batch_id, $itemnumber;
909 # $sth2->execute($batch_id, C4::Context->dbh->quote($data->{itemnumber}), $data->{count} - 1)
910 $sth2->execute($batch_id, $itemnumber) and
911 $killed += ($data->{count} - 1);
912 warn $sth2->errstr if $sth2->errstr;
914 return $killed, undef;
919 my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
920 $text_wrap_cols, $item, $conf_data, $printingtype, $nowrap )
922 # hack to fix column name mismatch betwen labels_conf.class, and bibitems.classification
923 $$item->{'class'} = $$item->{'classification'};
925 $Text::Wrap::columns = $text_wrap_cols;
926 $Text::Wrap::separator = "\n";
930 my $top_text_margin = ( $fontsize + 3 ); #FIXME: This should be a template parameter and passed in...
931 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.).
933 my $layout_id = $$conf_data->{'id'};
935 my $vPos = ( $y_pos + ( $label_height - $top_text_margin ) );
936 my $font = prFont($fontname);
938 my @str_fields = get_text_fields($layout_id, 'codes' );
940 foreach my $field (@str_fields) {
941 push (@fields, $field->{'code'});
944 foreach my $field (@fields) {
947 # $$item->{"$field"} = $field . ": " . $$item->{"$field"};
949 # if the display option for this field is selected in the DB,
950 # and the item record has some values for this field, display it.
951 if ( $$conf_data->{"$field"} && $$item->{"$field"} ) {
953 # warn "CONF_TYPE = $field";
956 $str = $$item->{"$field"};
957 # strip out naughty existing nl/cr's
961 if (($nowrap == 0) || (!$nowrap)) {
962 # wrap lines based on segmentation markers: '/' (other types of segmentation markers can be added as needed here or this could be added as a syspref.)
964 while ( $str =~ /\// ) {
965 $str =~ /^(.*)\/(.*)$/;
968 unshift @strings, $2;
971 unshift @strings, $str;
973 push @strings, $str; # if we are not wrapping the call number just send it along as we found it...
976 # strip out division slashes
978 #warn "\$str after striping division marks: $str";
979 # chop the string up into _upto_ 12 chunks
980 # and seperate the chunks with newlines
982 #$str = wrap( "", "", "$str" );
983 #$str = wrap( "", "", "$str" );
985 # split the chunks between newline's, into an array
986 #my @strings = split /\n/, $str;
988 # then loop for each string line
989 foreach my $str (@strings) {
991 if ( $printingtype eq 'BIB' ) { #FIXME: This is a hack and needs to be implimented as a text justification option in the template...
992 # some code to try and center each line on the label based on font size and string point width...
993 my $stringwidth = prStrWidth($str, $fontname, $fontsize);
994 my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
995 $hPos = ( ( $whitespace / 2 ) + $x_pos + $left_text_margin );
996 #warn "\$label_width=$label_width \$stringwidth=$stringwidth \$whitespace=$whitespace \$left_text_margin=$left_text_margin for $str\n";
998 $hPos = ( $x_pos + $left_text_margin );
1000 PrintText( $hPos, $vPos, $font, $fontsize, $str );
1001 $vPos = $vPos - $line_spacer;
1009 my ( $hPos, $vPos, $font, $fontsize, $text ) = @_;
1010 my $str = "BT /$font $fontsize Tf $hPos $vPos Td ($text) Tj ET";
1015 sub DrawPatronCardText {
1017 my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
1018 $text_wrap_cols, $text, $printingtype )
1021 my $top_text_margin = 25; #FIXME: This should be a template parameter and passed in...
1023 my $vPos = ( $y_pos + ( $label_height - $top_text_margin ) );
1024 my $font = prFont($fontname);
1028 foreach my $line (keys %$text) {
1029 warn "Current text is \"$line\" and font size for \"$line\" is $text->{$line} points";
1030 # some code to try and center each line on the label based on font size and string point width...
1031 my $stringwidth = prStrWidth($line, $fontname, $text->{$line});
1032 my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
1033 $hPos = ( ( $whitespace / 2 ) + $x_pos + $left_text_margin );
1035 PrintText( $hPos, $vPos, $font, $text->{$line}, $line );
1036 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.).
1037 $vPos = $vPos - ($line_spacer + $text->{$line}); # Linefeed equiv: leading + font size
1041 # Not used anywhere.
1045 # my ($fontsize) = @_;
1047 # my $str = "BT/F13 30 Tf288 720 Td( AAAAAAAAAA ) TjET";
1053 # x and y are from the top-left :)
1054 my ( $x_pos, $y_pos, $height, $width, $barcode, $barcodetype ) = @_;
1055 my $num_of_bars = length($barcode);
1056 my $bar_width = $width * .8; # %80 of length of label width
1059 my $guard_length = 10;
1062 if ( $barcodetype eq 'CODE39' ) {
1063 $bar_length = '17.5';
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,
1078 warn "$barcodetype, $barcode FAILED:$@";
1082 elsif ( $barcodetype eq 'CODE39MOD' ) {
1084 # get modulo43 checksum
1085 my $c39 = CheckDigits('code_39');
1086 $barcode = $c39->complete($barcode);
1090 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1091 $xsize_ratio = ( $bar_width / $tot_bar_length );
1093 PDF::Reuse::Barcode::Code39(
1094 x => ( $x_pos + ( $width / 10 ) ),
1095 y => ( $y_pos + ( $height / 10 ) ),
1096 value => "*$barcode*",
1097 ySize => ( .02 * $height ),
1098 xSize => $xsize_ratio,
1104 warn "$barcodetype, $barcode FAILED:$@";
1107 elsif ( $barcodetype eq 'CODE39MOD10' ) {
1109 # get modulo43 checksum
1110 my $c39_10 = CheckDigits('visa');
1111 $barcode = $c39_10->complete($barcode);
1115 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1116 $xsize_ratio = ( $bar_width / $tot_bar_length );
1118 PDF::Reuse::Barcode::Code39(
1119 x => ( $x_pos + ( $width / 10 ) ),
1120 y => ( $y_pos + ( $height / 10 ) ),
1121 value => "*$barcode*",
1122 ySize => ( .02 * $height ),
1123 xSize => $xsize_ratio,
1130 warn "$barcodetype, $barcode FAILED:$@";
1135 elsif ( $barcodetype eq 'COOP2OF5' ) {
1136 $bar_length = '9.43333333333333';
1138 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1139 $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1141 PDF::Reuse::Barcode::COOP2of5(
1142 x => ( $x_pos + ( $width / 10 ) ),
1143 y => ( $y_pos + ( $height / 10 ) ),
1145 ySize => ( .02 * $height ),
1146 xSize => $xsize_ratio,
1150 warn "$barcodetype, $barcode FAILED:$@";
1154 elsif ( $barcodetype eq 'INDUSTRIAL2OF5' ) {
1155 $bar_length = '13.1333333333333';
1157 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1158 $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1160 PDF::Reuse::Barcode::Industrial2of5(
1161 x => ( $x_pos + ( $width / 10 ) ),
1162 y => ( $y_pos + ( $height / 10 ) ),
1164 ySize => ( .02 * $height ),
1165 xSize => $xsize_ratio,
1169 warn "$barcodetype, $barcode FAILED:$@";
1173 my $moo2 = $tot_bar_length * $xsize_ratio;
1175 warn "$x_pos, $y_pos, $barcode, $barcodetype" if $DEBUG;
1176 warn "BAR_WDTH = $bar_width, TOT.BAR.LGHT=$tot_bar_length R*TOT.BAR =$moo2" if $DEBUG;
1179 =item build_circ_barcode;
1181 build_circ_barcode( $x_pos, $y_pos, $barcode,
1182 $barcodetype, \$item);
1184 $item is the result of a previous call to GetLabelItems();
1189 sub build_circ_barcode {
1190 my ( $x_pos_circ, $y_pos, $value, $barcodetype, $item ) = @_;
1192 #warn Dumper \$item;
1194 #warn "value = $value\n";
1198 if ( $barcodetype eq 'EAN13' ) {
1200 #testing EAN13 barcodes hack
1201 $value = $value . '000000000';
1203 $value = substr( $value, 0, 12 );
1207 PDF::Reuse::Barcode::EAN13(
1208 x => ( $x_pos_circ + 27 ),
1209 y => ( $y_pos + 15 ),
1217 # added for xpdf compat. doesnt use type3 fonts., but increases filesize from 20k to 200k
1218 # i think its embedding extra fonts in the pdf file.
1219 # mode => 'graphic',
1223 $item->{'barcodeerror'} = 1;
1225 #warn "EAN13BARCODE FAILED:$@";
1231 elsif ( $barcodetype eq 'Code39' ) {
1234 PDF::Reuse::Barcode::Code39(
1235 x => ( $x_pos_circ + 9 ),
1236 y => ( $y_pos + 15 ),
1246 $item->{'barcodeerror'} = 1;
1248 #warn "CODE39BARCODE $value FAILED:$@";
1255 elsif ( $barcodetype eq 'Matrix2of5' ) {
1257 #warn "MATRIX ELSE:";
1259 #testing MATRIX25 barcodes hack
1260 # $value = $value.'000000000';
1263 # $value = substr( $value, 0, 12 );
1267 PDF::Reuse::Barcode::Matrix2of5(
1268 x => ( $x_pos_circ + 27 ),
1269 y => ( $y_pos + 15 ),
1279 $item->{'barcodeerror'} = 1;
1281 #warn "BARCODE FAILED:$@";
1288 elsif ( $barcodetype eq 'EAN8' ) {
1290 #testing ean8 barcodes hack
1291 $value = $value . '000000000';
1293 $value = substr( $value, 0, 8 );
1297 #warn "EAN8 ELSEIF";
1299 PDF::Reuse::Barcode::EAN8(
1300 x => ( $x_pos_circ + 42 ),
1301 y => ( $y_pos + 15 ),
1311 $item->{'barcodeerror'} = 1;
1313 #warn "BARCODE FAILED:$@";
1320 elsif ( $barcodetype eq 'UPC-E' ) {
1322 PDF::Reuse::Barcode::UPCE(
1323 x => ( $x_pos_circ + 27 ),
1324 y => ( $y_pos + 15 ),
1334 $item->{'barcodeerror'} = 1;
1336 #warn "BARCODE FAILED:$@";
1342 elsif ( $barcodetype eq 'NW7' ) {
1344 PDF::Reuse::Barcode::NW7(
1345 x => ( $x_pos_circ + 27 ),
1346 y => ( $y_pos + 15 ),
1356 $item->{'barcodeerror'} = 1;
1358 #warn "BARCODE FAILED:$@";
1364 elsif ( $barcodetype eq 'ITF' ) {
1366 PDF::Reuse::Barcode::ITF(
1367 x => ( $x_pos_circ + 27 ),
1368 y => ( $y_pos + 15 ),
1378 $item->{'barcodeerror'} = 1;
1380 #warn "BARCODE FAILED:$@";
1386 elsif ( $barcodetype eq 'Industrial2of5' ) {
1388 PDF::Reuse::Barcode::Industrial2of5(
1389 x => ( $x_pos_circ + 27 ),
1390 y => ( $y_pos + 15 ),
1399 $item->{'barcodeerror'} = 1;
1401 #warn "BARCODE FAILED:$@";
1407 elsif ( $barcodetype eq 'IATA2of5' ) {
1409 PDF::Reuse::Barcode::IATA2of5(
1410 x => ( $x_pos_circ + 27 ),
1411 y => ( $y_pos + 15 ),
1420 $item->{'barcodeerror'} = 1;
1422 #warn "BARCODE FAILED:$@";
1429 elsif ( $barcodetype eq 'COOP2of5' ) {
1431 PDF::Reuse::Barcode::COOP2of5(
1432 x => ( $x_pos_circ + 27 ),
1433 y => ( $y_pos + 15 ),
1442 $item->{'barcodeerror'} = 1;
1444 #warn "BARCODE FAILED:$@";
1450 elsif ( $barcodetype eq 'UPC-A' ) {
1453 PDF::Reuse::Barcode::UPCA(
1454 x => ( $x_pos_circ + 27 ),
1455 y => ( $y_pos + 15 ),
1464 $item->{'barcodeerror'} = 1;
1466 #warn "BARCODE FAILED:$@";
1475 =item draw_boundaries
1477 sub draw_boundaries ($x_pos_spine, $x_pos_circ1, $x_pos_circ2,
1478 $y_pos, $spine_width, $label_height, $circ_width)
1480 This sub draws boundary lines where the label outlines are, to aid in printer testing, and debugging.
1485 sub draw_boundaries {
1488 $x_pos_spine, $x_pos_circ1, $x_pos_circ2, $y_pos,
1489 $spine_width, $label_height, $circ_width
1492 my $y_pos_initial = ( ( 792 - 36 ) - 90 );
1493 $y_pos = $y_pos_initial; # FIXME - why are we ignoring the y_pos parameter by redefining it?
1496 for ( $i = 1 ; $i <= 8 ; $i++ ) {
1498 &drawbox( $x_pos_spine, $y_pos, ($spine_width), ($label_height) );
1500 #warn "OLD BOXES x=$x_pos_spine, y=$y_pos, w=$spine_width, h=$label_height";
1501 &drawbox( $x_pos_circ1, $y_pos, ($circ_width), ($label_height) );
1502 &drawbox( $x_pos_circ2, $y_pos, ($circ_width), ($label_height) );
1504 $y_pos = ( $y_pos - $label_height );
1511 sub drawbox { $lower_left_x, $lower_left_y,
1512 $upper_right_x, $upper_right_y )
1514 this is a low level sub, that draws a pdf box, it is called by draw_boxes
1516 FYI: the $upper_right_x and $upper_right_y values are RELATIVE to $lower_left_x and $lower_left_y
1518 and $lower_left_x, $lower_left_y are ABSOLUTE, this caught me out!
1524 my ( $llx, $lly, $urx, $ury ) = @_;
1526 # warn "llx,y= $llx,$lly , urx,y=$urx,$ury \n";
1528 my $str = "q\n"; # save the graphic state
1529 $str .= "0.5 w\n"; # border color red
1530 $str .= "1.0 0.0 0.0 RG\n"; # border color red
1531 # $str .= "0.5 0.75 1.0 rg\n"; # fill color blue
1532 $str .= "1.0 1.0 1.0 rg\n"; # fill color white
1534 $str .= "$llx $lly $urx $ury re\n"; # a rectangle
1535 $str .= "B\n"; # fill (and a little more)
1536 $str .= "Q\n"; # save the graphic state
1542 END { } # module clean-up code here (global destructor)
1551 Mason James <mason@katipo.co.nz>