How can I make PDF tables from Perl? - perl

Are there any perl modules to draw a table in a pdf at specified position with given rows and columns with an empty content in each cell?

Two come to mind:
PDF::Table
PDF::Report::Table
I produced a simple table using PDF::Table like so:
use PDF::API2;
use PDF::Table;
my $pdf = PDF::API2->new( -file => 'table.pdf' );
my $table = PDF::Table->new;
my $page = $pdf->page;
my $data = [
[ 'A1', 'A2', 'A3' ],
[ 'B1', 'B2', 'B3' ],
[ 'C1', 'C2', 'C3' ],
];
$table->table( $pdf, $page, $data,
x => 50,
w => 495,
start_y => 750,
next_y => 700,
start_h => 300,
next_h => 500,
);
$pdf->save;
And with PDF::Report::Table like this:
use PDF::Report;
use PDF::Report::Table;
my $pdf = PDF::Report->new( PageSize => 'A4', PageOrientation => 'Portrait' );
my $table = PDF::Report::Table->new( $pdf );
my $data = [
[ 'A1', 'A2', 'A3' ],
[ 'B1', 'B2', 'B3' ],
[ 'C1', 'C2', 'C3' ],
];
$pdf->openpage;
$pdf->setAddTextPos( 50, 50 );
$table->addTable( $data, 400 ); # 400 is table width
$pdf->saveAs( 'table.pdf' );

Related

How can I sort a Hash of hashes of Array of Arrays by (inner) array in value in Perl [duplicate]

This question already has an answer here:
Sorting a Hash of Hashes of Array of Arrays in Perl
(1 answer)
Closed 7 years ago.
I have this data-structure, product of my data:
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4' => {
'-1.19 +' => [
[
'+', '2', '120', '0.76', '0.809799'
]
],
'-1.19 -' => [
[
'-', '2', '120', '0.19', '0.585090'
]
]
}
I want to organize it according to the last value of the 'inside' array that corresponds to the data grouped by a 'inside' key, in this case: '-1.19 +' and '-1.19 -'. Because I want to put the the key that has the greatest value of the inside array, on the bottom of my hash.
In other words:
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4' => {
'-1.19 -' => [
[
'-', '2', '120', '0.19', '0.585090'
]
],
'-1.19 +' => [
[
'+', '2', '120', '0.76', '0.809799'
]
]
}
How Can I do this task, because I want to select the key that has the greater value, so I want to shift the other ones.
Any suggestions? Thanks in advance.
To get the last element, use the index of -1.
my $inner_hash = $hash{'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4'};
my #sorted = sort { $inner_hash->{$a}[-1]
<=>
$inner_hash->{$b}[-1]
} keys %$inner_hash;
print "#sorted\n";
To sort a list by comparing some expression on each element individually, you might like to use the collection of functions in List::UtilsBy called sort_by. Specifically in this case as the key expression is returning a number to be compared numerically, you'll want to use nsort_by:
use List::UtilsBy 'nsort_by';
my #sorted = nsort_by { $_->[-1] } #items;
Keys in the hash doesn't have any order so if you want to have keys in order you have to change your inner hash into an array.
my %hash = (
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4'
=> {
'-1.19 +' => [ [ '+', '2', '120', '0.76', '0.809799' ] ],
'-1.19 -' => [ [ '-', '2', '120', '0.19', '0.585090' ] ]
}
);
for my $value ( values %hash ) {
$value = [
map +[ $_, $value->{$_} ],
sort { $value->{$a}[0][-1] <=> $value->{$b}[0][-1] } keys %$value
];
}
The result will be
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4'
=> [
[ '-1.19 -', [ [ '-', '2', '120', '0.19', '0.585090' ] ] ],
[ '-1.19 +', [ [ '+', '2', '120', '0.76', '0.809799' ] ] ]
]
I would recommend removing double array in inside so code would look like:
for my $value ( values %hash ) {
$value = [
map +[ $_, $value->{$_}[0] ],
sort { $value->{$a}[0][-1] <=> $value->{$b}[0][-1] } keys %$value
];
}
and the result:
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4'
=> [
[ '-1.19 -', [ '-', '2', '120', '0.19', '0.585090' ] ],
[ '-1.19 +', [ '+', '2', '120', '0.76', '0.809799' ] ]
]

