- =head1 NAME
- LedgerSMB::Template::ODS Template support module for LedgerSMB
- =head1 SYNOPSIS
- OpenDocument Spreadsheet output.
- =head1 METHODS
- =over
- =item get_template ($name)
- Returns the appropriate template filename for this format. '.xlst' is the
- extension that was chosen for the templates.
- =item preprocess ($vars)
- Returns $vars.
- =item process ($parent, $cleanvars)
- Processes the template for text.
- =item postprocess ($parent)
- Returns the output filename.
- =back
- =head1 Copyright (C) 2007, The LedgerSMB core team.
- This work contains copyrighted information from a number of sources all used
- with permission.
- It is released under the GNU General Public License Version 2 or, at your
- option, any later version. See COPYRIGHT file for details. For a full list
- including contact information of contributors, maintainers, and copyright
- holders, see the CONTRIBUTORS file.
- =cut
- package LedgerSMB::Template::ODS;
- use strict;
- use warnings;
- use Error qw(:try);
- use Data::Dumper;
- use CGI::Simple::Standard qw(:html);
- use Template;
- use XML::Twig;
- use OpenOffice::OODoc;
- use LedgerSMB::Template::TTI18N;
- # SC: The ODS handlers need these vars in common
- my $ods;
- my $rowcount;
- my $currcol;
- my %celltype;
- # SC: The elements of the style table for regular styles and stack are
- # arrays where the stack name is the first element and the style
- # properties are the second. The name is used for setting styles,
- # while the properties are used in handling nested styles.
- my @style_stack; # stack of styles, 0 is active style
- my %style_table; # hash table for created styles
- # SC: Subtract 8 from the attribute to get the index
- # http://search.cpan.org/src/JMCNAMARA/Spreadsheet-WriteExcel-2.11/doc/palette.html
- my @colour = (odfColor(0, 0, 0), odfColor(255, 255, 255),
- odfColor(255, 0, 0), odfColor(0, 255, 0),
- odfColor(0, 0, 255), odfColor(255, 255, 0),
- odfColor(255, 0, 255), odfColor(0, 255, 255),
- odfColor(128, 0, 0), odfColor(0, 128, 0),
- odfColor(0, 0, 128), odfColor(128, 128, 0),
- odfColor(128, 0, 128), odfColor(0, 128, 128),
- odfColor(192, 192, 192), odfColor(128, 128, 128),
- odfColor(153, 153, 255), odfColor(153, 51, 102),
- odfColor(255, 255, 204), odfColor(204, 255, 255),
- odfColor(102, 0, 102), odfColor(255, 128, 128),
- odfColor(0, 102, 204), odfColor(204, 204, 255),
- odfColor(0, 0, 128), odfColor(255, 0, 255),
- odfColor(255, 255, 0), odfColor(0, 255, 255),
- odfColor(128, 0, 128), odfColor(120, 0, 0),
- odfColor(0, 128, 128), odfColor(0, 0, 255),
- odfColor(0, 204, 255), odfColor(204, 255, 255),
- odfColor(204, 255, 204), odfColor(255, 255, 153),
- odfColor(153, 204, 255), odfColor(255, 153, 204),
- odfColor(204, 153, 255), odfColor(192, 192, 192),
- odfColor(51, 102, 255), odfColor(51, 204, 204),
- odfColor(153, 204, 0), odfColor(255, 204, 0),
- odfColor(255, 153, 0), odfColor(255, 102, 0),
- odfColor(102, 102, 153), odfColor(150, 150, 150),
- odfColor(0, 51, 102), odfColor(51, 153, 102),
- odfColor(0, 51, 0), odfColor(51, 51, 0),
- odfColor(153, 51, 0), odfColor(153, 51, 102),
- odfColor(51, 51, 153), odfColor(51, 51, 51),
- );
- my %colour_name = ('black' => $colour[0], 'white' => $colour[1],
- 'red' => $colour[2], 'lime' => $colour[3],
- 'blue' => $colour[4], 'yellow' => $colour[5],
- 'magenta' => $colour[6], 'cyan' => $colour[7],
- 'brown' => $colour[8], 'green' => $colour[9],
- 'navy' => $colour[10], 'purple' => $colour[12],
- 'silver' => $colour[14], 'gray' => $colour[15],
- 'grey' => $colour[15], 'orange' => $colour[45],
- );
- my @line_width = ('none', '0.018cm solid', '0.035cm solid',
- '0.018cm dashed', '0.018cm dotted', '0.141cm solid',
- '0.039cm double', '0.002cm solid'
- );
- sub _worksheet_handler {
- $rowcount = -1;
- $currcol = 0;
- my $rows = $_->{att}->{rows};
- my $columns = $_->{att}->{columns};
- $rows ||= 1000;
- $columns ||= 52;
- my $sheet;
- if ($_->is_first_child) {
- $sheet = $ods->getTable(0, $rows, $columns);
- $ods->renameTable($sheet, $_->{att}->{name});
- } else {
- $sheet = $ods->appendTable($_->{att}->{name}, $rows, $columns);
- }
- }
- sub _row_handler {
- $rowcount++;
- $currcol = 0;
- }
- sub _cell_handler {
- my $cell = $ods->getCell(-1, $rowcount, $currcol);
-
- if (@style_stack and $celltype{$style_stack[0][0]}) {
- $ods->cellValueType($cell, $celltype{$style_stack[0][0]}[0]);
- } elsif ($_->{att}->{type}) {
- my $type = $_->{att}->{type};
- if ($type =~ /^(string|blank|url)$/i) {
- $ods->cellValueType($cell, 'string');
- } elsif ($type =~ /^(number|formula)$/i) {
- $ods->cellValueType($cell, 'float');
- }
- }
- $ods->cellValue($cell, $_->{att}->{text});
- if (@style_stack) {
- $ods->cellStyle($cell, $style_stack[0][0]);
- }
- $currcol++;
- }
- sub _formula_handler {
- my $cell = $ods->getCell(-1, $rowcount, $currcol);
-
- if (@style_stack and $celltype{$style_stack[0][0]}) {
- $ods->cellValueType($cell, $celltype{$style_stack[0][0]}[0]);
- } elsif ($_->{att}->{type}) {
- my $type = $_->{att}->{type};
- if ($type =~ /^(string|blank|url)$/i) {
- $ods->cellValueType($cell, 'string');
- } elsif ($type =~ /^(number|formula)$/i) {
- $ods->cellValueType($cell, 'float');
- }
- }
- $ods->cellFormula($cell, "oooc:=$_->{att}->{text}");
- if (@style_stack) {
- $ods->cellStyle($cell, $style_stack[0][0]);
- }
- $currcol++;
- }
- sub _border_set {
- my ($format, $properties, $border) = @_;
- my $edge = $border;
- $edge =~ s/^border-?//;
- my $val;
- if ($edge) {
- $val = $format->{att}{$edge};
- } else {
- $val = $format->{att}{'border'};
- }
- if ($properties->{cell}{"fo:$border"}){
- $properties->{cell}{"fo:$border"} =~ s/^.* (\#......)$/$val $1/;
- } else {
- $properties->{cell}{"fo:$border"} = "$line_width[$val] #000000";
- }
- if ($edge and $format->{att}->{"${edge}_color"}) {
- my $colour = $format->{att}->{"${edge}_color"};
- if ($colour =~ /^\d+$/) {
- $colour = $colour[$colour];
- } elsif ($colour !~ /^\#......$/) {
- $colour = $colour_name{$colour};
- }
- $properties->{cell}{"fo:$border"} =~ s/^(.*) \#......$/$1 $colour/;
- } elsif ($format->{att}->{border_color}) {
- my $colour = $format->{att}->{"${edge}_color"};
- if ($colour =~ /^\d+$/) {
- $colour = $colour[$colour];
- } elsif ($colour !~ /^\#......$/) {
- $colour = $colour_name{$colour};
- }
- $properties->{cell}{"fo:$border"} =~ s/^(.*) \#......$/$1 $colour/;
- }
- }
- sub _prepare_float {
- my ($style) = shift;
- my %properties;
- my @sides = split /\./, $style;
- if ($#sides == 1) { # decimal places
- $properties{'number:decimal-places'} = length $sides[1];
- } else {
- $properties{'number:decimal-places'} = 0;
- }
- $properties{'number:min-integer-digits'} = length($sides[0] =~ /0+$/);
- $properties{'number:grouping'} = 'true' if $sides[0] =~ /.,...$/;
- \%properties;
- }
- sub _prepare_fraction {
- my ($style) = shift;
- my %properties;
- my @sides = split /[ \/]/, $style;
- $properties{'number:min-integer-digits'} = length($sides[0] =~ /0+$/);
- $properties{'number:min-numerator-digits'} = length($sides[1]);
- $properties{'number:min-denominator-digits'} = length($sides[2]);
- \%properties;
- }
- sub _create_positive_style {
- my ($name, $type, $base) = @_;
- my $pstyle = $ods->createStyle(
- $name,
- namespace => 'number',
- type => $type,
- properties => $base,
- references => {
- 'style:volatile' => 'true',
- },
- );
- $pstyle->insert_new_elt('last-child',
- 'number:text', {}, ' ');
- }
- sub _format_handler {
- my ($t, $format) = @_;
- my $style = sprintf "ce%d", (scalar (keys %style_table) + 1);
- my @extras;
- # SC: There are multiple types of properties that can be associated
- # with a style. However, the OO::OOD style creation code appears
- # to only allow for a single type to be added to the style at a
- # time. As a result, %properties is split into property groupings
- # to allow for each group to get the correct type.
- my %properties;
- if (@style_stack) {
- %properties = %{$style_stack[0][1]};
- if ($celltype{$style_stack[0][0]}) {
- $celltype{$style} = $celltype{$style_stack[0][0]};
- @extras = ('references', {
- 'style:data-style-name' => $celltype{$style}[1]
- });
- }
- }
- &_border_set(\%properties, $format, 'border') if $format->{att}->{border};
- while (my ($attr, $val) = each %{$format->{att}}) {
- if ($attr eq 'bottom') {
- &_border_set($format, \%properties, 'border-bottom');
- } elsif ($attr eq 'top') {
- &_border_set($format, \%properties, 'border-top');
- } elsif ($attr eq 'left') {
- &_border_set($format, \%properties, 'border-left');
- } elsif ($attr eq 'right') {
- &_border_set($format, \%properties, 'border-right');
- } elsif ($attr eq 'bg_color' or $attr eq 'bg_colour') {
- if ($val =~ /^\d+$/) {
- $properties{cell}{'fo:background-color'} =
- $colour[$val - 8];
- } elsif ($val =~ /^\#[0-9A-Fa-f]{6}$/) {
- $properties{cell}{'fo:background-color'} = $val;
- } else {
- $properties{cell}{'fo:background-color'} =
- $colour_name{$val};
- }
- } elsif ($attr eq 'color' or $attr eq 'colour') {
- if ($val =~ /^\d+$/) {
- $properties{text}{'fo:color'} =
- $colour[$val - 8];
- } elsif ($val =~ /^\#[0-9A-Fa-f]{6}$/) {
- $properties{text}{'fo:color'} = $val;
- } else {
- $properties{text}{'fo:color'} =
- $colour_name{$val};
- }
- } elsif ($attr eq 'align') {
- if (lc $val eq 'right') {
- $properties{paragraph}{'fo:text-align'} = 'end';
- } elsif (lc $val eq 'left') {
- $properties{paragraph}{'fo:text-align'} = 'start';
- } else {
- $properties{paragraph}{'fo:text-align'} = $val;
- }
- } elsif ($attr eq 'valign') {
- # takes top, vcenter, bottom, or vjustify
- # needs top, middle, or bottom
- if ($val =~ /^v/i) {
- $properties{paragraph}{'style:vertical-align'} = 'middle';
- } else {
- $properties{paragraph}{'style:vertical-align'} = $val;
- }
- } elsif ($attr eq 'hidden') {
- if ($properties{cell}{'style:cell-protect'} and !$val) {
- delete $properties{cell}{'style:cell-protect'};
- } elsif ($val) {
- $properties{cell}{'style:cell-protect'} = 'formula-hidden';
- }
- } elsif ($attr eq 'font') {
- $properties{text}{'style:font-name'} = $val;
- } elsif ($attr eq 'size') {
- $properties{text}{'fo:font-size'} = "${val}pt";
- } elsif ($attr eq 'bold') {
- if ($properties{text}{'fo:font-weight'} and !$val) {
- delete $properties{text}{'fo:font-weight'};
- } elsif ($val) {
- $properties{text}{'fo:font-weight'} = 'bold';
- }
- } elsif ($attr eq 'italic') {
- if ($properties{text}{'fo:font-style'} and !$val) {
- delete $properties{text}{'fo:font-style'};
- } elsif ($val) {
- $properties{text}{'fo:font-style'} = 'italic';
- }
- } elsif ($attr eq 'font_strikeout') {
- if (!$val) {
- $properties{text}{'style:text-line-through-type'} = 'none';
- } elsif ($val) {
- $properties{text}{'style:text-line-through-type'} = 'single';
- }
- } elsif ($attr eq 'font_shadow') {
- if ($properties{text}{'fo:text-shadow'} and !$val) {
- delete $properties{text}{'fo:text-shadow'};
- } elsif ($val) {
- $properties{text}{'fo:text-shadow'} = '2pt';
- }
- } elsif ($attr eq 'font_outline') {
- if ($properties{text}{'style:text-outline'} and !$val) {
- delete $properties{text}{'style:text-outline'};
- } elsif ($val) {
- $properties{text}{'style:text-outline'} = 'true';
- }
- } elsif ($attr eq 'shrink') {
- if ($properties{cell}{'style:shrink-to-fit'} and !$val) {
- delete $properties{cell}{'style:shrink-to-fit'};
- } elsif ($val) {
- $properties{cell}{'style:shrink-to-fit'} = 'true';
- }
- } elsif ($attr eq 'text_wrap') {
- if (!$val) {
- $properties{cell}{'style:wrap-option'} = 'no-wrap';
- } else {
- $properties{text}{'style:wrap-option'} = 'wrap';
- }
- } elsif ($attr eq 'text_justlast') {
- if ($properties{paragraph}{'fo:text-align-last'} and !$val) {
- delete $properties{paragraph}{'fo:text-align-last'};
- } elsif ($val) {
- $properties{paragraph}{'fo:text-align-last'} = 'justify';
- }
- } elsif ($attr eq 'num_format') {
- #SC: Number formatting is when I hit the point of,
- # "Screw the OO::OOD API, XML::Twig is simpler".
- # @children's elements are passed right into the
- # style via XML::Twig::Elt's insert_new_elt. The
- # OO:OOD API, while decent enough for the text
- # styles, is not so pleasant with complex number
- # styles.
- my @children;
- my %nproperties;
- my @nextras;
- my $nstyle;
- my $fval = sprintf 'N%02d', $val;
- @extras = ('references', {'style:data-style-name' => $fval});
- if ($style_table{$fval}) {
- # pass through
- } elsif ($val == 0) {
- $celltype{$style} = 'float';
- } elsif ($val == 1) {
- $celltype{$style} = ['float', 'N01'];
- $nstyle = 'number-style';
- %nproperties = %{&_prepare_float('0')}
- } elsif ($val == 2) {
- $celltype{$style} = ['float', 'N02'];
- $nstyle = 'number-style';
- %nproperties = %{&_prepare_float('0.00')}
- } elsif ($val == 3) {
- $celltype{$style} = ['float', 'N03'];
- $nstyle = 'number-style';
- %nproperties = %{&_prepare_float('#,##0')}
- } elsif ($val == 4) {
- $celltype{$style} = ['float', "N04"];
- $nstyle = 'number-style';
- %nproperties = %{&_prepare_float('#,##0.00')}
- } elsif ($val == 5) { ## ($#,##0_);($#,##0)
- } elsif ($val == 6) {
- $celltype{$style} = 'currency';
- } elsif ($val == 7) {
- $celltype{$style} = 'currency';
- } elsif ($val == 8) {
- $celltype{$style} = 'currency';
- } elsif ($val == 9) { ## 0%
- $celltype{$style} = ['percentage', "N09"];
- $nstyle = 'percentage-style';
- %nproperties = %{&_prepare_float('0')};
- push @children, ['number:text', {}, '%'];
- } elsif ($val == 10) { ## 0.00%
- $celltype{$style} = ['percentage', "N10"];
- $nstyle = 'percentage-style';
- %nproperties = %{&_prepare_float('0.00')};
- push @children, ['number:text', {}, '%'];
- } elsif ($val == 11) { ## 0.00E+00
- $celltype{$style} = ['float', "N11"];
- $nstyle = 'number-style';
- push @children, ['number:scientific-number', {
- %{&_prepare_float('0.00')},
- 'number:min-exponent-digits' => 2
- }];
- } elsif ($val == 12) { ## # ?/?
- $celltype{$style} = ['float', "N12"];
- $nstyle = 'number-style';
- push @children, ['number:fraction',
- %{&_prepare_fraction('# ?/?')}];
- } elsif ($val == 13) { ## # ??/??
- $celltype{$style} = ['float', "N13"];
- $nstyle = 'number-style';
- push @children, ['number:fraction',
- %{&_prepare_fraction('# ??/??')}];
- } elsif ($val == 14) { ## m/d/yy
- $celltype{$style} = ['date', "N14"];
- $nstyle = 'date-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:month'],
- ['number:text', {}, '/'],
- ['number:day'],
- ['number:text', {}, '/'],
- ['number:year'],
- );
- } elsif ($val == 15) { ## d-mmm-yy
- $celltype{$style} = ['date', "N15"];
- $nstyle = 'date-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:day'],
- ['number:text', {}, '-'],
- ['number:month', {
- 'number:textual' => 'true'}],
- ['number:text', {}, '-'],
- ['number:year'],
- );
- } elsif ($val == 16) { ## d-mmm
- $celltype{$style} = ['date', "N16"];
- $nstyle = 'date-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:day'],
- ['number:text', {}, '-'],
- ['number:month', {
- 'number:textual' => 'true'}],
- );
- } elsif ($val == 17) { ## mmm-yy
- $celltype{$style} = ['date', "N17"];
- $nstyle = 'date-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:month', {
- 'number:textual' => 'true'}],
- ['number:text', {}, '-'],
- ['number:year'],
- );
- } elsif ($val == 18) { ## h:mm AM/PM
- $celltype{$style} = ['time', "N18"];
- $nstyle = 'time-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:hours'],
- ['number:text', {}, ':'],
- ['number:minutes',
- {'number:style' => 'long'}],
- ['number:text', {}, ' '],
- ['number:am-pm']
- );
- } elsif ($val == 19) { ## h:mm:ss AM/PM
- $celltype{$style} = ['time', "N19"];
- $nstyle = 'time-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:hours'],
- ['number:text', {}, ':'],
- ['number:minutes',
- {'number:style' => 'long'}],
- ['number:text', {}, ':'],
- ['number:seconds',
- {'number:style' => 'long'}],
- ['number:text', {}, ' '],
- ['number:am-pm']
- );
- } elsif ($val == 20) { ## h:mm
- $celltype{$style} = ['time', "N20"];
- $nstyle = 'time-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:hours'],
- ['number:text', {}, ':'],
- ['number:minutes',
- {'number:style' => 'long'}],
- );
- } elsif ($val == 21) { ## h:mm:ss
- $celltype{$style} = ['time', "N21"];
- $nstyle = 'time-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:hours'],
- ['number:text', {}, ':'],
- ['number:minutes',
- {'number:style' => 'long'}],
- ['number:text', {}, ':'],
- ['number:seconds',
- {'number:style' => 'long'}],
- );
- } elsif ($val == 22) { ## m/d/yy h:mm
- $celltype{$style} = ['date', "N22"];
- $nstyle = 'date-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:month'],
- ['number:text', {}, '/'],
- ['number:day'],
- ['number:text', {}, '/'],
- ['number:year'],
- ['number:text', {}, ' '],
- ['number:hours'],
- ['number:text', {}, ':'],
- ['number:minutes',
- {'number:style' => 'long'}],
- );
- } elsif ($val == 37) { ## (#,##0_);(#,##0)
- $celltype{$style} = ['float', "N37"];
- $nstyle = 'number-style';
- my %base = (
- 'number:min-integer-digits' => 1,
- 'number:grouping' => 'true',
- );
- @children = (
- ['number:text', {}, '('],
- ['number:number', \%base],
- ['number:text', {}, ')'],
- ['style:map', {
- 'style:condition' => 'value()>=0',
- 'style:apply-style-name' => "NP37",
- }],
- );
-
- &_create_positive_style("NP37",
- $nstyle, \%base);
- } elsif ($val == 38) { ## (#,##0_);[Red](#,##0)
- $celltype{$style} = ['float', "N38"];
- $nstyle = 'number-style';
- my %base = %{&_prepare_float('#,##0')};
- @children = (
- ['style:text-properties',
- {'fo:color' => '#ff0000'}],
- ['number:text', {}, '('],
- ['number:number', \%base],
- ['number:text', {}, ')'],
- ['style:map',{
- 'style:condition' => 'value()>=0',
- 'style:apply-style-name' => "NP38",
- }]
- );
-
- &_create_positive_style("NP38",
- $nstyle, \%base);
- } elsif ($val == 39) { ## (#,##0.00_);(#,##0.00)
- $celltype{$style} = ['float', "N39"];
- $nstyle = 'number-style';
- my %base = %{&_prepare_float('#,##0.00')};
- @children = (
- ['number:text', {}, '('],
- ['number:number', \%base],
- ['number:text', {}, ')'],
- ['style:map',{
- 'style:condition' => 'value()>=0',
- 'style:apply-style-name' => "NP39",
- }]
- );
-
- &_create_positive_style("NP39",
- $nstyle, \%base);
- } elsif ($val == 40) { ## (#,##0.00_);[Red](#,##0.00)
- $celltype{$style} = ['float', "N40"];
- $nstyle = 'number-style';
- my %base = %{&_prepare_float('#,##0.00')};
- @children = (
- ['style:text-properties',
- {'fo:color' => '#ff0000'}],
- ['number:text', {}, '('],
- ['number:number', \%base],
- ['number:text', {}, ')'],
- ['style:map', {
- 'style:condition' => 'value()>=0',
- 'style:apply-style-name' => "NP40",
- }],
- );
- &_create_positive_style("NP40",
- $nstyle, \%base);
- } elsif ($val == 41) {
- $celltype{$style} = 'float';
- } elsif ($val == 42) {
- $celltype{$style} = 'currency';
- } elsif ($val == 43) {
- $celltype{$style} = 'float';
- } elsif ($val == 44) {
- $celltype{$style} = 'currency';
- } elsif ($val == 45) { ## mm:ss
- $celltype{$style} = ['time', "N45"];
- $nstyle = 'time-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:minutes',
- {'number:style' => 'long'}],
- ['number:text', {}, ':'],
- ['number:seconds',
- {'number:style' => 'long'}],
- );
- } elsif ($val == 46) { ## [h]:mm:ss
- $celltype{$style} = ['time', "N46"];
- $nstyle = 'time-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true',
- 'number:truncate-on-overflow' => 'false'});
- @children = (
- ['number:hours', {}],
- ['number:text', {}, ':'],
- ['number:minutes',
- {'number:style' => 'long'}],
- ['number:text', {}, ':'],
- ['number:seconds',
- {'number:style' => 'long'}],
- );
- } elsif ($val == 47) { ## mm:ss.0
- $celltype{$style} = ['time', "N47"];
- $nstyle = 'time-style';
- @nextras = ('references' => {
- 'number:automatic-order' => 'true'});
- @children = (
- ['number:minutes',
- {'number:style' => 'long'}],
- ['number:text', {}, ':'],
- ['number:seconds',
- {'number:style' => 'long',
- 'number:decimal-places' => 1}],
- );
- } elsif ($val == 48) { ## ##0.0E+0
- $celltype{$style} = ['float', "N48"];
- $nstyle = 'number-style';
- %nproperties = ();
- push @children, ['number:scientific-number',
- {%{&_prepare_float(0.0)},
- 'number:min-exponent-digits' => 1
- }];
- } elsif ($val == 49) {
- $celltype{$style} = 'string';
- }
- # $nstyle is set on new styles
- if ($nstyle) {
- my $cstyle = $ods->createStyle(
- $celltype{$style}[1],
- namespace => 'number',
- type => $nstyle,
- properties => \%nproperties,
- @nextras,
- );
- for my $child (@children) {
- $cstyle->insert_new_elt('last_child',
- @$child);
- }
- $style_table{$fval} = 1;
- }
- }
- }
- # Maintain a hash table to keep the final style list size down
- $Data::Dumper::Sortkeys = 1;
- my $mystyle = Digest::MD5::md5_hex(Dumper(\%properties, \@extras));
- if (!$style_table{$mystyle}) {
- $ods->createStyle(
- $style,
- family => 'table-cell',
- properties => $properties{cell},
- @extras,
- );
- $ods->updateStyle(
- $style,
- properties => {
- -area => 'text',
- %{$properties{text}}
- }
- ) if $properties{text};
- $ods->updateStyle(
- $style,
- properties => {
- -area => 'paragraph',
- %{$properties{paragraph}}
- }
- ) if $properties{paragraph};
- $style_table{$mystyle} = [$style, \%properties];
- }
- unshift @style_stack, $style_table{$mystyle};
- }
- sub _named_format {
- my ($name, $t, $format) = @_;
- $format->{att}{$name} = 1;
- &_format_handler($t, $format);
- }
- sub _format_cleanup_handler {
- my ($t, $format) = @_;
- shift @style_stack;
- }
- sub _ods_process {
- my ($filename, $template) = @_;
- $ods = ooDocument(file => $filename, create => 'spreadsheet');
-
- my $parser = XML::Twig->new(
- start_tag_handlers => {
- worksheet => \&_worksheet_handler,
- row => \&_row_handler,
- cell => \&_cell_handler,
- formula => \&_formula_handler,
- format => \&_format_handler,
- bold => sub { &_named_format('bold', @_) },
- hidden => sub { &_named_format('hidden', @_) },
- italic => sub { &_named_format('italic', @_) },
- shadow => sub { &_named_format('shadow', @_) },
- strikeout => sub { &_named_format('strikeout', @_) },
- },
- twig_handlers => {
- format => \&_format_cleanup_handler,
- bold => \&_format_cleanup_handler,
- hidden => \&_format_cleanup_handler,
- italic => \&_format_cleanup_handler,
- shadow => \&_format_cleanup_handler,
- strikeout => \&_format_cleanup_handler,
- }
- );
- $parser->parse($template);
- $parser->purge;
- $ods->save;
- }
- sub get_template {
- my $name = shift;
- return "${name}.odst";
- }
- sub preprocess {
- my $rawvars = shift;
- my $vars;
- my $type = ref $rawvars;
- #XXX fix escaping function
- return $rawvars if $type =~ /^LedgerSMB::Locale/;
- return unless defined $rawvars;
- if ( $type eq 'ARRAY' ) {
- for (@{$rawvars}) {
- push @{$vars}, preprocess( $_ );
- }
- } elsif (!$type) {
- return escapeHTML($rawvars);
- } elsif ($type eq 'SCALAR') {
- return escapeHTML($$rawvars);
- } else { # Hashes and objects
- for ( keys %{$rawvars} ) {
- $vars->{preprocess($_)} = preprocess( $rawvars->{$_} );
- }
- }
-
- return $vars;
- }
- sub process {
- my $parent = shift;
- my $cleanvars = shift;
- my $template;
- my $source;
- my $tempdir = ${LedgerSMB::Sysconfig::tempdir};
- my $output = '';
- $parent->{outputfile} ||= "$tempdir/$parent->{template}-output-$$";
- if (ref $parent->{template} eq 'SCALAR') {
- $source = $parent->{template};
- } elsif (ref $parent->{template} eq 'ARRAY') {
- $source = join "\n", @{$parent->{template}};
- } else {
- $source = get_template($parent->{template});
- }
- $template = Template->new({
- INCLUDE_PATH => $parent->{include_path},
- START_TAG => quotemeta('<?lsmb'),
- END_TAG => quotemeta('?>'),
- DELIMITER => ';',
- DEBUG => ($parent->{debug})? 'dirs': undef,
- DEBUG_FORMAT => '',
- }) || throw Error::Simple Template->error();
- if (not $template->process(
- $source,
- {%$cleanvars, %$LedgerSMB::Template::TTI18N::ttfuncs,
- 'escape' => \&preprocess},
- \$output, binmode => ':utf8')) {
- throw Error::Simple $template->error();
- }
- &_ods_process("$parent->{outputfile}.ods", $output);
- $parent->{mimetype} = 'application/vnd.oasis.opendocument.spreadsheet';
- }
- sub postprocess {
- my $parent = shift;
- $parent->{rendered} = "$parent->{outputfile}.ods";
- return $parent->{rendered};
- }
- 1;
|