From d074ad1767c66c54313adac185f807cf06e6978f Mon Sep 17 00:00:00 2001 From: sushi Date: Tue, 11 Apr 2006 05:50:28 +0000 Subject: [PATCH] Inital add of spine label perl files. --- barcodes/label-home.pl | 53 ++++ barcodes/label-manager.pl | 137 ++++++++++ barcodes/label-print-pdf.pl | 526 ++++++++++++++++++++++++++++++++++++ barcodes/label-print.pl | 92 +++++++ 4 files changed, 808 insertions(+) create mode 100755 barcodes/label-home.pl create mode 100755 barcodes/label-manager.pl create mode 100755 barcodes/label-print-pdf.pl create mode 100755 barcodes/label-print.pl diff --git a/barcodes/label-home.pl b/barcodes/label-home.pl new file mode 100755 index 0000000000..d9e3146f84 --- /dev/null +++ b/barcodes/label-home.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +use strict; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Interface::CGI::Output; +use C4::Context; +use HTML::Template; + +my $query = new CGI; +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "barcodes/label-home.tmpl", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { catalogue => 1 }, + debug => 1, + } +); + +my $dbh = C4::Context->dbh; +my $query2 = "SELECT * FROM labels_conf LIMIT 1"; +my $sth = $dbh->prepare($query2); +$sth->execute(); + +my $data = $sth->fetchrow_hashref; +$sth->finish; + +# next line passes a var like 'EAN13_cheched' to the tmpl , +# which makes the barcodetype sticky in the dropbox + +$template->param( "$data->{'barcodetype'}_checked" => 1 ); +$template->param( "startrow" . $data->{'startrow'} . "_checked" => 1 ); +$template->param( + itemtype => $data->{'itemtype'}, + papertype => $data->{'papertype'}, + author => $data->{'author'}, + barcode => $data->{'barcode'}, + id => $data->{'id'}, + barcodetype => $data->{'barcodetype'}, + title => $data->{'title'}, + isbn => $data->{'isbn'}, + dewey => $data->{'dewey'}, + class => $data->{'class'}, + startrow => $data->{'startrow'}, + intranetcolorstylesheet => + C4::Context->preference("intranetcolorstylesheet"), + intranetstylesheet => C4::Context->preference("intranetstylesheet"), + IntranetNav => C4::Context->preference("IntranetNav"), +); +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/barcodes/label-manager.pl b/barcodes/label-manager.pl new file mode 100755 index 0000000000..02e534a758 --- /dev/null +++ b/barcodes/label-manager.pl @@ -0,0 +1,137 @@ +#!/usr/bin/perl + +use strict; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Interface::CGI::Output; +use HTML::Template; +use POSIX; + +my $dbh = C4::Context->dbh; +my $query = new CGI; +my $op = $query->param('op'); +my $barcodetype = $query->param('barcodetype'); +my $title = $query->param('title'); +my $isbn = $query->param('isbn'); +my $itemtype = $query->param('itemtype'); +my $bcn = $query->param('bcn'); +my $dcn = $query->param('dcn'); +my $classif = $query->param('classif'); +my $author = $query->param('author'); +my $papertype = $query->param('papertype'); +my $itemnumber = $query->param('itemnumber'); +my $summary = $query->param('summary'); +my $startrow = $query->param('startrow'); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "barcodes/label-manager.tmpl", + query => $query, + type => "intranet", + authnotrequired => 1, + flagsrequired => { catalogue => 1 }, + debug => 1, + } +); + +if ( $op eq 'save_conf' ) { + my $query2 = "DELETE FROM labels_conf"; + my $sth2 = $dbh->prepare($query2); + $sth2->execute(); + $sth2->finish; + my $query2 = "INSERT INTO labels_conf + ( barcodetype, title, isbn, itemtype, barcode, + dewey, class, author, papertype, startrow) + values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"; + my $sth2 = $dbh->prepare($query2); + $sth2->execute( + $barcodetype, $title, $isbn, $itemtype, $bcn, + $dcn, $classif, $author, $papertype, $startrow + ); + $sth2->finish; + +} +elsif ( $op eq 'add' ) { + my $query2 = "INSERT INTO labels ( itemnumber ) values ( ? )"; + my $sth2 = $dbh->prepare($query2); + $sth2->execute($itemnumber); + $sth2->finish; +} +elsif ( $op eq 'deleteall' ) { + my $query2 = "DELETE FROM labels"; + my $sth2 = $dbh->prepare($query2); + $sth2->execute(); + $sth2->finish; +} +elsif ( $op eq 'delete' ) { + my $query2 = "DELETE FROM labels where itemnumber = ?"; + my $sth2 = $dbh->prepare($query2); + $sth2->execute($itemnumber); + $sth2->finish; +} + +# first lets do a read of the labels table , to get the a list of the +# currently entered items to be prinited + +my @resultsloop = (); +my $count; +my @data; +my $query3 = "Select * from labels"; +my $sth = $dbh->prepare($query3); +$sth->execute(); + +my $cnt = $sth->rows; +my $i1 = 1; +while ( my $data = $sth->fetchrow_hashref ) { + + # lets get some summary info from each item + my $query1 = " + select * from biblio,biblioitems,items where itemnumber=? and + items.biblioitemnumber=biblioitems.biblioitemnumber and + biblioitems.biblionumber=biblio.biblionumber"; + + my $sth1 = $dbh->prepare($query1); + $sth1->execute( $data->{'itemnumber'} ); + my $data1 = $sth1->fetchrow_hashref(); + + $data1->{'labelno'} = $i1; + $data1->{'summary'} = + "$data1->{'barcode'}, $data1->{'title'}, $data1->{'isbn'}"; + + push( @resultsloop, $data1 ); + $sth1->finish; + + $i1++; +} +$sth->finish; + +# this script can be run from the side nav, and is not passed a value for $startrow +# so lets get it from the DB +if ( !$startrow ) { + + my $dbh = C4::Context->dbh; + my $query2 = "SELECT * FROM labels_conf LIMIT 1"; + my $sth = $dbh->prepare($query2); + $sth->execute(); + + my $data = $sth->fetchrow_hashref; + $startrow = $data->{'startrow'}; + $sth->finish; +} + +#calc-ing number of sheets +my $number_of_results = scalar @resultsloop; +my $sheets_needed = ( ( $number_of_results + $startrow ) / 8 ); +$sheets_needed = ceil($sheets_needed); # rounding up int's + +$template->param( + resultsloop => \@resultsloop, + startrow => $startrow, + sheets => $sheets_needed, + intranetcolorstylesheet => + C4::Context->preference("intranetcolorstylesheet"), + intranetstylesheet => C4::Context->preference("intranetstylesheet"), + IntranetNav => C4::Context->preference("IntranetNav"), +); +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/barcodes/label-print-pdf.pl b/barcodes/label-print-pdf.pl new file mode 100755 index 0000000000..ba82f80096 --- /dev/null +++ b/barcodes/label-print-pdf.pl @@ -0,0 +1,526 @@ +#!/usr/bin/perl + +#use strict; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Interface::CGI::Output; +use C4::Context; +use HTML::Template; +use PDF::Reuse; +use PDF::Reuse::Barcode; + +my $htdocs_path = C4::Context->config('intrahtdocs'); +my $cgi = new CGI; + +my $spine_text = ""; + +#-------------------------------------------------------- +# get the printing settings + +my $dbh = C4::Context->dbh; +my $query2 = " SELECT * FROM labels_conf LIMIT 1 "; +my $sth = $dbh->prepare($query2); +$sth->execute(); + +my $conf_data = $sth->fetchrow_hashref; + +# get barcode type from $conf_data +my $barcodetype = $conf_data->{'barcodetype'}; +my $startrow = $conf_data->{'startrow'}; + +$sth->finish; + +#------------------ + +# get the actual items to be printed. +my @data; +my $query3 = " Select * from labels "; +my $sth = $dbh->prepare($query3); +$sth->execute(); +my @resultsloop; +my $cnt = $sth->rows; +my $i1 = 1; +while ( my $data = $sth->fetchrow_hashref ) { + + # lets get some summary info from each item + my $query1 = + " select *from biblio, biblioitems, items where itemnumber = ? and + items.biblioitemnumber=biblioitems.biblioitemnumber and + biblioitems.biblionumber=biblio.biblionumber"; + + my $sth1 = $dbh->prepare($query1); + $sth1->execute( $data->{'itemnumber'} ); + my $data1 = $sth1->fetchrow_hashref(); + + push( @resultsloop, $data1 ); + $sth1->finish; + + $i1++; +} +$sth->finish; + +# dimensions of gaylord paper +my $lowerLeftX = 0; +my $lowerLeftY = 0; +my $upperRightX = 612; +my $upperRightY = 792; + +#---------------------------------- +# setting up the pdf doc + +prFile("$htdocs_path/barcodes/new.pdf"); +prLogDir("$htdocs_path/barcodes"); + +#prMbox ( $lowerLeftX, $lowerLeftY, $upperRightX, $upperRightY ); +prMbox( 0, 0, 612, 792 ); + +prFont('Times-Roman'); # Just setting a font +prFontSize(10); + +my $margin = 36; + +my $label_height = 90; +my $spine_width = 72; +my $circ_width = 207; +my $colspace = 27; + +my $x_pos_spine = 36; +my $x_pos_circ1 = 135; +my $x_pos_circ2 = 369; + +my $pageheight = 792; + +my $y_pos_initial = ( ( $pageheight - $margin ) - $label_height ); +my $y_pos_initial_startrow = + ( ( $pageheight - $margin ) - ( $label_height * $startrow ) ); + +my $y_pos_initial = ( ( 792 - 36 ) - 90 ); + +my $y_pos = $y_pos_initial_startrow; + +#my $y_pos = $y_pos_initial; +my $rowspace = 36; +my $page_break_count = $startrow; +my $codetype = 'Code39'; + +# do border--------------- +my $str = "q\n"; # save the graphic state +$str .= "4 w\n"; # border color red +$str .= "0.0 0.0 0.0 RG\n"; # border color red +$str .= "1 1 1 rg\n"; # fill color blue +$str .= "0 0 612 792 re\n"; # a rectangle +$str .= "B\n"; # fill (and a little more) +$str .= "Q\n"; # save the graphic state + +# do border--------------- + +prAdd($str); +my $item; + +my $i2 = 1; +foreach $item (@resultsloop) { + if ( $i2 == 1 ) { + + #draw_boxes(); + } + + #building up spine text + my $line = 75; + my $line_spacer = 16; + + build_circ_barcode( $x_pos_circ1, $y_pos, $item->{'barcode'}, + $conf_data->{'barcodetype'} ); + build_circ_barcode( $x_pos_circ2, $y_pos, $item->{'barcode'}, + $conf_data->{'barcodetype'} ); + +# added for xpdf compat. doesnt use type3 fonts., but increases filesize from 20k to 200k +# i think its embedding extra fonts in the pdf file. +# mode => 'graphic', + + $y_pos = ( $y_pos - $label_height ); + + # the gaylord labels have 8 rows per sheet, this pagebreaks after 8 rows + if ( $page_break_count == 8 ) { + prPage(); + + $page_break_count = 0; + $i2 = 0; + $y_pos = $y_pos_initial; + } + $page_break_count++; + $i2++; +} + +prEnd(); + +#---------------------------------------------------------------------------- + +use PDF::Table; +use Acme::Comment; + +$file = '/usr/local/opus-dev/intranet/htdocs/intranet-tmpl/barcodes/new.pdf'; +use PDF::Report; + +my $pdf = new PDF::Report( File => $file ); + +# my $pdf = new PDF::Report(PageSize => "letter", +# PageOrientation => "Landscape"); + +#$pdf->newpage($nopage); +my $pagenumber = 1; +$pdf->openpage($pagenumber); + +( $pagewidth, $pageheight ) = $pdf->getPageDimensions(); +my $y_pos = ( $y_pos_initial_startrow + 90 ); +$pdf->setAlign('left'); +$pdf->setSize(9); + +my $page_break_count = $startrow; + +foreach $item (@resultsloop) { + + my $firstrow = 0; + + $pdf->setAddTextPos( 36, ( $y_pos - 15 ) ); # INIT START POS + ( $hPos, $vPos ) = $pdf->getAddTextPos(); + ( $hPos, $vPos1 ) = $pdf->getAddTextPos(); + + if ( $conf_data->{'dewey'} && $item->{'dewey'} ) { + + ( $hPos, $vPos1 ) = $pdf->getAddTextPos(); + $pdf->addText( $item->{'dewey'}, 10, 72, 90 ); + ( $hPos, $vPos1 ) = $pdf->getAddTextPos(); + $firstrow = 1; + } + + if ( $conf_data->{'isbn'} && $item->{'isbn'} ) { + if ( $vPos1 == $vPos && $firstrow != 0 ) { + $pdf->setAddTextPos( 36, ( $vPos - 15 ) ); + } + else { + $pdf->setAddTextPos( 36, $vPos1 - 5 ); #add a space + } + + ( $hPos, $vPos ) = $pdf->getAddTextPos(); + $pdf->addText( $item->{'isbn'}, 10, 72, 90 ); + ( $hPos, $vPos1 ) = $pdf->getAddTextPos(); + $firstrow = 1; + } + + if ( $conf_data->{'class'} && $item->{'classification'} ) { + + if ( $vPos1 == $vPos && $firstrow != 0 ) { + $pdf->setAddTextPos( 36, ( $vPos - 15 ) ); + } + else { + $pdf->setAddTextPos( 36, $vPos1 - 5 ); #add a space + } + + ( $hPos, $vPos ) = $pdf->getAddTextPos(); + $pdf->addText( $item->{'classification'}, 10, 72, 90 ); + ( $hPos, $vPos1 ) = $pdf->getAddTextPos(); + $firstrow = 1; + } + + if ( $conf_data->{'itemtype'} && $item->{'itemtype'} ) { + + if ( $vPos1 == $vPos && $firstrow != 0 ) { + $pdf->setAddTextPos( 36, ( $vPos - 15 ) ); + } + else { + $pdf->setAddTextPos( 36, $vPos1 - 5 ); #add a space + } + + ( $hPos, $vPos ) = $pdf->getAddTextPos(); + $pdf->addText( $item->{'itemtype'}, 10, 72, 90 ); + ( $hPos, $vPos1 ) = $pdf->getAddTextPos(); + $firstrow = 1; + } + + #$pdf->drawRect( + # $x_pos_spine, $y_pos, + # ( $x_pos_spine + $spine_width ), + # ( $y_pos - $label_height ) + #); + + $y_pos = ( $y_pos - $label_height ); + if ( $page_break_count == 8 ) { + $pagenumber++; + $pdf->openpage($pagenumber); + + $page_break_count = 0; + $i2 = 0; + $y_pos = ( $y_pos_initial + 90 ); + } + + $page_break_count++; + $i2++; + +} +$DB::single = 1; +$pdf->saveAs($file); + +#------------------------------------------------ + +print $cgi->redirect("/intranet-tmpl/barcodes/new.pdf"); + +# draw boxes------------------ +sub draw_boxes { + + my $y_pos_initial = ( ( 792 - 36 ) - 90 ); + my $y_pos = $y_pos_initial; + my $i = 1; + + for ( $i = 1 ; $i <= 8 ; $i++ ) { + + &drawbox( $x_pos_spine, $y_pos, ($spine_width), ($label_height) ); + + &drawbox( $x_pos_circ1, $y_pos, ($circ_width), ($label_height) ); + &drawbox( $x_pos_circ2, $y_pos, ($circ_width), ($label_height) ); + + $y_pos = ( $y_pos - $label_height ); + + } +} + +# draw boxes------------------ + +sub build_circ_barcode { + my ( $x_pos_circ, $y_pos, $value, $barcodetype ) = @_; + + #$DB::single = 1; + + if ( $barcodetype eq 'EAN13' ) { + + #testing EAN13 barcodes hack + $value = $value . '000000000'; + $value =~ s/-//; + $value = substr( $value, 0, 12 ); + + eval { + PDF::Reuse::Barcode::EAN13( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + + # prolong => 2.96, + # xSize => 1.5, + + # ySize => 1.2, + +# added for xpdf compat. doesnt use type3 fonts., but increases filesize from 20k to 200k +# i think its embedding extra fonts in the pdf file. +# mode => 'graphic', + ); + }; + if ($@) { + $item->{'barcodeerror'} = 1; + } + + } + elsif ( $barcodetype eq 'Code39' ) { + + eval { + PDF::Reuse::Barcode::Code39( + x => ( $x_pos_circ + 9 ), + y => ( $y_pos + 15 ), + value => $value, + + # prolong => 2.96, + xSize => .85, + + ySize => 1.3, + ); + }; + if ($@) { + $item->{'barcodeerror'} = 1; + } + } + + elsif ( $barcodetype eq 'Matrix2of5' ) { + + #testing MATRIX25 barcodes hack + # $value = $value.'000000000'; + $value =~ s/-//; + + # $value = substr( $value, 0, 12 ); + + eval { + PDF::Reuse::Barcode::Matrix2of5( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + + # prolong => 2.96, + # xSize => 1.5, + + # ySize => 1.2, + ); + }; + if ($@) { + $item->{'barcodeerror'} = 1; + } + } + + elsif ( $barcodetype eq 'EAN8' ) { + + #testing ean8 barcodes hack + $value = $value . '000000000'; + $value =~ s/-//; + $value = substr( $value, 0, 8 ); + + eval { + PDF::Reuse::Barcode::EAN8( + x => ( $x_pos_circ + 42 ), + y => ( $y_pos + 15 ), + value => $value, + prolong => 2.96, + xSize => 1.5, + + # ySize => 1.2, + ); + }; + + if ($@) { + $item->{'barcodeerror'} = 1; + } + + } + + elsif ( $barcodetype eq 'UPC-E' ) { + eval { + PDF::Reuse::Barcode::UPCE( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + prolong => 2.96, + xSize => 1.5, + + # ySize => 1.2, + ); + }; + + if ($@) { + $item->{'barcodeerror'} = 1; + } + } + elsif ( $barcodetype eq 'NW7' ) { + eval { + PDF::Reuse::Barcode::NW7( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + prolong => 2.96, + xSize => 1.5, + + # ySize => 1.2, + ); + }; + + if ($@) { + $item->{'barcodeerror'} = 1; + } + } + elsif ( $barcodetype eq 'ITF' ) { + eval { + PDF::Reuse::Barcode::ITF( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + prolong => 2.96, + xSize => 1.5, + + # ySize => 1.2, + ); + }; + + if ($@) { + $item->{'barcodeerror'} = 1; + } + } + elsif ( $barcodetype eq 'Industrial2of5' ) { + eval { + PDF::Reuse::Barcode::Industrial2of5( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + prolong => 2.96, + xSize => 1.5, + + # ySize => 1.2, + ); + }; + if ($@) { + $item->{'barcodeerror'} = 1; + } + } + elsif ( $barcodetype eq 'IATA2of5' ) { + eval { + PDF::Reuse::Barcode::IATA2of5( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + prolong => 2.96, + xSize => 1.5, + + # ySize => 1.2, + ); + }; + if ($@) { + $item->{'barcodeerror'} = 1; + } + + } + + elsif ( $barcodetype eq 'COOP2of5' ) { + eval { + PDF::Reuse::Barcode::COOP2of5( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + prolong => 2.96, + xSize => 1.5, + + # ySize => 1.2, + ); + }; + if ($@) { + $item->{'barcodeerror'} = 1; + } + } + elsif ( $barcodetype eq 'UPC-A' ) { + + eval { + PDF::Reuse::Barcode::UPCA( + x => ( $x_pos_circ + 27 ), + y => ( $y_pos + 15 ), + value => $value, + prolong => 2.96, + xSize => 1.5, + + # ySize => 1.2, + ); + }; + if ($@) { + $item->{'barcodeerror'} = 1; + } + } +} + +#----------------------------- + +sub drawbox { + my ( $llx, $lly, $urx, $ury ) = @_; + + my $str = "q\n"; # save the graphic state + $str .= "1.0 0.0 0.0 RG\n"; # border color red + $str .= "1 1 1 rg\n"; # fill color blue + $str .= "$llx $lly $urx $ury re\n"; # a rectangle + $str .= "B\n"; # fill (and a little more) + $str .= "Q\n"; # save the graphic state + + prAdd($str); + +} + diff --git a/barcodes/label-print.pl b/barcodes/label-print.pl new file mode 100755 index 0000000000..013e29f40c --- /dev/null +++ b/barcodes/label-print.pl @@ -0,0 +1,92 @@ +#!/usr/bin/perl + +use strict; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Interface::CGI::Output; +use C4::Context; +use HTML::Template; +use GD::Barcode::UPCE; + +my $htdocs_path = C4::Context->config('intrahtdocs'); +my $query = new CGI; +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "barcodes/label-print.tmpl", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { catalogue => 1 }, + debug => 1, + } +); + +my $dbh = C4::Context->dbh; +my $query2 = "SELECT * FROM labels_conf LIMIT 1"; +my $sth = $dbh->prepare($query2); +$sth->execute(); +my $conf_data = $sth->fetchrow_hashref; + +# get barcode type from $conf_data +my $barcodetype = $conf_data->{'barcodetype'}; +$sth->finish; + +my @data; +my $query3 = "Select * from labels"; +my $sth = $dbh->prepare($query3); +$sth->execute(); +my @resultsloop; +my $cnt = $sth->rows; +my $i1 = 1; +while ( my $data = $sth->fetchrow_hashref ) { + + # lets get some summary info from each item + my $query1 = " + SELECT * FROM biblio,biblioitems,items WHERE itemnumber=? AND + items.biblioitemnumber=biblioitems.biblioitemnumber AND + biblioitems.biblionumber=biblio.biblionumber"; + + my $sth1 = $dbh->prepare($query1); + $sth1->execute( $data->{'itemnumber'} ); + my $data1 = $sth1->fetchrow_hashref(); + push( @resultsloop, $data1 ); + $sth1->finish; + + $i1++; +} +$sth->finish; + +#lets write barcode files to tmp dir for every item in @resultsloop + +binmode(FILE); +foreach my $item (@resultsloop) { + my $filename = "$htdocs_path/barcodes/$barcodetype-$item->{'barcode'}.png"; + open( FILE, ">$filename" ); + eval { + print FILE GD::Barcode->new( $barcodetype, $item->{'barcode'} ) + ->plot->png; + }; + if ($@) { + $item->{'barcodeerror'} = 1; + } + close(FILE); +} + +$template->param( + resultsloop => \@resultsloop, + itemtype_opt => $conf_data->{'itemtype'}, + papertype_opt => $conf_data->{'papertype'}, + author_opt => $conf_data->{'author'}, + id_opt => $conf_data->{'id'}, + barcodetype_opt => $conf_data->{'barcodetype'}, + title_opt => $conf_data->{'title'}, + isbn_opt => $conf_data->{'isbn'}, + dewey_opt => $conf_data->{'dewey'}, + class_opt => $conf_data->{'class'}, + intranetcolorstylesheet => + C4::Context->preference("intranetcolorstylesheet"), + intranetstylesheet => C4::Context->preference("intranetstylesheet"), + IntranetNav => C4::Context->preference("IntranetNav"), +); +output_html_with_http_headers $query, $cookie, $template->output; -- 2.39.5