dereference xml::simple output which is complex data structure that is a hash of an array of hashs

I am trying to parse xml using xml::simple .Here is the output of xml::simple
$VAR1 = {
'soapenv:Body'=>[
{
'ns1:queryResponse'=>[
{
'ns1:result'=>[
{
'ns1:done'=>['true'],
'ns1:queryLocator' => [
{
'xsi:nil' => '1',
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
}
],
'ns1:size' => [
'60'
],
'ns1:records' => [
{
'ns2:RefundTransactionTime' => [
'2013-09-12T13:17:18.000-08:00'
],
'xmlns:ns2' => 'http://object.abccom/',
'ns2:MethodType' => [
'CreditCard'
],
'ns2:Gateway' => [
'Chase Paymentech'
],
'ns2:Type' => [
'Electronic'
],
'ns2:RefundDate' => [
'2013-09-12T00:00:00'
],
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'ns2:Status' => [
'Processed'
],
'ns2:Id' => [
'2c92c0f8410f4d9a014113d2170a2e17'
],
'xsi:type' => 'ns2:Refund',
'ns2:AccountId' => [
'2c92c0f9410f55ee0141132b6c936e15'
],
'ns2:Amount' => [
'99'
],
'ns2:CreatedDate' => [
'2013-09-12T13:17:17.000-08:00'
]
},
{
'xmlns:ns2' => 'http://object.abccom/',
'ns2:MethodType' => [
'CreditCard'
],
'ns2:Type' => [
'External'
],
'ns2:RefundDate' => [
'2013-10-12T00:00:00'
],
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'ns2:Status' => [
'Processed'
],
'ns2:Id' => [
'2c92c0f8410f4d9a0141145bfbb61a9b'
],
'xsi:type' => 'ns2:Refund',
'ns2:AccountId' => [
'2c92c0f8410f4d8701411411a9ad79b7'
],
'ns2:Amount' => [
'477.74'
],
'ns2:CreatedDate' => [
'2013-09-12T15:47:54.000-08:00'
],
'ns2:Comment' => [
'16 Payment Exception - Chargeback'
]
}
]
}
],
'xmlns:ns1' => 'http://api.abccom/'
}
]
}
],
'xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/'
};
I am using below code :
#!/usr/bin/env perl
use strict;
use Data::Dumper;
use XML::Simple qw(:strict);
my $data = XMLin('Refund.xml', forcearray=>1, keyattr=>[] );
print "Reference type in data is : ", ref($data), "\n";
print Dumper($data);
#Try to access the values
my $records=$data->{"soapenv:Body"}->[0]->{"ns1:queryResponse"}->[0]->{"ns1:result"}->
[0]->{"ns1:records"};
foreach my $record ( #{ $records } ) {
print $record->{"ns2:RefundTransactionTime"};
print "\n";
}
print Dumper($data) generates hash reference containing array of hashes.
I want to format the hash reference generated above to the array of array references format as shown below:
[
[
"AccountId",
"Id",
"Amount",
"CreatedDate",
"Comment",
"Status",
"Type",
"RefundDate",
"MethodType"
],
[
"2c92c0f8410f4d8701411411a9ad79b7",
"2c92c0f8410f4d9a0141145bfbb61a9b",
"477.74",
"2013-09-12T15:47:54.000-08:00",
"16 Payment Exception - Chargeback",
"Processes",
"External",
"2013-10-12T00:00:00",
"CreditCard"
],
[
"2c92c0f9410f55ee0141132b6c936e15",
"2c92c0f8410f4d9a014113d2170a2e17",
"99",
"2013-09-12T13:17:17.000-08:00",
"",
"Processed",
"Electronic",
"2013-09-12T00:00:00",
"Chase Paymentech"
]
],
Any help is greatly appreciated. Thank you
The return value ARRAY(0x1f9fca98) indicates that $record->{"ns2:RefundTransactionTime"} contains an array reference. You probably want the first element: $record->{"ns2:RefundTransactionTime"}->[0].

