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;
32 # use Smart::Comments;
39 &get_label_options &GetLabelItems
40 &build_circ_barcode &draw_boundaries
41 &drawbox &GetActiveLabelTemplate
42 &GetAllLabelTemplates &DeleteTemplate
43 &GetSingleLabelTemplate &SaveTemplate
44 &CreateTemplate &SetActiveTemplate
45 &SaveConf &DrawSpineText &GetTextWrapCols
46 &GetUnitsValue &DrawBarcode &DrawPatronCardText
47 &get_printingtypes &GetPatronCardItems
50 &get_batches &delete_batch
54 get_layout &save_layout &add_layout
57 &delete_layout &get_active_layout
60 &GetAllPrinterProfiles &GetSinglePrinterProfile
61 &SaveProfile &CreateProfile &DeleteProfile
62 &GetAssociatedProfile &SetAssociatedProfile
69 C4::Labels - Functions for printing spine labels and barcodes in Koha
75 =item get_label_options;
77 $options = get_label_options()
79 Return a pointer on a hash list containing info from labels_conf table in Koha DB.
84 sub get_label_options
{
85 my $query2 = " SELECT * FROM labels_conf where active = 1"; # FIXME: exact same as get_active_layout
86 my $sth = C4
::Context
->dbh->prepare($query2);
88 return $sth->fetchrow_hashref;
93 ## FIXME: this if/else could be compacted...
94 my $dbh = C4
::Context
->dbh;
96 my $query = " Select * from labels_conf";
97 my $sth = $dbh->prepare($query);
100 while ( my $data = $sth->fetchrow_hashref ) {
102 $data->{'fieldlist'} = get_text_fields
( $data->{'id'} );
103 push( @resultsloop, $data );
113 my ($layout_id) = @_;
114 my $dbh = C4
::Context
->dbh;
116 # get the actual items to be printed.
117 my $query = " Select * from labels_conf where id = ?";
118 my $sth = $dbh->prepare($query);
119 $sth->execute($layout_id);
120 my $data = $sth->fetchrow_hashref;
125 sub get_active_layout
{
126 my $query = " Select * from labels_conf where active = 1"; # FIXME: exact same as get_label_options
127 my $sth = C4
::Context
->dbh->prepare($query);
129 return $sth->fetchrow_hashref;
133 my ($layout_id) = @_;
134 my $dbh = C4
::Context
->dbh;
136 # get the actual items to be printed.
137 my $query = "delete from labels_conf where id = ?";
138 my $sth = $dbh->prepare($query);
139 $sth->execute($layout_id);
143 sub get_printingtypes
{
144 my ($layout_id) = @_;
146 # FIXME: hard coded print types
147 push( @printtypes, { code
=> 'BAR', desc
=> "barcode only" } );
148 push( @printtypes, { code
=> 'BIB', desc
=> "biblio only" } );
149 push( @printtypes, { code
=> 'BARBIB', desc
=> "barcode / biblio" } );
150 push( @printtypes, { code
=> 'BIBBAR', desc
=> "biblio / barcode" } );
151 push( @printtypes, { code
=> 'ALT', desc
=> "alternating labels" } );
152 push( @printtypes, { code
=> 'CSV', desc
=> "csv output" } );
153 push( @printtypes, { code
=> 'PATCRD', desc
=> "patron cards" } );
155 my $conf = get_layout
($layout_id);
156 my $active_printtype = $conf->{'printingtype'};
158 # lop thru layout, insert selected to hash
160 foreach my $printtype (@printtypes) {
161 if ( $printtype->{'code'} eq $active_printtype ) {
162 $printtype->{'active'} = 1;
168 # this sub (build_text_dropbox) is deprecated and should be deleted.
171 sub build_text_dropbox
{
174 # my @fields = get_text_fields();
175 # my $field_count = scalar @fields;
176 my $field_count = 10; # <----------- FIXME hard coded
180 ?
push( @lines, { num
=> '', selected
=> '1' } )
181 : push( @lines, { num
=> '' } );
182 for ( my $i = 1 ; $i <= $field_count ; $i++ ) {
183 my $line = { num
=> "$i" };
184 $line->{'selected'} = 1 if $i eq $order;
185 push( @lines, $line );
188 # add a blank row too
193 sub get_text_fields
{
194 my ($layout_id, $sorttype) = @_;
197 my $sortorder = get_layout
($layout_id);
198 if( $sortorder->{formatstring
}) {
200 return $sortorder->{formatstring
} ;
202 my $csv = Text
::CSV_XS
->new( { allow_whitespace
=> 1 } ) ;
203 my $line= $sortorder->{formatstring
} ;
204 my $status = $csv->parse( $line );
205 @sorted_fields = map {{ 'code' => $_ , desc
=> $_ } } $csv->fields() ;
206 $error = $csv->error_input();
207 warn $error if $error ; # TODO - do more with this.
210 # These fields are hardcoded based on the template for label-edit-layout.pl
215 order
=> $sortorder->{'itemtype'}
220 order
=> $sortorder->{'dewey'}
225 order
=> $sortorder->{'issn'}
230 order
=> $sortorder->{'isbn'}
234 desc
=> "Classification",
235 order
=> $sortorder->{'class'}
240 order
=> $sortorder->{'subclass'}
245 order
=> $sortorder->{'barcode'}
250 order
=> $sortorder->{'author'}
255 order
=> $sortorder->{'title'}
258 code
=> 'itemcallnumber',
259 desc
=> "Call Number",
260 order
=> $sortorder->{'itemcallnumber'}
265 order
=> $sortorder->{'subtitle'}
271 foreach my $field (@text_fields) {
272 push( @new_fields, $field ) if $field->{'order'} > 0;
275 @sorted_fields = sort { $$a{order
} <=> $$b{order
} } @new_fields;
277 # if we have a 'formatstring', then we ignore these hardcoded fields.
280 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
281 return @sorted_fields;
283 foreach my $field (@sorted_fields) {
284 $active_fields .= "$field->{'desc'} ";
286 return $active_fields;
293 add_batch($batch_type,\@batch_list);
294 if $batch_list is supplied,
295 create a new batch with those items.
296 else, return the next available batch_id.
299 sub add_batch
($;$) {
300 my $table = (@_ and 'patroncards' eq shift) ?
'patroncards' : 'labels';
301 my $batch_list = (@_) ?
shift : undef;
302 my $dbh = C4
::Context
->dbh;
303 my $q ="SELECT MAX(DISTINCT batch_id) FROM $table";
304 my $sth = $dbh->prepare($q);
306 my ($batch_id) = $sth->fetchrow_array || 0;
309 if ($table eq 'patroncards') {
310 $sth = $dbh->prepare("INSERT INTO $table (`batch_id`,`borrowernumber`) VALUES (?,?)");
312 $sth = $dbh->prepare("INSERT INTO $table (`batch_id`,`itemnumber` ) VALUES (?,?)");
315 $sth->execute($batch_id,$_);
321 #FIXME: Needs to be ported to receive $batch_type
322 # ... this looks eerily like add_batch() ...
323 sub get_highest_batch
{
324 my $table = (@_ and 'patroncards' eq shift) ?
'patroncards' : 'labels';
326 "select distinct batch_id from $table order by batch_id desc limit 1";
327 my $sth = C4
::Context
->dbh->prepare($q);
329 my $data = $sth->fetchrow_hashref or return 1;
330 return ($data->{'batch_id'} || 1);
334 sub get_batches
(;$) {
335 my $table = (@_ and 'patroncards' eq shift) ?
'patroncards' : 'labels';
336 my $q = "SELECT batch_id, COUNT(*) AS num FROM $table GROUP BY batch_id";
337 my $sth = C4
::Context
->dbh->prepare($q);
339 my $batches = $sth->fetchall_arrayref({});
344 my ($batch_id, $batch_type) = @_;
345 warn "Deleteing batch of type $batch_type";
346 my $dbh = C4
::Context
->dbh;
347 my $q = "DELETE FROM $batch_type WHERE batch_id = ?";
348 my $sth = $dbh->prepare($q);
349 $sth->execute($batch_id);
353 sub get_barcode_types
{
354 my ($layout_id) = @_;
355 my $layout = get_layout
($layout_id);
356 my $barcode = $layout->{'barcodetype'};
359 push( @array, { code
=> 'CODE39', desc
=> 'Code 39' } );
360 push( @array, { code
=> 'CODE39MOD', desc
=> 'Code39 + Modulo43' } );
361 push( @array, { code
=> 'CODE39MOD10', desc
=> 'Code39 + Modulo10' } );
362 push( @array, { code
=> 'ITF', desc
=> 'Interleaved 2 of 5' } );
364 foreach my $line (@array) {
365 if ( $line->{'code'} eq $barcode ) {
366 $line->{'active'} = 1;
377 $unitvalue = '1' if ( $units eq 'POINT' );
378 $unitvalue = '2.83464567' if ( $units eq 'MM' );
379 $unitvalue = '28.3464567' if ( $units eq 'CM' );
380 $unitvalue = 72 if ( $units eq 'INCH' );
384 sub GetTextWrapCols
{
385 my ( $font, $fontsize, $label_width, $left_text_margin ) = @_;
389 # my $textlimit = $label_width - ($left_text_margin);
390 my $textlimit = $label_width - ( 3 * $left_text_margin);
392 while ( $strwidth < $textlimit ) {
393 $strwidth = prStrWidth
( $string, $font, $fontsize );
394 $string = $string . '0';
395 #warn "strwidth:$strwidth, textlimit:$textlimit, count:$count string:$string";
401 sub GetActiveLabelTemplate
{
402 my $dbh = C4
::Context
->dbh;
403 my $query = " SELECT * FROM labels_templates where active = 1 limit 1";
404 my $sth = $dbh->prepare($query);
406 my $active_tmpl = $sth->fetchrow_hashref;
411 sub GetSingleLabelTemplate
{
413 my $dbh = C4
::Context
->dbh;
414 my $query = " SELECT * FROM labels_templates where tmpl_id = ?";
415 my $sth = $dbh->prepare($query);
416 $sth->execute($tmpl_id);
417 my $template = $sth->fetchrow_hashref;
422 sub SetActiveTemplate
{
426 my $dbh = C4
::Context
->dbh;
427 my $query = " UPDATE labels_templates SET active = NULL";
428 my $sth = $dbh->prepare($query);
431 $query = "UPDATE labels_templates SET active = 1 WHERE tmpl_id = ?";
432 $sth = $dbh->prepare($query);
433 $sth->execute($tmpl_id);
437 sub set_active_layout
{
439 my ($layout_id) = @_;
440 my $dbh = C4
::Context
->dbh;
441 my $query = " UPDATE labels_conf SET active = NULL";
442 my $sth = $dbh->prepare($query);
445 $query = "UPDATE labels_conf SET active = 1 WHERE id = ?";
446 $sth = $dbh->prepare($query);
447 $sth->execute($layout_id);
453 my $dbh = C4
::Context
->dbh;
454 my $query = " DELETE FROM labels_templates where tmpl_id = ?";
455 my $sth = $dbh->prepare($query);
456 $sth->execute($tmpl_id);
462 $tmpl_id, $tmpl_code, $tmpl_desc, $page_width,
463 $page_height, $label_width, $label_height, $topmargin,
464 $leftmargin, $cols, $rows, $colgap,
465 $rowgap, $font, $fontsize, $units
467 $debug and warn "Passed \$font:$font";
468 my $dbh = C4
::Context
->dbh;
470 " UPDATE labels_templates SET tmpl_code=?, tmpl_desc=?, page_width=?,
471 page_height=?, label_width=?, label_height=?, topmargin=?,
472 leftmargin=?, cols=?, rows=?, colgap=?, rowgap=?, font=?, fontsize=?,
476 my $sth = $dbh->prepare($query);
478 $tmpl_code, $tmpl_desc, $page_width, $page_height,
479 $label_width, $label_height, $topmargin, $leftmargin,
480 $cols, $rows, $colgap, $rowgap,
481 $font, $fontsize, $units, $tmpl_id
483 my $dberror = $sth->errstr;
491 $tmpl_code, $tmpl_desc, $page_width, $page_height,
492 $label_width, $label_height, $topmargin, $leftmargin,
493 $cols, $rows, $colgap, $rowgap,
494 $font, $fontsize, $units
497 my $dbh = C4
::Context
->dbh;
499 my $query = "INSERT INTO labels_templates (tmpl_code, tmpl_desc, page_width,
500 page_height, label_width, label_height, topmargin,
501 leftmargin, cols, rows, colgap, rowgap, font, fontsize, units)
502 VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
504 my $sth = $dbh->prepare($query);
506 $tmpl_code, $tmpl_desc, $page_width, $page_height,
507 $label_width, $label_height, $topmargin, $leftmargin,
508 $cols, $rows, $colgap, $rowgap,
509 $font, $fontsize, $units
511 my $dberror = $sth->errstr;
516 sub GetAllLabelTemplates
{
517 my $dbh = C4
::Context
->dbh;
519 # get the actual items to be printed.
521 my $query = " Select * from labels_templates ";
522 my $sth = $dbh->prepare($query);
525 while ( my $data = $sth->fetchrow_hashref ) {
526 push( @resultsloop, $data );
530 #warn Dumper @resultsloop;
538 $barcodetype, $title, $subtitle, $isbn, $issn,
539 $itemtype, $bcn, $dcn, $classif,
540 $subclass, $itemcallnumber, $author, $tmpl_id,
541 $printingtype, $guidebox, $startlabel, $layoutname, $formatstring
544 my $dbh = C4
::Context
->dbh;
545 my $query2 = "update labels_conf set active = NULL";
546 my $sth2 = $dbh->prepare($query2);
548 $query2 = "INSERT INTO labels_conf
549 ( barcodetype, title, subtitle, isbn,issn, itemtype, barcode,
550 dewey, classification, subclass, itemcallnumber, author, printingtype,
551 guidebox, startlabel, layoutname, formatstring, active )
552 values ( ?, ?,?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?,?,?, 1 )";
553 $sth2 = $dbh->prepare($query2);
555 $barcodetype, $title, $subtitle, $isbn, $issn,
557 $itemtype, $bcn, $dcn, $classif,
558 $subclass, $itemcallnumber, $author, $printingtype,
559 $guidebox, $startlabel, $layoutname, $formatstring
563 SetActiveTemplate
($tmpl_id);
570 $barcodetype, $title, $subtitle, $isbn, $issn,
571 $itemtype, $bcn, $dcn, $classif,
572 $subclass, $itemcallnumber, $author, $tmpl_id,
573 $printingtype, $guidebox, $startlabel, $layoutname, $formatstring,
579 my $dbh = C4
::Context
->dbh;
580 my $query2 = "update labels_conf set
581 barcodetype=?, title=?, subtitle=?, isbn=?,issn=?,
582 itemtype=?, barcode=?, dewey=?, classification=?,
583 subclass=?, itemcallnumber=?, author=?, printingtype=?,
584 guidebox=?, startlabel=?, layoutname=?, formatstring=? where id = ?";
585 my $sth2 = $dbh->prepare($query2);
587 $barcodetype, $title, $subtitle, $isbn, $issn,
588 $itemtype, $bcn, $dcn, $classif,
589 $subclass, $itemcallnumber, $author, $printingtype,
590 $guidebox, $startlabel, $layoutname, $formatstring, $layout_id
597 =item GetAllPrinterProfiles;
599 @profiles = GetAllPrinterProfiles()
601 Returns an array of references-to-hash, whos keys are .....
605 sub GetAllPrinterProfiles
{
607 my $dbh = C4
::Context
->dbh;
609 my $query = "SELECT * FROM printers_profile AS pp INNER JOIN labels_templates AS lt ON pp.tmpl_id = lt.tmpl_id; ";
610 my $sth = $dbh->prepare($query);
613 while ( my $data = $sth->fetchrow_hashref ) {
614 push( @resultsloop, $data );
621 =item GetSinglePrinterProfile;
623 $profile = GetSinglePrinterProfile()
625 Returns a hashref whos keys are...
629 sub GetSinglePrinterProfile
{
631 my $dbh = C4
::Context
->dbh;
632 my $query = " SELECT * FROM printers_profile WHERE prof_id = ?; ";
633 my $sth = $dbh->prepare($query);
634 $sth->execute($prof_id);
635 my $template = $sth->fetchrow_hashref;
642 SaveProfile('parameters')
644 When passed a set of parameters, this function updates the given profile with the new parameters.
650 $prof_id, $offset_horz, $offset_vert, $creep_horz, $creep_vert, $units
652 my $dbh = C4
::Context
->dbh;
654 " UPDATE printers_profile
655 SET offset_horz=?, offset_vert=?, creep_horz=?, creep_vert=?, unit=?
657 my $sth = $dbh->prepare($query);
659 $offset_horz, $offset_vert, $creep_horz, $creep_vert, $units, $prof_id
666 CreateProfile('parameters')
668 When passed a set of parameters, this function creates a new profile containing those parameters
669 and returns any errors.
675 $prof_id, $printername, $paper_bin, $tmpl_id, $offset_horz,
676 $offset_vert, $creep_horz, $creep_vert, $units
678 my $dbh = C4
::Context
->dbh;
680 " INSERT INTO printers_profile (prof_id, printername, paper_bin, tmpl_id,
681 offset_horz, offset_vert, creep_horz, creep_vert, unit)
682 VALUES(?,?,?,?,?,?,?,?,?) ";
683 my $sth = $dbh->prepare($query);
685 $prof_id, $printername, $paper_bin, $tmpl_id, $offset_horz,
686 $offset_vert, $creep_horz, $creep_vert, $units
688 my $error = $sth->errstr;
695 DeleteProfile(prof_id)
697 When passed a profile id, this function deletes that profile from the database and returns any errors.
703 my $dbh = C4
::Context
->dbh;
704 my $query = " DELETE FROM printers_profile WHERE prof_id = ?";
705 my $sth = $dbh->prepare($query);
706 $sth->execute($prof_id);
707 my $error = $sth->errstr;
712 =item GetAssociatedProfile;
714 $assoc_prof = GetAssociatedProfile(tmpl_id)
716 When passed a template id, this function returns the parameters from the currently associated printer profile
717 in a hashref where key=fieldname and value=fieldvalue.
721 sub GetAssociatedProfile
{
723 my $dbh = C4
::Context
->dbh;
724 # First we find out the prof_id for the associated profile...
725 my $query = "SELECT * FROM labels_profile WHERE tmpl_id = ?";
726 my $sth = $dbh->prepare($query);
727 $sth->execute($tmpl_id);
728 my $assoc_prof = $sth->fetchrow_hashref;
730 # Then we retrieve that profile and return it to the caller...
731 $assoc_prof = GetSinglePrinterProfile
($assoc_prof->{'prof_id'});
735 =item SetAssociatedProfile;
737 SetAssociatedProfile($prof_id, $tmpl_id)
739 When passed both a profile id and template id, this function establishes an association between the two. No more
740 than one profile may be associated with any given template at the same time.
744 sub SetAssociatedProfile
{
746 my ($prof_id, $tmpl_id) = @_;
748 my $dbh = C4
::Context
->dbh;
749 my $query = "INSERT INTO labels_profile (prof_id, tmpl_id) VALUES (?,?) ON DUPLICATE KEY UPDATE prof_id = ?";
750 my $sth = $dbh->prepare($query);
751 $sth->execute($prof_id, $tmpl_id, $prof_id);
757 $options = GetLabelItems()
759 Returns an array of references-to-hash, whos keys are the field from the biblio, biblioitems, items and labels tables in the Koha database.
766 my $dbh = C4
::Context
->dbh;
768 my @resultsloop = ();
774 my $query3 = "Select * from labels where batch_id = ? order by labelid ";
775 $sth = $dbh->prepare($query3);
776 $sth->execute($batch_id);
781 my $query3 = "Select * from labels";
782 $sth = $dbh->prepare($query3);
785 my $cnt = $sth->rows;
787 while ( my $data = $sth->fetchrow_hashref ) {
789 # lets get some summary info from each item
791 select i.*, bi.*, b.* from items i,biblioitems bi,biblio b
792 where itemnumber=? and i.biblioitemnumber=bi.biblioitemnumber and
793 bi.biblionumber=b.biblionumber";
795 my $sth1 = $dbh->prepare($query1);
796 $sth1->execute( $data->{'itemnumber'} );
798 my $data1 = $sth1->fetchrow_hashref();
799 $data1->{'labelno'} = $i1;
800 $data1->{'labelid'} = $data->{'labelid'};
801 $data1->{'batch_id'} = $batch_id;
802 $data1->{'summary'} =
803 "$data1->{'barcode'}, $data1->{'title'}, $data1->{'isbn'}";
805 push( @resultsloop, $data1 );
817 barcode title subtitle
818 dewey isbn issn author
class
819 itemtype subclass itemcallnumber
828 Parse labels_conf.formatstring value
829 (one value of the csv, which has already been split)
830 and return string from koha tables or MARC record.
835 my ($f,$item,$record) = @_;
836 my $kohatables= &_descKohaTables
();
839 my $match_kohatable = join('|', (@
{$kohatables->{biblio
}},@
{$kohatables->{biblioitems
}},@
{$kohatables->{items
}}) );
841 if( $f =~ /^'(.*)'.*/ ) {
842 # single quotes indicate a static text string.
845 } elsif ( $f =~ /^($match_kohatable).*/ ) {
846 # grep /$f/, (@$kohatables->{biblio},@$kohatables->{biblioitems},@$kohatables->{items}) ) {
847 $datastring .= $item->{$f};
849 } elsif ( $f =~ /^([0-9a-z]{3})(\w)(\W*).*/ ) {
850 $datastring .= $record->subfield($1,$2) . $3 if($record->subfield($1,$2)) ;
853 last if ( $f eq $last_f ); # failed to match
859 Return a hashref of an array of hashes,
863 sub _descKohaTables {
864 my $dbh = C4::Context->dbh();
866 for my $table ( 'biblio
','biblioitems
','items
' ) {
867 my $sth = $dbh->column_info(undef,undef,$table,'%');
868 while (my $info = $sth->fetchrow_hashref()){
869 push @{$kohatables->{$table}} , $info->{'COLUMN_NAME
'} ;
876 sub GetPatronCardItems {
878 my ( $batch_id ) = @_;
881 my $dbh = C4::Context->dbh;
882 # my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY borrowernumber";
883 my $query = "SELECT * FROM patroncards WHERE batch_id = ? ORDER BY cardid";
884 my $sth = $dbh->prepare($query);
885 $sth->execute($batch_id);
887 while ( my $data = $sth->fetchrow_hashref ) {
888 my $patron_data = GetMember( $data->{'borrowernumber
'} );
889 $patron_data->{'branchname
'} = GetBranchName( $patron_data->{'branchcode
'} );
890 $patron_data->{'cardno
'} = $cardno;
891 $patron_data->{'cardid
'} = $data->{'cardid
'};
892 $patron_data->{'batch_id
'} = $batch_id;
893 push( @resultsloop, $patron_data );
901 sub deduplicate_batch {
902 my ( $batch_id, $batch_type ) = @_;
905 batch_id," . (($batch_type eq 'labels
') ? 'itemnumber
' : 'borrowernumber
') . ",
906 count(". (($batch_type eq 'labels
') ? 'labelid
' : 'cardid
') . ") as count
909 GROUP BY " . (($batch_type eq 'labels
') ? 'itemnumber
' : 'borrowernumber
') . ",batch_id
913 my $sth = C4::Context->dbh->prepare($query);
914 $sth->execute($batch_id);
915 warn $sth->errstr if $sth->errstr;
916 $sth->rows or return undef, $sth->errstr;
922 AND " . (($batch_type eq 'labels
') ? 'itemnumber
' : 'borrowernumber
') . " = ?
923 ORDER BY timestamp ASC
926 while (my $data = $sth->fetchrow_hashref()) {
927 my $itemnumber = $data->{(($batch_type eq 'labels
') ? 'itemnumber
' : 'borrowernumber
')} or next;
928 my $limit = $data->{count} - 1 or next;
929 my $sth2 = C4::Context->dbh->prepare("$del_query LIMIT $limit");
930 # die sprintf "$del_query LIMIT %s\n (%s, %s)", $limit, $batch_id, $itemnumber;
931 # $sth2->execute($batch_id, C4::Context->dbh->quote($data->{itemnumber}), $data->{count} - 1)
932 $sth2->execute($batch_id, $itemnumber) and
933 $killed += ($data->{count} - 1);
934 warn $sth2->errstr if $sth2->errstr;
936 return $killed, undef;
941 my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
942 $text_wrap_cols, $item, $conf_data, $printingtype, $nowrap ) = @_;
944 # Replaced item's itemtype with the more user
-friendly description
...
945 my $dbh = C4
::Context
->dbh;
947 my $sth = $dbh->prepare("SELECT itemtype,description FROM itemtypes");
949 while ( my $data = $sth->fetchrow_hashref ) {
950 $$item->{'itemtype'} = $data->{'description'} if ($$item->{'itemtype'} eq $data->{'itemtype'});
955 my $top_text_margin = ( $fontsize + 3 ); #FIXME: This should be a template parameter and passed in...
956 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.).
958 my $layout_id = $$conf_data->{'id'};
960 my $vPos = ( $y_pos + ( $label_height - $top_text_margin ) );
962 my @str_fields = get_text_fields
($layout_id, 'codes' );
963 my $record = GetMarcBiblio
($$item->{biblionumber
});
964 # FIXME - returns all items, so you can't get data from an embedded holdings field.
965 # TODO - add a GetMarcBiblio1item(bibnum,itemnum) or a GetMarcItem(itemnum).
967 my $old_fontname = $fontname; # We need to keep track of the original font passed in...
969 for my $field (@str_fields) {
970 $field->{'code'} or warn "get_text_fields($layout_id, 'codes') element missing 'code' field";
971 if ($$conf_data->{'formatstring'}) {
972 $field->{'data'} = GetBarcodeData
($field->{'code'},$$item,$record) ;
974 $field->{data
} = $$item->{$field->{'code'}} ;
977 # This allows us to print the title in italic (oblique) type... (Times Roman has a different nomenclature.)
978 # It seems there should be a better way to handle fonts in the label/patron card tool altogether -fbcit
979 ($field->{code
} eq 'title') ?
(($old_fontname =~ /T/) ?
($fontname = 'TI') : ($fontname = ($old_fontname . 'O'))) : ($fontname = $old_fontname);
980 my $font = prFont
($fontname);
981 # if the display option for this field is selected in the DB,
982 # and the item record has some values for this field, display it.
983 if ( ($$conf_data->{'formatstring'}) || ( $$conf_data->{$field->{code
}} && $$item->{$field->{code
}} ) ) {
985 my $str = $field->{data
} ;
986 # strip out naughty existing nl/cr's
990 if ($field->{code
} eq 'itemcallnumber') { # If the field contains the call number, we do some special processing on it here...
991 if (($nowrap == 0) || (!$nowrap)) { # 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.)
992 while ( $str =~ /\// ) {
993 $str =~ /^(.*)\/(.*)$/;
994 unshift @strings, $2;
997 unshift @strings, $str;
999 push @strings, $str; # if $nowrap == 1 do not wrap or remove segmentation markers...
1002 $str =~ s/\/$//g; # Here we will strip out all trailing '/' in fields other than the call number...
1003 if ( length($str) > $text_wrap_cols ) { # wrap lines greater than $text_wrap_cols width...
1004 my $wrap = substr($str, ($text_wrap_cols - length($str)), $text_wrap_cols, "");
1005 push @strings, $str;
1006 push @strings, $wrap;
1008 push @strings, $str;
1011 # loop for each string line
1012 foreach my $str (@strings) {
1014 if ( $printingtype eq 'BIB
' ) { #FIXME: This is a hack and needs to be implimented as a text justification option in the template...
1015 # some code to try and center each line on the label based on font size and string point width...
1016 my $stringwidth = prStrWidth($str, $fontname, $fontsize);
1017 my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
1018 $hPos = ( ( $whitespace / 2 ) + $x_pos + $left_text_margin );
1019 #warn "\$label_width=$label_width \$stringwidth=$stringwidth \$whitespace=$whitespace \$left_text_margin=$left_text_margin for $str\n";
1021 $hPos = ( $x_pos + $left_text_margin );
1023 PrintText( $hPos, $vPos, $font, $fontsize, $str );
1024 $vPos = $vPos - $line_spacer;
1031 my ( $hPos, $vPos, $font, $fontsize, $text ) = @_;
1032 my $str = "BT /$font $fontsize Tf $hPos $vPos Td ($text) Tj ET";
1036 sub DrawPatronCardText {
1038 my ( $x_pos, $y_pos, $label_height, $label_width, $fontname, $fontsize, $left_text_margin,
1039 $text_wrap_cols, $text, $printingtype )
1042 my $top_text_margin = 25; #FIXME: This should be a template parameter and passed in...
1044 my $vPos = ( $y_pos + ( $label_height - $top_text_margin ) );
1045 my $font = prFont($fontname);
1049 foreach my $line (keys %$text) {
1050 $debug and warn "Current text is \"$line\" and font size for \"$line\" is $text->{$line} points";
1051 # some code to try and center each line on the label based on font size and string point width...
1052 my $stringwidth = prStrWidth($line, $fontname, $text->{$line});
1053 my $whitespace = ( $label_width - ( $stringwidth + (2 * $left_text_margin) ) );
1054 $hPos = ( ( $whitespace / 2 ) + $x_pos + $left_text_margin );
1056 PrintText( $hPos, $vPos, $font, $text->{$line}, $line );
1057 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.).
1058 $vPos = $vPos - ($line_spacer + $text->{$line}); # Linefeed equiv: leading + font size
1062 # Not used anywhere.
1066 # my ($fontsize) = @_;
1068 # my $str = "BT/F13 30 Tf288 720 Td( AAAAAAAAAA ) TjET";
1074 # x and y are from the top-left :)
1075 my ( $x_pos, $y_pos, $height, $width, $barcode, $barcodetype ) = @_;
1076 my $num_of_bars = length($barcode);
1077 my $bar_width = $width * .8; # %80 of length of label width
1080 my $guard_length = 10;
1083 if ( $barcodetype eq 'CODE39
' ) {
1084 $bar_length = '17.5';
1086 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1087 $xsize_ratio = ( $bar_width / $tot_bar_length );
1089 PDF::Reuse::Barcode::Code39(
1090 x => ( $x_pos + ( $width / 10 ) ),
1091 y => ( $y_pos + ( $height / 10 ) ),
1092 value => "*$barcode*",
1093 ySize => ( .02 * $height ),
1094 xSize => $xsize_ratio,
1099 warn "$barcodetype, $barcode FAILED:$@";
1103 elsif ( $barcodetype eq 'CODE39MOD
' ) {
1105 # get modulo43 checksum
1106 my $c39 = CheckDigits('code_39
');
1107 $barcode = $c39->complete($barcode);
1111 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1112 $xsize_ratio = ( $bar_width / $tot_bar_length );
1114 PDF::Reuse::Barcode::Code39(
1115 x => ( $x_pos + ( $width / 10 ) ),
1116 y => ( $y_pos + ( $height / 10 ) ),
1117 value => "*$barcode*",
1118 ySize => ( .02 * $height ),
1119 xSize => $xsize_ratio,
1125 warn "$barcodetype, $barcode FAILED:$@";
1128 elsif ( $barcodetype eq 'CODE39MOD10
' ) {
1130 # get modulo43 checksum
1131 my $c39_10 = CheckDigits('visa
');
1132 $barcode = $c39_10->complete($barcode);
1136 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1137 $xsize_ratio = ( $bar_width / $tot_bar_length );
1139 PDF::Reuse::Barcode::Code39(
1140 x => ( $x_pos + ( $width / 10 ) ),
1141 y => ( $y_pos + ( $height / 10 ) ),
1142 value => "*$barcode*",
1143 ySize => ( .02 * $height ),
1144 xSize => $xsize_ratio,
1151 warn "$barcodetype, $barcode FAILED:$@";
1156 elsif ( $barcodetype eq 'COOP2OF5
' ) {
1157 $bar_length = '9.43333333333333';
1159 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1160 $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1162 PDF::Reuse::Barcode::COOP2of5(
1163 x => ( $x_pos + ( $width / 10 ) ),
1164 y => ( $y_pos + ( $height / 10 ) ),
1166 ySize => ( .02 * $height ),
1167 xSize => $xsize_ratio,
1171 warn "$barcodetype, $barcode FAILED:$@";
1175 elsif ( $barcodetype eq 'INDUSTRIAL2OF5
' ) {
1176 $bar_length = '13.1333333333333';
1178 ( $bar_length * $num_of_bars ) + ( $guard_length * 2 );
1179 $xsize_ratio = ( $bar_width / $tot_bar_length ) * .9;
1181 PDF::Reuse::Barcode::Industrial2of5(
1182 x => ( $x_pos + ( $width / 10 ) ),
1183 y => ( $y_pos + ( $height / 10 ) ),
1185 ySize => ( .02 * $height ),
1186 xSize => $xsize_ratio,
1190 warn "$barcodetype, $barcode FAILED:$@";
1194 my $moo2 = $tot_bar_length * $xsize_ratio;
1196 warn "$x_pos, $y_pos, $barcode, $barcodetype" if $debug;
1197 warn "BAR_WDTH = $bar_width, TOT.BAR.LGHT=$tot_bar_length R*TOT.BAR =$moo2" if $debug;
1200 =item build_circ_barcode;
1202 build_circ_barcode( $x_pos, $y_pos, $barcode,
1203 $barcodetype, \$item);
1205 $item is the result of a previous call to GetLabelItems();
1210 sub build_circ_barcode
{
1211 my ( $x_pos_circ, $y_pos, $value, $barcodetype, $item ) = @_;
1213 #warn Dumper \$item;
1215 #warn "value = $value\n";
1219 if ( $barcodetype eq 'EAN13' ) {
1221 #testing EAN13 barcodes hack
1222 $value = $value . '000000000';
1224 $value = substr( $value, 0, 12 );
1228 PDF
::Reuse
::Barcode
::EAN13
(
1229 x
=> ( $x_pos_circ + 27 ),
1230 y
=> ( $y_pos + 15 ),
1238 # added for xpdf compat. doesnt use type3 fonts., but increases filesize from 20k to 200k
1239 # i think its embedding extra fonts in the pdf file.
1240 # mode => 'graphic',
1244 $item->{'barcodeerror'} = 1;
1246 #warn "EAN13BARCODE FAILED:$@";
1252 elsif ( $barcodetype eq 'Code39' ) {
1255 PDF
::Reuse
::Barcode
::Code39
(
1256 x
=> ( $x_pos_circ + 9 ),
1257 y
=> ( $y_pos + 15 ),
1267 $item->{'barcodeerror'} = 1;
1269 #warn "CODE39BARCODE $value FAILED:$@";
1276 elsif ( $barcodetype eq 'Matrix2of5' ) {
1278 #warn "MATRIX ELSE:";
1280 #testing MATRIX25 barcodes hack
1281 # $value = $value.'000000000';
1284 # $value = substr( $value, 0, 12 );
1288 PDF
::Reuse
::Barcode
::Matrix2of5
(
1289 x
=> ( $x_pos_circ + 27 ),
1290 y
=> ( $y_pos + 15 ),
1300 $item->{'barcodeerror'} = 1;
1302 #warn "BARCODE FAILED:$@";
1309 elsif ( $barcodetype eq 'EAN8' ) {
1311 #testing ean8 barcodes hack
1312 $value = $value . '000000000';
1314 $value = substr( $value, 0, 8 );
1318 #warn "EAN8 ELSEIF";
1320 PDF
::Reuse
::Barcode
::EAN8
(
1321 x
=> ( $x_pos_circ + 42 ),
1322 y
=> ( $y_pos + 15 ),
1332 $item->{'barcodeerror'} = 1;
1334 #warn "BARCODE FAILED:$@";
1341 elsif ( $barcodetype eq 'UPC-E' ) {
1343 PDF
::Reuse
::Barcode
::UPCE
(
1344 x
=> ( $x_pos_circ + 27 ),
1345 y
=> ( $y_pos + 15 ),
1355 $item->{'barcodeerror'} = 1;
1357 #warn "BARCODE FAILED:$@";
1363 elsif ( $barcodetype eq 'NW7' ) {
1365 PDF
::Reuse
::Barcode
::NW7
(
1366 x
=> ( $x_pos_circ + 27 ),
1367 y
=> ( $y_pos + 15 ),
1377 $item->{'barcodeerror'} = 1;
1379 #warn "BARCODE FAILED:$@";
1385 elsif ( $barcodetype eq 'ITF' ) {
1387 PDF
::Reuse
::Barcode
::ITF
(
1388 x
=> ( $x_pos_circ + 27 ),
1389 y
=> ( $y_pos + 15 ),
1399 $item->{'barcodeerror'} = 1;
1401 #warn "BARCODE FAILED:$@";
1407 elsif ( $barcodetype eq 'Industrial2of5' ) {
1409 PDF
::Reuse
::Barcode
::Industrial2of5
(
1410 x
=> ( $x_pos_circ + 27 ),
1411 y
=> ( $y_pos + 15 ),
1420 $item->{'barcodeerror'} = 1;
1422 #warn "BARCODE FAILED:$@";
1428 elsif ( $barcodetype eq 'IATA2of5' ) {
1430 PDF
::Reuse
::Barcode
::IATA2of5
(
1431 x
=> ( $x_pos_circ + 27 ),
1432 y
=> ( $y_pos + 15 ),
1441 $item->{'barcodeerror'} = 1;
1443 #warn "BARCODE FAILED:$@";
1450 elsif ( $barcodetype eq 'COOP2of5' ) {
1452 PDF
::Reuse
::Barcode
::COOP2of5
(
1453 x
=> ( $x_pos_circ + 27 ),
1454 y
=> ( $y_pos + 15 ),
1463 $item->{'barcodeerror'} = 1;
1465 #warn "BARCODE FAILED:$@";
1471 elsif ( $barcodetype eq 'UPC-A' ) {
1474 PDF
::Reuse
::Barcode
::UPCA
(
1475 x
=> ( $x_pos_circ + 27 ),
1476 y
=> ( $y_pos + 15 ),
1485 $item->{'barcodeerror'} = 1;
1487 #warn "BARCODE FAILED:$@";
1496 =item draw_boundaries
1498 sub draw_boundaries ($x_pos_spine, $x_pos_circ1, $x_pos_circ2,
1499 $y_pos, $spine_width, $label_height, $circ_width)
1501 This sub draws boundary lines where the label outlines are, to aid in printer testing, and debugging.
1506 sub draw_boundaries
{
1509 $x_pos_spine, $x_pos_circ1, $x_pos_circ2, $y_pos,
1510 $spine_width, $label_height, $circ_width
1513 my $y_pos_initial = ( ( 792 - 36 ) - 90 );
1514 $y_pos = $y_pos_initial; # FIXME - why are we ignoring the y_pos parameter by redefining it?
1517 for ( $i = 1 ; $i <= 8 ; $i++ ) {
1519 &drawbox
( $x_pos_spine, $y_pos, ($spine_width), ($label_height) );
1521 #warn "OLD BOXES x=$x_pos_spine, y=$y_pos, w=$spine_width, h=$label_height";
1522 &drawbox
( $x_pos_circ1, $y_pos, ($circ_width), ($label_height) );
1523 &drawbox
( $x_pos_circ2, $y_pos, ($circ_width), ($label_height) );
1525 $y_pos = ( $y_pos - $label_height );
1532 sub drawbox { $lower_left_x, $lower_left_y,
1533 $upper_right_x, $upper_right_y )
1535 this is a low level sub, that draws a pdf box, it is called by draw_boxes
1537 FYI: the $upper_right_x and $upper_right_y values are RELATIVE to $lower_left_x and $lower_left_y
1539 and $lower_left_x, $lower_left_y are ABSOLUTE, this caught me out!
1545 my ( $llx, $lly, $urx, $ury ) = @_;
1547 # warn "llx,y= $llx,$lly , urx,y=$urx,$ury \n";
1549 my $str = "q\n"; # save the graphic state
1550 $str .= "0.5 w\n"; # border color red
1551 $str .= "1.0 0.0 0.0 RG\n"; # border color red
1552 # $str .= "0.5 0.75 1.0 rg\n"; # fill color blue
1553 $str .= "1.0 1.0 1.0 rg\n"; # fill color white
1555 $str .= "$llx $lly $urx $ury re\n"; # a rectangle
1556 $str .= "B\n"; # fill (and a little more)
1557 $str .= "Q\n"; # save the graphic state
1563 END { } # module clean-up code here (global destructor)
1572 Mason James <mason@katipo.co.nz>