Perl - How to sort array values by a given order?

In that How to sort a list with a given order?
We have discussed how to sort a list based on a given order using map and $_. Today I have another question.
I have the same orderby:
my #orderby = ( 'car', 'boat', 'chicken', 'cat', 'dog', 'mouse');
# or if it's better to the code:
my %orderby = ( 'car' => 0,
'boat' => 1,
'chicken' => 2,
'cat' => 3,
'dog' => 4,
'mouse' => 5);
And now I have the following that need to be ordered by orderby:
print Dumper \%toys;
$VAR = {
'animals' => [
[
'feather', 'cluck-2', 'chicken', 'white'
],
[
'bald', 'bark', 'dog', 'black stripes'
],
[
'feather', 'cluck-2', 'chicken', 'white'
]
],
'notanima' => [
[
'paited', 'motor', 'boat', 'red'
],
[
'painted', 'motor', 'car', 'blue on top'
]
]
};
The code need to sort using the 3 column based on orderby. You need to use the same ordery for animals and notanima.
After the rearrange, the $VAR will be:
$VAR = {
'animals' => [
[
'feather', 'cluck-2', 'chicken', 'white'
],
[
'feather', 'cluck-2', 'chicken', 'white'
],
[
'bald', 'bark', 'dog', 'black stripes'
]
],
'notanima' => [
[
'painted', 'motor', 'car', 'blue on top'
],
[
'paited', 'motor', 'boat', 'red'
]
]
};
order %toys{key} by orderby;
I have tried to change the map solution that #ikegami provided
my %counts; ++$counts{$_} for #list;
my #sorted = map { ($_) x ($counts{$_}||0) } #orderby;
but I didn't have success.
Do you guys have any ideas how can I achieve this objective?
Thx in advance!
Update!
I was trying to use the suggestion from ikegami, I have done this:
# that first foreach will give one ARRAY for animals and one ARRAY for notanima
foreach my $key (keys %toys)
{
# that one will give me access to the ARRAY referenced by the $key.
foreach my $toy_ref ($toys{$key})
{
my %orderby = map {$orderby[$_] => $_} 0..$#orderby;
my #sorted = sort { $orderby{$a} <=> $orderby{$b} } #{$toy_ref};
# my #sorted = sort { $orderby{$a} <=> $orderby{$b} } $toy_ref;
print Dumper #sorted;
}
}
First, this gives me the warning:
Use of uninitialized value in numeric comparison (<=>) at....
And the result of the sort for notanima (I will ignore animals, so the post will not be so big):
$VAR1 = [
'paited', 'motor', 'boat', 'red'
];
$VAR2 = [
'painted', 'motor', 'car', 'blue on top'
];
Based on orderby, the print order need to be:
$VAR1 = [
'painted', 'motor', 'car', 'blue on top'
];
$VAR2 = [
'paited', 'motor', 'boat', 'red'
];
Car need to come first.
What I have done wrong?
So you have 6 arrays to sort, so you'll need six calls to sort.
For each element of %toys,
For each element of the array referenced by that element of %toys,
Sort the referenced array as previously shown.
And please don't use the solution that was labeled "messes with people's mind".

Hash merge/concatenation

this is a dump of my hashes: %hash1
$VAR1 = {
abc => {
123 => [
'xx',
'yy',
'zy'
],
456 => [
'ab',
'cd',
'ef'
]
}
};
and the second one: %hash2
$VAR2 = {
def => {
659 => [
'wx',
'yg',
'kl'
],
456 => [
'as',
'sd',
'df'
]
},
abc => {
987 => [
'lk',
'dm',
'sd'
]
}
};
Now I want to merge these two hashes in a new hash, but if a key is duplicated (here 'abc'), the values should be appended, not replaced, so the keys should remain unique, and all the values should be retained as well. How can this be done in Perl?
The output should be as follows:
$VAR1 = {
def => {
659 => [
'wx',
'yg',
'kl'
],
456 => [
'as',
'sd',
'df'
]
},
abc => {
987 => [
'lk',
'dm',
'sd'
],
123 => [
'xx',
'yy',
'zy'
],
456 => [
'ab',
'cd',
'ef'
]
}
};
Use the CPAN modules Hash::Merge or Hash::Merge::Simple. The first is highly configurable and the second is very simple to use.
for my $x (keys(%h2)) {
for my $y (keys(%{ $h2{$x} })) {
push #{ $h1{$x}{$y} }, #{ $h2{$x}{$y} };
}
}
For the sample data provided, the following would perform the merging you describe:
my %merged = map {
$_ => {
%{$a{$_} // {}},
%{$b{$_} // {}}
}
} ( keys %a, keys %b );
Test:
use strict;
use warnings;
use Data::Dump 'dd';
my %a = (
abc => {
123 => [
'xx',
'yy',
'zy'
],
456 => [
'ab',
'cd',
'ef'
]
}
);
my %b = (
def => {
659 => [
'wx',
'yg',
'kl'
],
456 => [
'as',
'sd',
'df'
]
},
abc => {
987 => [
'lk',
'dm',
'sd'
]
}
);
my %merged = map {
$_ => {
%{$a{$_} // {}},
%{$b{$_} // {}}
}
} ( keys %a, keys %b );
dd \%merged;
# {
# abc => {
# 123 => ["xx", "yy", "zy"],
# 456 => ["ab", "cd", "ef"],
# 987 => ["lk", "dm", "sd"],
# },
# def => { 456 => ["as", "sd", "df"], 659 => ["wx", "yg", "kl"] },
# }
sub merge_hashes {
my ($h1, $h2) = #_;
foreach my $key (keys %$h2) {
if (!exists $h1->{$key} || ref($h1->{$key}) ne 'HASH' || ref($h2->{$key}) ne 'HASH') {
$h1->{$key} = $h2->{$key};
}
else {
merge_hashes($h1->{$key}, $h2->{$key});
}
}
}
merge_hashes(\%hash1, \%hash2);

Excel::Writer::XLSX::Chart::Line - Question

Is there a way to colorize the space between the to lines (Max. Temp and Min. Temp.) in the chart?
#!/usr/local/bin/perl
use warnings;
use 5.014;
use utf8;
use Excel::Writer::XLSX;
my $workbook = Excel::Writer::XLSX->new( 'spreadsheet.xlsx' ) or die $!;
my $worksheet = $workbook->add_worksheet( 'One' );
my $ref = [
[ 'Day', 'Max. Temp.', 'Min. Temp' ],
[ 1, -3.1, -6.4 ],
[ 2, -2.3, -7.2 ],
[ 3, -4.7, -6.8 ],
[ 4, -3.1, -9.5 ],
[ 5, 1.1, -11.4 ],
[ 6, 1.2, 0.3 ],
[ 7, 5.3, 0.7 ],
[ 8, 7.7, 1.5 ],
[ 9, 1.5, -2 ],
[ 10, 1, -1.1 ] ];
$worksheet->write_col( 0, 0, $ref );
my $chart = $workbook->add_chart( type => 'line' );
$chart->set_x_axis( name => 'Day' );
$chart->set_y_axis( name => 'Temperature (° Celsius)' );
$chart->set_style( 11 );
$chart->add_series(
values => [ 'One', 1, $#$ref, 1, 1 ],
name => '=One!$B$1',
);
$chart->add_series(
values => [ 'One', 1, $#$ref, 2, 2 ],
name => '=One!$C$1',
);
I don't think that this feature is available in Excel so it isn't possible in Excel::Writer::XLSX.
At least it isn't available as a single operation in Excel. There are some workarounds, for example here and here, but you don't currently have that level of control in Excel::Writer::XLSX.
Droplines/High-Low lines will be added in a future release. That might be an acceptable workaround.