Spreadsheet-ReadSXC-0.24/0000755000175000017500000000000013547152475014516 5ustar corioncorionSpreadsheet-ReadSXC-0.24/MANIFEST.SKIP0000755000175000017500000000052313547152473016415 0ustar corioncorion^\.git\/ ^\.gitignore maint ^tags$ .last_cover_stats .travis.yml .appveyor.yml ^\.github Makefile$ ^blib ^pm_to_blib ^.*.bak ^.*.old ^t.*sessions ^t/.*\.disabled$ ^cover_db ^.*\.log ^.*\.swp$ ^jar/ ^cpan/ ^MYMETA ^.releaserc ^.*.cmd ^chrome-versions ^Spreadsheet-ReadSXC-* ^frame-\d+.png ^demo/ ^.*\.mkdn$ ^.*\.patch$ ^\.prove$ ^t/.*.zip$ Spreadsheet-ReadSXC-0.24/Makefile.PL0000644000175000017500000001577013547152473016500 0ustar corioncorion# -*- mode: perl; c-basic-offset: 4; indent-tabs-mode: nil; -*- use strict; use ExtUtils::MakeMaker qw(WriteMakefile); # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. # Normalize version strings like 6.30_02 to 6.3002, # so that we can do numerical comparisons on it. my $eumm_version = $ExtUtils::MakeMaker::VERSION; $eumm_version =~ s/_//; my $module = 'Spreadsheet::ReadSXC'; (my $main_file = "lib/$module.pm" ) =~ s!::!/!g; (my $distbase = $module) =~ s!::!-!g; my $distlink = $distbase; my @tests = map { glob $_ } 't/*.t', 't/*/*.t'; my %module = ( NAME => $module, AUTHOR => q{Max Maischein }, VERSION_FROM => $main_file, ABSTRACT_FROM => $main_file, META_MERGE => { "meta-spec" => { version => 2 }, resources => { repository => { web => "https://github.com/Corion/$distlink", url => "git://github.com/Corion/$distlink.git", type => 'git', }, bugtracker => "https://rt.cpan.org/Public/Dist/Display.html?Name=$distbase", license => "https://dev.perl.org/licenses/", }, dynamic_config => 0, # we promise to keep META.* up-to-date x_static_install => 1, # we are pure Perl and don't do anything fancy }, MIN_PERL_VERSION => '5.008', # we want/need a special binmode form 'LICENSE'=> 'perl', PL_FILES => {}, BUILD_REQUIRES => { 'ExtUtils::MakeMaker' => 0, }, PREREQ_PM => { 'Carp' => 0, 'Archive::Zip' => '1.34', # for seekable streams, verified thanks to Backpan 'PerlIO::gzip' => 0, 'XML::Parser' => 0, }, TEST_REQUIRES => { 'Test::More' => 0, 'File::Temp' => 0, 'File::Basename' => 0, 'Data::Dumper' => 0, }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, clean => { FILES => "$distbase-*" }, test => { TESTS => join( ' ', @tests ) }, ); # This is so that we can do # require 'Makefile.PL' # and then call get_module_info sub get_module_info { %module } if( ! caller ) { # I should maybe use something like Shipwright... regen_README($main_file); regen_EXAMPLES() if -d 'examples'; WriteMakefile1(get_module_info); }; 1; sub WriteMakefile1 { #Written by Alexandr Ciornii, version 0.21. Added by eumm-upgrade. my %params=@_; my $eumm_version=$ExtUtils::MakeMaker::VERSION; $eumm_version=eval $eumm_version; die "EXTRA_META is deprecated" if exists $params{EXTRA_META}; die "License not specified" if not exists $params{LICENSE}; if ($params{BUILD_REQUIRES} and $eumm_version < 6.5503) { #EUMM 6.5502 has problems with BUILD_REQUIRES $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{BUILD_REQUIRES}} }; delete $params{BUILD_REQUIRES}; } if ($params{TEST_REQUIRES} and $eumm_version < 6.64) { $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{TEST_REQUIRES}} }; delete $params{TEST_REQUIRES}; } delete $params{CONFIGURE_REQUIRES} if $eumm_version < 6.52; delete $params{MIN_PERL_VERSION} if $eumm_version < 6.48; delete $params{META_MERGE} if $eumm_version < 6.46; delete $params{META_ADD} if $eumm_version < 6.46; delete $params{LICENSE} if $eumm_version < 6.31; delete $params{AUTHOR} if $] < 5.005; delete $params{ABSTRACT_FROM} if $] < 5.005; delete $params{BINARY_LOCATION} if $] < 5.005; WriteMakefile(%params); } sub regen_README { # README is the short version that just tells people what this is # and how to install it eval { # Get description my $readme = join "\n", pod_section($_[0], 'NAME', 'no heading' ), pod_section($_[0], 'DESCRIPTION' ), <new(); # Read POD from Module.pm and write to README $parser->parse_from_file($_[0]); my $readme_mkdn = <as_markdown; [![Travis Build Status](https://travis-ci.org/Corion/$distlink.svg?branch=master)](https://travis-ci.org/Corion/$distlink) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/Corion/$distlink?branch=master&svg=true)](https://ci.appveyor.com/project/Corion/$distlink) STATUS update_file( 'README.mkdn', $readme_mkdn ); }; } sub pod_section { my( $filename, $section, $remove_heading ) = @_; open my $fh, '<', $filename or die "Couldn't read '$filename': $!"; my @section = grep { /^=head1\s+$section/.../^=/ } <$fh>; # Trim the section if( @section ) { pop @section if $section[-1] =~ /^=/; shift @section if $remove_heading; pop @section while @section and $section[-1] =~ /^\s*$/; shift @section while @section and $section[0] =~ /^\s*$/; }; @section = map { $_ =~ s!^=\w+\s+!!; $_ } @section; return join "", @section; } sub regen_EXAMPLES { my $perl = $^X; if ($perl =~/\s/) { $perl = qq{"$perl"}; }; (my $example_file = $main_file) =~ s!\.pm$!/Examples.pm!; my $examples = `$perl -w examples/gen_examples_pod.pl`; if ($examples) { warn "(Re)Creating $example_file\n"; $examples =~ s/\r\n/\n/g; update_file( $example_file, $examples ); }; }; sub update_file { my( $filename, $new_content ) = @_; my $content; if( -f $filename ) { open my $fh, '<', $filename or die "Couldn't read '$filename': $!"; binmode $fh; local $/; $content = <$fh>; }; if( $content ne $new_content ) { if( open my $fh, '>', $filename ) { binmode $fh; print $fh $new_content; } else { warn "Couldn't (re)write '$filename': $!"; }; }; } Spreadsheet-ReadSXC-0.24/t/0000755000175000017500000000000013547152475014761 5ustar corioncorionSpreadsheet-ReadSXC-0.24/t/03-error-handling.t0000644000175000017500000000223513547152473020301 0ustar corioncorion#!perl use strict; use Test::More tests => 10; use File::Basename 'dirname'; use Spreadsheet::ReadSXC; my $d = dirname($0); my $sxc_file = "$d/t.sxc"; sub dies_ok { my( $code, $error_msg, $name ) = @_; $name ||= $error_msg; my $old_handler = Archive::Zip::setErrorHandler(sub {}); my $died = eval { $code->(); 1 }; my $err = $@; is $died, undef, $name; like $err, $error_msg, $name; Archive::Zip::setErrorHandler($old_handler); }; is Spreadsheet::ReadSXC::read_sxc('no-such-file.sxc'), undef, "Default silent API"; dies_ok sub { Spreadsheet::ReadSXC::read_sxc('no-such-file.sxc', { StrictErrors => 1 }) }, qr/Couldn't open 'no-such-file.sxc':/,"Non-existent file"; dies_ok sub { Spreadsheet::ReadSXC::read_sxc_fh(undef) }, qr//, "undef filehandle"; is Spreadsheet::ReadSXC::read_xml_file('no-such-file.xml'), undef, "Default silent API"; dies_ok sub { Spreadsheet::ReadSXC::read_xml_file('no-such-file.xml', { StrictErrors => 1 })}, qr/Couldn't open no-such-file.xml:/, "Non-existent XML file"; dies_ok sub { Spreadsheet::ReadSXC::read_xml_string('')}, qr/no element found at line 1/, "Invalid XML string"; Spreadsheet-ReadSXC-0.24/t/00-load.t0000755000175000017500000000057513547152473016312 0ustar corioncorion#!perl -T use strict; use warnings; use Test::More tests => 1; require './Makefile.PL'; my %module = get_module_info(); my $module = $module{ NAME }; require_ok( $module ); diag( sprintf "Testing %s %s, Perl %s", $module, $module->VERSION, $] ); for (sort grep /\.pm\z/, keys %INC) { s/\.pm\z//; s!/!::!g; eval { diag(join(' ', $_, $_->VERSION || '')) }; } Spreadsheet-ReadSXC-0.24/t/t.sxc0000644000175000017500000001363513547152473015751 0ustar corioncorionPKG2Emimetypeapplication/vnd.sun.xml.calcPKG2 content.xmlXQo6~߯ഢ؀ȴi\E]1diH@QvioÀA_HJKJRg;#H*ђp`uZm . uq~a~ztvr 9shNP%| l"iX (B7,d-Wְ_8qhBʨ10|nY[A,~3^#2W9dՈVyZZ}1~E@r.W> /kZA$Q̄HRK.s΂iĦ"w30$I>8o'F 537+;Ox`őčnTVOzvþF:$סK|${ ͹IzJ",l)?nL}>{hײ; q`z~灌wP8Qd8@MEx-E`Qt:-klj\ZL^$Pa"&8K>U[NRI$d]=?K8I17YF*dti( eNb_VhtDWU\2o3:誐4<, ]"aݝ/Vf.A*x6#:'/Rі=őOuLs!9}xiLڷyNdYrγzP(uKa.txJn.Vu0BIY?~ԓ/%QawnNfZ7g2k"MvہϷ㪟GA_>tS^Jbo&L3UMddL@+٥VT_sW )4(^5ԅ,-.G\R]e؁*n4O-ב$ek;ё {ՋΞq*djy}~xu7:ox;WaK5\3郒 ?R--,J׿=4gcDwӟ]>8er+2tlN)u`qD Atr+pť{pPK'FPKG2 styles.xmlXnF}Wl HS8F 9bHچ˥e?5P I"M9svvPニ8Bg%k8p-D4 'c{:y7G'$eD24;}0}YLIRsxrY`yB67D`y5$ʉ"wj뺎~\ ы6VpEK0^D/r[!{{{\D4y43=x.ey? Q1l`d;(bM(կiE{K@i﹆XENKYs?ʼ" j~cdL8ĺSݫl ϣc4CW\_zOd:[o?eo! o'H ?}b:Ń5BgֵQ 9:M(U׵ܧ6+5$ /X-  Myj >"}Md.Xttjo!~\zpOlI:Gˈtk(iQ㥺mDUS XGvadkT󬟒'aC@D |'·ZflQ+! . ob5 Y۾4"]uP-1 Ҵ\M h&p"[p0cn3Ԓ|Z$ɮVc92O1Mlŀ\ح8sн;shY^|{x 9S5k)Ni^N*S.3X`WbÈסx*{3:vD F9XtL &j8QNnݾ{NqF&v +i;ԧra.TS] |xE~&ż|#wмP#dΓpAA%TǽM*k . 08B xAɪNJ%TSV!TK ѩ4vr.ǜ^T$fl(wx&kǷ zAQuGPN|}V#QF̙RhcYVkm;4s]oRٵcx-o54?KH1̾ YU j]taw_ +9!"2x]z%4.'按1j:{29)^R@~9]?FhnB8 d[,P,o)YXA@IugY+Y{6n#FգS]lAIH|,f0=QV{ebe OpenOffice.org 1.1.2 (Unix)Christoph Terhechte2005-04-30T17:20:51Christoph Terhechte2005-04-30T18:50:14de-DE3PT3M6SPKG2 settings.xmlKs8+X_ȐJ22pd#+0;!`fj}jKnosfqDɽԔ$&+ōߥ6)LxkQs  !ey->Wr Z:T.m"U-ו/-a)ti%Ab ު*zz2A[ cD?}߿kt>W׽I+|N ⵟ&  |M#g2C9Keۘ|q4PgtbNDRO8 ,aoC箯W [-p{Њg#+[c0VJ.\Jaՙ+h!mBc5)GB.A$Jn#(BZR"n`6eG,p2OEY:( '@3pO0xhoA+Ķv#ZBP-_~> ?=$@d{̎)H\&ۙfR#9nYv"J@?me>m2*|b1^p5#~K`:B?2/  ŔИP:f2_s <x4sH,YsD:Hsz7<3a@HDIC[ I\囂dZ39o/1ķf4?!xqx}x{ALQ@t`6m.*-hx ggxFdu- bjqt>8& 㛲DƎȐHFnc/~%0殊P Yx_t+O9WW:^5V]7Tv[~,[Oɣ6lsܿjA5./=뱷0f83Ik1cp3csINm؟ftW ODrþ6-1: .߇<gAQ%mQ]̱3ݼdt˭8.-E{uōiYS4Ͼ^N%,.jze//'/":6JzG~Q+ϼnxVLs}iB0f;ӐGz#yWAիyw F׺фW :_OC^- 7ר$/ox_~o9J\2ŋ.H>6`1Ơ _Eﴪ;dDGL-ȉ\oS~!H?u:q?:_Pc,UcdjuͥAPKTK<,G"PKG2META-INF/manifest.xmlұj0Onө;)ڃ3t9Ȓ!~BI_pqkg3X 38T+l7Os^쪟r/ZiuCAϏMŮ?"U.`#A\Co5sFtw$+$fц"܏SecGȮ3ZIƳc?8 4 f0&$2@&WJx?1]CrCu=lK,JaiiSY/PKSSPKG2EmimetypePKG2'F Bcontent.xmlPKG2%) Ustyles.xmlPKG2F meta.xmlPKG2TK<,G" settings.xmlPKG2SSMETA-INF/manifest.xmlPKZ-Spreadsheet-ReadSXC-0.24/t/02-read-xml-file.t0000644000175000017500000000113413547152473020010 0ustar corioncorion#!perl use strict; use Test::More tests => 1; use File::Basename 'dirname'; use File::Temp 'tempfile'; use Spreadsheet::ReadSXC; use Archive::Zip; my $d = dirname($0); my $sxc_file = "$d/t.sxc"; my $content = Archive::Zip->new($sxc_file)->memberNamed('content.xml')->contents; my ($fh,$tempfile) = tempfile(); binmode $fh; print $fh $content; close $fh; my $workbook_ref_from_xml = Spreadsheet::ReadSXC::read_xml_file($tempfile); my $workbook_ref = Spreadsheet::ReadSXC::read_sxc($sxc_file); is_deeply $workbook_ref_from_xml, $workbook_ref, "Reading from XML is the same as reading from a file"; Spreadsheet-ReadSXC-0.24/t/regression-stockchart.t0000644000175000017500000003276613547152473021505 0ustar corioncorionuse strict; use Test::More tests => 2; use File::Basename 'dirname'; use Spreadsheet::ReadSXC; use XML::Parser; use Data::Dumper; my $d = dirname($0); my $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/StockChart.ods"); my $expected = { Sheet1 => [ [ undef, 'Fun Microsystems (FUNW)', undef, undef, undef, 'FUNW', undef ], [ undef, undef, undef, undef, undef, undef, undef ], [ undef, 'Date', 'Volume', 'Open', 'Low', 'High', 'Close' ], [ undef, 'Apr 7, 00', '15,000,000', '$88.75', '$88.88', '$94.50', '$92.69' ], [ undef, 'Apr 8, 00', '14,652,562', '$92.69', '$87.00', '$95.50', '$93.69' ], [ undef, 'Apr 9, 00', '15,070,589', '$93.69', '$91.00', '$96.50', '$92.00' ], [ undef, 'Apr 10, 00', '15,207,571', '$92.00', '$90.00', '$97.50', '$93.00' ], [ undef, 'Apr 11, 00', '14,869,716', '$93.00', '$90.00', '$98.50', '$93.60' ], [ undef, 'Apr 12, 00', '15,225,943', '$93.60', '$87.00', '$99.50', '$93.80' ], [ undef, 'Apr 13, 00', '15,243,446', '$93.80', '$88.00', '$100.50', '$94.00' ], [ undef, 'Apr 14, 00', '14,889,873', '$94.00', '$89.00', '$101.50', '$93.00' ], [ undef, 'Apr 15, 00', '14,781,243', '$93.00', '$90.00', '$100.00', '$92.00' ], [ undef, 'Apr 16, 00', '15,132,068', '$92.00', '$88.00', '$96.00', '$92.00' ], [ undef, 'Apr 17, 00', '15,253,822', '$92.00', '$89.00', '$95.00', '$93.00' ], [ undef, 'Apr 18, 00', '14,919,019', '$93.00', '$93.00', '$97.00', '$95.00' ], [ undef, 'Apr 19, 00', '14,972,533', '$95.00', '$84.00', '$95.00', '$91.00' ], [ undef, 'Apr 20, 00', '14,642,786', '$91.00', '$87.00', '$100.00', '$97.00' ], [ undef, 'Apr 21, 00', '14,804,376', '$97.00', '$91.00', '$98.00', '$96.00' ], [ undef, 'Apr 22, 00', '14,907,452', '$96.00', '$92.00', '$102.00', '$95.00' ], [ undef, 'Apr 23, 00', '15,319,376', '$95.00', '$91.00', '$96.00', '$93.00' ], [ undef, 'Apr 24, 00', '15,066,118', '$93.00', '$94.00', '$99.00', '$98.00' ] ], Sheet2 => [], Sheet3 => [], }; is_deeply $workbook_ref, $expected, "StockChart.ods gets parsed identically" or diag Dumper $workbook_ref; $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/StockChart.ods", { StandardCurrency => 1, StandardDate => 1, StandardTime => 1, }); $expected = { 'Sheet1' => [ [ undef, 'Fun Microsystems (FUNW)', undef, undef, undef, 'FUNW', undef ], [ undef, undef, undef, undef, undef, undef, undef ], [ undef, 'Date', 'Volume', 'Open', 'Low', 'High', 'Close' ], [ undef, '2000-04-07', '15000000', '88.75', '88.875', '94.5', '92.6875' ], [ undef, '2000-04-08', '14652562', '92.6875', '87', '95.5', '93.6875' ], [ undef, '2000-04-09', '15070589', '93.6875', '91', '96.5', '92' ], [ undef, '2000-04-10', '15207571', '92', '90', '97.5', '93' ], [ undef, '2000-04-11', '14869716', '93', '90', '98.5', '93.6' ], [ undef, '2000-04-12', '15225943', '93.6', '87', '99.5', '93.8' ], [ undef, '2000-04-13', '15243446', '93.8', '88', '100.5', '94' ], [ undef, '2000-04-14', '14889873', '94', '89', '101.5', '93' ], [ undef, '2000-04-15', '14781243', '93', '90', '100', '92' ], [ undef, '2000-04-16', '15132068', '92', '88', '96', '92' ], [ undef, '2000-04-17', '15253822', '92', '89', '95', '93' ], [ undef, '2000-04-18', '14919019', '93', '93', '97', '95' ], [ undef, '2000-04-19', '14972533', '95', '84', '95', '91' ], [ undef, '2000-04-20', '14642786', '91', '87', '100', '97' ], [ undef, '2000-04-21', '14804376', '97', '91', '98', '96' ], [ undef, '2000-04-22', '14907452', '96', '92', '102', '95' ], [ undef, '2000-04-23', '15319376', '95', '91', '96', '93' ], [ undef, '2000-04-24', '15066118', '93', '94', '99', '98' ] ], Sheet2 => [], Sheet3 => [], }; is_deeply $workbook_ref, $expected, "StockChart.ods gets parsed identically with standardized values" or diag Dumper $workbook_ref; Spreadsheet-ReadSXC-0.24/t/t-multiline.ods0000644000175000017500000002516213547152473017737 0ustar corioncorionPKDCOl9..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPKDCO Thumbnails/thumbnail.pngPNG  IHDR|[L[PLTE###+++333<<O e_or) >z2\_/~Ou|w__?}q珇1T8Ox'< 4y|1;&@^LXqY5Ř.ρk3=޷>m\)I~U)ky/ D'.9^﫞 ,+wާkć>,Jyκҹez|Һ$ .6 V:0X_^m8Ox'< 8^6[x4ԠJa(js}cE]=⇆CN;<8e+9O +,33|cxjŒ CCkuc-e;Jq.5 s_PӟbNs~aQ,vخ,Ahgg2f$58l퓷]R gx܌4Q:s^ L4vE$A 8zc% yxy@=WtsEIcWٺ/j1b)?`]gJƝꌼ~HηG`y5=u5bI7ЈUbuF,8I3g0Ӻ3d8!Z0Ti6<^k=noxP&qnX+kYW&v_MhCK#x=: ok&V`Hv [eˬӾy^Ƀ*)'< piPWP? H)xsuO u)$}R;Ku1!2t]}wޟZur\cІi ;-Ҕy7u@."N.2:k2^,\Sm'upb^;ƒ2E_[ḱ3bfb}Zu]V3*y`_MZ;p ї?PK}t.brUA嶙`Lhd`< `8s]WyReKǜ(񃦰FC|WƩЭc΋"Ào.,ÐbSJni3CeIfWr<O7qy3݈xQ8@j|esfguHyyxʭUarTgKvf<ԓ3!-O#ig8~~^Qb2byP=b`e٤4p%:!Vr7A"t>;~ #"a_kMWfkxWXX8'Sr1ܬʈԸ{$jm@v:3)7 :6J+n{mQTYGtOf8VaF"PTs~A<>sb+It#^k]TQ˘ONɼIZꄷeu[3PCr(0YCXTQt_ X 䮫>v\0`X0VBG y0Yp1{0c*;Hv+ɤXL@?62$ؙIcD[!|مs`dd7:nvLʚzXҾle4m3K^-c=nA''< Ox'< Ox'< Ox'< Ox'< Ox'< Ox'< Ox'< Ox'< Ox'< Ox'< Ox'< Ox'< \ #IENDB`PKDCOConfigurations2/progressbar/PKDCOConfigurations2/statusbar/PKDCOConfigurations2/accelerator/PKDCOConfigurations2/popupmenu/PKDCOConfigurations2/menubar/PKDCOConfigurations2/toolpanel/PKDCOConfigurations2/toolbar/PKDCOConfigurations2/floater/PKDCOConfigurations2/images/Bitmaps/PKDCO content.xmlZnF)&ZEɿѴ&WK.k[Q@@Eg$ERBY4>H73_Ԙb. ]W[7l<&.x̍JeoC1ԡp bt,a&5(r[zE9m-0%moI]0=fm/(>fmo DH [J›92l6}988jӄsmL2&lo%jO!qpy v5X L)~ av'γ*}}la3 &gA[[*(ˡ*}nwJ'E#΂c*C!z&kT-- EdڄFOwl#eq ˱Md@Kf>ZVӷ!Ϟj|8b=])tmœIE~ޟ,=]t0m9Q$DAIC1)ʴ4 =4;KjIϢAA7xpNJt捫+/hjRKqg@a c1raѡyظ@0~4 g@|h@/lzmgƹq񯻏}t|yz^\2N?v wx~Boemu![^s^ghU8AԸ p8(SOcx(4c07{$gOű1EeJib VJ56,2'7^vif&Y\"hK1fkHCt1eO ^gw/8:xS8UJ& &vgWX$IU0rpR@3ȹgq~yam'*]+~5E4~—/y*(Xd_ 7 Ҍ!mf5,H^,cZWgojJ;_/C:9K!o؍ShJf|~  pusK>Fq^(2`kE#GkhMtax2Ñ C[f?t=ljt}uT6fVit8V:􃈠 X5:v]R<5畺Ct{U* oK&tʞ5PKTN%PKDCOmeta.xmlO0ݫ $Y,`*mJM"bO[ǎl}@D{o~ AG/ZeMXLQFXLS_/=U )\Zx|_Gմ|,sۺU-7ZG0_<4W?rBNS|Jc<':Rct a32C{C e$kрCҌnz6%}E=/|蔖8)}(j~;z{Gkp{{O]`ᔮي'/ )d`9ft(g X|ErQx]$OC,Y@ M?S [ˣ+[ Xhh=k,D,SDSH{Zfg#v܇aـ$oPK-;PKDCO styles.xmlZn6S*P@dINn?[ Ae$ZJE.ͮv1`0`;$EZ$GmؖItC)ue7$K&_zwgl6#,Z8^!.(.`Ί^ *H1P,ǙaWcJS \\e/:Y1G 1X_z3E,͑ VSs!Vjwx⇣W G>X*+pb'UEzy Y9.ʺ'SeһIG9L륲/ݸʛ"1]Տu].*$FWcTɠ72w{~W[+Nx!و-h }@xx)KޠtSqθ7;nչHiV8n9>l[4ޒՍZ/@! |[»n<-Xo; |cNm\PAdyT$+6RƼHEqbWs_yTYCwjNSs"8@wKp}"ŵL<"0>ݼ 1͉p%P<ݶmwQ^Ǻ`zo<|syE.fJKb$S'||1I E-8E0{/BېHˈf 9zlֵ,nݬ&-ruQW}[ԱǦcFV RU?.7L[&n׆PM\%of|m:; ]ruf"MlɢphZXodGɅ3cp7YR`5L(z%g,6=F66L nIO>h]]~g*RTXz2VD)iZ-M?=P!bFOMbI7wD \4?DR>H> aU VF -6"kD]Ҡ &[/ABF@jk$6 wNZ0Y._p䅁  (c N-AFtýpp'F{wbmeXզɘ_eP7hTeдPK&.!5!PKDCO manifest.rdf͓n0Gv^{} : Ӂ8i`r"n{ڠS7C`Rw"P2et:8GKic0ʭWS4lh?m[bT~S.b`BgV*昆=&``]=(zMF=Yzpփ|l >BY΂p ! $5LJLǮ)|k{tr}Dk53'tdC8at2pǘ1OcY'Pt>K>BmRLt)W+eʳ7ӚCP: 4@@?#q"&. 8F7E1}}H=h!FęHY &T}Rea` ,i0&7 :d\Ȥ)>Dq`9hXv>-uFlE|JS2K}c?^|W s%oC 11 dZM@lsE[Ĕ À<J, 5XMz>dme=T=o A3ϸ5~n T'οŒޚh!=i40|c+H'OӍug׃{r0=wSվ/C5U]OG ۭX]7Ic.K欫ڴ?i7]]=cʦgmCV+/6^YȪ|/nsT4 10; use File::Basename 'dirname'; use Spreadsheet::ReadSXC; use Archive::Zip; use XML::Parser; use Data::Dumper; my $d = dirname($0); my $zip = Archive::Zip->new(); ok(( $zip->read("$d/t.sxc") == 0 ), 'Unzipping .sxc file'); my $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/t.sxc"); my @sheets = sort keys %$workbook_ref; ok((($sheets[0] eq "Sheet1") and ($sheets[1] eq "Sheet2") and ($sheets[2] eq "Sheet3")), 'Comparing spreadsheet names'); my @sheet1_data = (['-$1,500.99', '17', undef],[undef, undef, undef],['one', 'more', 'cell']); my @sheet1_curr = ([-1500.99, 17, undef],[undef, undef, undef],['one', 'more', 'cell']); my @sheet1_data_ods = (['-$1,500.99', '17', undef],[undef, undef, undef],['one', 'more', 'cell'],[undef,undef,undef],['Date','1980-11-21', undef]); my @sheet1_curr_date_multiline = ( [-1500.99, 17, undef], [undef, undef, undef], ['one', 'more', 'cell'], [undef,undef,undef], ['Date','1980-11-21', undef], ["A cell value\nThat contains\nMultiple lines",undef,undef], ["\nA cell that starts\nWith an empty line\nAnd ends with an empty\nLine as well\n",undef,undef], ); my @sheet1_curr_date = ([-1500.99, 17, undef],[undef, undef, undef],['one', 'more', 'cell'],[undef,undef,undef],['Date','1980-11-21', undef]); my @sheet3_data = (['Both alike', 'Both alike', undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, undef], [undef, undef, 'Cell C14']); my @sheet1 = @{$$workbook_ref{"Sheet1"}}; is_deeply \@sheet1, \@sheet1_data, 'Verifying Sheet1'; is_deeply $workbook_ref->{"Sheet2"}, [], 'Verifying Sheet2'; my @sheet3 = @{$$workbook_ref{"Sheet3"}}; is_deeply \@sheet3, \@sheet3_data, 'Verifying Sheet3'; ok Spreadsheet::ReadSXC::read_sxc("$d/t.sxc"), "We can read a file twice"; $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/t.sxc", { StandardCurrency => 1 }); @sheet1 = @{$$workbook_ref{"Sheet1"}}; is_deeply \@sheet1, \@sheet1_curr, 'Verifying Sheet1 (raw)'; $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/t-date.ods", { StandardDate => 1 }); @sheet1 = @{$$workbook_ref{"Sheet1"}}; is_deeply \@sheet1, \@sheet1_data_ods, 'Verifying Sheet1 (raw, ods)'; $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/t-date.ods", { StandardCurrency => 1, StandardDate => 1 }); @sheet1 = @{$$workbook_ref{"Sheet1"}}; is_deeply \@sheet1, \@sheet1_curr_date, 'Verifying Sheet1 (raw, ods)'; $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/t-multiline.ods", { StandardCurrency => 1, StandardDate => 1, ReplaceNewlineWith => "\n" }); @sheet1 = @{$$workbook_ref{"Sheet1"}}; $Data::Dumper::Useqq = 1; is_deeply \@sheet1, \@sheet1_curr_date_multiline, 'Verifying Sheet1 (raw, ods multiline)' or do { diag Dumper \@sheet1 }; Spreadsheet-ReadSXC-0.24/t/StockChart.ods0000644000175000017500000004430413547152473017540 0ustar corioncorionPKn!5l9..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPKn!5Configurations2/statusbar/PKn!5'Configurations2/accelerator/current.xmlPKPKn!5Configurations2/floater/PKn!5Configurations2/popupmenu/PKn!5Configurations2/progressbar/PKn!5Configurations2/menubar/PKn!5Configurations2/toolbar/PKn!5Configurations2/images/Bitmaps/PKn!5ObjectReplacements/Object 1] pUSyI6!IۦP!}B XP0"VN #IzJd 穸ÓU\;q ~|68JS!e~R3W: 5>ViV,Ԩ&MgFU 4QGjtkNf|k4zGN"8 H aUaa9 9  pA؋ #40B pAË8 lrmHrAءF#4{7aa-F8fXZ>z[9+9eF[HDGjd3~vMT*;;p^Q7#pUHw jTٴZѨ5FYQ\GpgF++hykL6'SچN9h۲v~g[6-D/] Nvdt .nw ׇ"a,îڸ;/oi| v=~l,jD7X:eO.daYVyCG`:R+kh CQ: YNfYN~Z:m';|uNvdY;H>`~S<| qKzvv5"*9Z 0o5?tȰF /0B<#4˫0B0:&AFa0BRJ*ةb w* ߬rahH#a"DdsF> uC=ޗ E%ńq s\8f."}M?q48O˂T܊.A, '0K4>;' A<- zJ=yAZPv8eA_DX=k]0iYg" 5>K A<- eyNPv8eA L䖹{ A<- 9LV㵕q48O˂^TD]ב]0iY:g=o6q48O˂:9P]0iY3Hl A<- z &TƗ.A,{әe+eyZb&0]j@ hpTDTۑ]092A]a{/-rU 骖})>}.hr!THsFi|BBzȥՃ3UP!(KbJhtXKbr[iG2:J!\P:-\KEpm}'.ݵ>tttN+QJ\˧6a#j-.C.4٢| X1t$+F>}s$~ V+2< QPܻjUJ J*.-j]Z'}*PKEPKn!5 content.xml][ۺ~0ܢhݒiS IEmD;.F$[X!#97O?z4H+Sy<¡C\/\/_Ow{CV+ 80G;irႠ؋! pHpZ\VJ߸;g.NcҴ39sKѮig Z"M;?ƾ"Ao&Il;mJzض=`\3>f2Uf9o-C =%ɮ>4ƺ۫J6GĚ}F.Р,+,C4SrBHuH UeY%ݳ;%؝g;ŊnрO">Asv68@f̒ d.MwL<8Oi,7Q6q9VG9CܕHkbSP|@cl<{^= 5ǭ.3 ׿׀#H-=l'8h *O$oh+}vLQ2:& (Sh-`uRP(8Y&J;ъ,)Fߤ{ 2s^'P8R ->=r*ȀR61{M–F5-?ycD _*wOȪ\$pBT*91S)Bk,=IeVől4^!H>P,ä?j ({O×;+d YK]o%ЪkJ?5W 9e#.&8M-KQ? +#{hiB6|b["Da? Qy<*oQ2.hL{1/ ix\u0^}I/$!N-9ߘB.s&rݳ-RpH͇/(u(dKٱ[,%`8¨4 qlGQ]KB"qi*>^%D(`~B9o-OeEr7@s6b{20bcJ(\lMbyIn@w(`++mam6#'.R]o_µXH{+ꮖսd.$ P&L9At[9Z8.ͧf7SeC~MlΫz]7Xs=毙J'mPPNP,UoE~PWT zY=qKb7D̚qF+IfΛl'hP"6./Xc{1#m $3>I&:krL"xjse-)k,Xo Ѻ*S]VdxU8@Z]-qfò6}J[xw_?ofY;|k$فL &tʘw6S={3Seev0 vL5! Qǹ~ f<,>iʊJ=~ M1?a* ̩fipb~lLuSs**Znj"N3[%ۈ9D!V$-O0^PZOX=S!/tJ7,`Xd7'!'ق嘄 o8/@k\+5I6VSMQ[G['3IRޓݥD z)YI|~턛jz jnp[cgI%y~'#Y>m kĮ|*h# 1gDgK 9a9meM1XUua>-C` [B0=,ӪzYY[\y-zCMC5LdHbi;n}!b/kA^^_ooAϧ]llCsd; d;YvnvnYabdO-r;P(e&)ڢy+­"fȗ*ύ"'&@5ޚV_˸=P]Ь,V*fu˴)@0;&̶[f{h",d6Dg`eU+F^#ƪkbU zVWq+mheZĵ>)ڲMM;2v;d#"˂Z]|=HWPc˲&f=ZV;v_˦l"+ Wj_֨Xvn).2X&*뉶8[_蝱YA YESer` MoU_K soL85p?˺+%OJ5OY*e m"6{z~*0dZ)RQe+B Ä{CU~m5v;3Z)|R|9S :ݔ>];X^鯽nZ}R|U+5OjtSWX+ =,+^ :FAFXR[$ڼR$uazj]e F_2K' O-uCW<@M|cU;d/ Vʝ^˝4#1Rz?]g]MEJzJ'C6ME`4ZVibZeT`kL<0 _k|SQk/z _/˟_PK} prPKn!5 styles.xml\n68EMl7vP`-HID}XV,y 693ߏ2dQך@pe}=y-2~A"l.T&|v%%K/i QZzE뫮>J 6Ge퀁}_e%+sZVҾ[j4@G^N\`Q߱l%U>Cq0S>4wU)T;u['}.I; J~gFMIr#%lhjM%lr}1_?ʛ'd>eUc@dHFT$b$| `H=Oxȡa켣}pGɯɗ68)*u;vFC< v2M;mDӽx-Hp˙KzG}eċ&;fL OS>rPE.b@@kIrpѺ7)ծVUK HP6B|d0_~5m-fL)O̦hl amuylx:ϑ˞ˉT@@$~V'h?{< Z@ E"w8J\G* yA +.dG-Dz#Hm D6 Ui#]mkB^d)nJ&ڲ R҃SY&@|Qy7p s_ogPN_v~4\# a_͉}?Dp/u8;㱪ږ/;ԁ+?O;W>\15RJO 3RpF~ 7MFƼ5+e9Xe>JlE꺊"hFɽwLit}@[ݓ9T/|)Qrwv%Qa+tvEp-1:{zerv =Gƣ(KE5 Oky8e1<|Jw*Ώ8syMp>mjX];]xZc:\]wqI^_2 bI6)fLH")1ZbB?4Ǚ!nqR_/BWfOCp87絽zRC -N/w FiH|cO '$,pQ"G.ZHd^Bqq`Q+kCqP̦əjbמp8t\h)4a= dYGy)})F0|YMwՎ& 3˨wVJ }RʨHo*nIZIe6ƣh"*1őg5}^! X+m[J%zGxuxaLQz&,}nUOe Buo6p{-<4ntS47Pqv{Dw"1?Q e{(7,|W˓TuBk:(ui;Mqu:"s(@uUДgE}K_?}egTx7f-QWPqYj;`|O*AyVqkόWj`LUrSKc:;%TҔ?/TN "ȓv%܅zoe^(&a~y;_nt{}3JM ^,ʂiۅ4uϬPKejFPKn!5Object 1/content.xml\ms۸_Q{R"NM/ɵ3 Ą$8$eY]|eJT+bXIj'Cu1N<)eyJic*k5$.JKIAq 84`8"`p\GO_ Ipg֢(q"SN]IoK pXމQA ~+0M$JAmI FtlOҭ`(ˈe%Ӟ #kkbx $Ān2C2]1`#$kYӞףn3^R@ .H`hYˈc .F3@*:Pè~,xjpX !r ]$Yu l)iiXr &(h0Ӻ"i(d)Y^M:C4ATj?L5(+ )MI5P/#lpl4.p?]Ḩg%`i]W6$ZoOdI|)3 r=ZX&,sK4VlXf N8}3VDqdwMU!M^by$"Wxb'iOl8}NI{!,lKQhPK 9晫̰J;c ~NW9FʽGwYfDA=/0ys5%6#'ssZ_mEd2$nPd GNlHYXIVr6gŨB0^{x.$у& E?^*ZCӛ3D!mq-3kP0it+ c|!X/Q{g{>o #%§-j)qtq.2IMUv!gw̏v# 7ey'Σ_rft%3 2gk$qQT^BV6Y{J&5WDzRa.[>l(i/J~_?ͫ񻹤CEվ<6-3fK!c18(@R9qcX:=]a/BJ4\ThZ(5@PVp74 r^W{\b]+Q%- Ism'᨟C%鏊`h1i}}vC\cnoݾ:nGa/{T00kT-,q!Ԝuͯɢk'l\'3{S#ΛrNyR4ý} x 򒽩G>\чƣ7C=4=ܜGT}֣mxQAFKKĴ(NJC ,0Z~&bJ$w ^[88e $gm1Ad.%QkWО*ꝬҞPҋyc`2u{'Y32+^}wNtb0ً!8b_|i>kv,ciy6 k}7sVT;PCCjbO3TEįLO!0-6lGj>k zp0]EP@iFlw%Ph׆$EHJG]wXPDJ,kHƢuH_Q|@W]ZhD*K.r-(Gu,1 yа}AN WD3o4$Jht)7(i@.C?N}4ZJ{٩˜2zDǠs궐DgC[%]\QFM Q*VHw쨬4tǓkћþ|Uun )}bئg ћ~zJ}J_ב|K]QFm:n_KW>H,߅\9 >Iy7 B*ߊXe^NZPF{7;gXSNDLNSFMcܗo2mTO]ҌuAot9dhPD~7U#z-M8ue^ ޭM`x}?KweҲ/+?}zPj)Hpe }YPK, PKn!5l~x,,meta.xml OOo-dev/2.0$Linux OpenOffice.org_project/680m172$Build-9037$CWS-chart2mst3Björn Milcke2006-09-01T15:32:39Björn Milcke2006-09-01T15:48:00en-US3PT15M21SPKn!5Thumbnails/thumbnail.png{4D ~$M'm2Nmq%E:2EHҎ5l c02]m?=du9|.x8!u%%%3[q&*VnUv#A+ow1x_ Q{an+e[wka u5w>+Jwf9WT}Nh ?c37/waU~@lFPA',]l`r$=2C;XTF>;mhs Kg)1CtٛZ݇AnYA]JO/E/wpWM:t/S@8(1 iܷerJZu.ih upZ©C]0+Qg.eY2ea499;-3yb#ISkr VxMחE/=_< |Dt>óxumy5R111QvwܫlN'獄EsPy_MFCbUۑD hҬOKŝbGnw&Z(v`!2d1#<;@ Q?|'(Mug?,Vv2_7[KSuSpl-f̡}@|f+#?wr&r|j~C|-"ŗfIID̐]js5E2p`AXnȇA>lj! pGD[=D2 y僫+<6:ȗQ5;yG:Z>ר;_=x}G'(n[Oص,$Tԇp,<&j1H"ବb ZsgjHv|T:%?t hnm`nmI# CBe Γ4aH\q=Aݮ>c'=#܏yBzūzf}_a@Gh fih2A YP^415&I RlEKTބ.h{WD&&ضtcfPgJf~/sALZ&'`])h,wV"q; Nʱ8up;?F ε4D4#@A6`}#T ĽԃnkԜ \WA)9J7K .'ىeI}s閯 RO+z-R9S[G;];؞ X_R1@fAٙW߈$o"n/Igyp+x`\ocwټ9Y~,.h?LTj5v32@&0%7߅VHe9÷>Kf1}B'B,B\u7vENM kG8_PKhBqPKn!5 settings.xmls:_N+b8ۺ} %kH$ Ӳе3wvꋚ9s||#JJLUN qȼ Kk+:!]>$C!~"^OJHxp4d7zt}#,iYaD "W*QE3敪MnC ~-RKP ]X4իLd5rEi~8}Zۛ̑.h {7&LzҌ;4i?)vJσPTpB3d.H=0b+<$wJ#D]G2cS,Tfx#n y;Twg4fyݛoQNKacWвSJ䔺c0MMZ  ]EAxϣQ,%3`\64gsX\2џ ,<ߡX[ҩrL5),4BU&볋V\Ģ,"dHd9S_$5vB,CEnk[n&c3FApȮw&yK #GtCd\*rcpNLL1HR 5105Um@|גE%x]r+o*yo?PKQ`cPKn!5META-INF/manifest.xmln0 } Çl= nТaНF TI̺YDj3=)k~_huZ-F0GfQuö 4HQc`DjX6֡ #n~o8{~[İSPa[sZIx0]Uyֈ\ }кr8Jsɚ^ ?B8 |%jٱ . u 7\r /W^}֚N,evʀ$xY;3 ۑ9<ٖPKï PKn!5l9..mimetypePKn!5TConfigurations2/statusbar/PKn!5'Configurations2/accelerator/current.xmlPKn!5Configurations2/floater/PKn!5Configurations2/popupmenu/PKn!5QConfigurations2/progressbar/PKn!5Configurations2/menubar/PKn!5Configurations2/toolbar/PKn!5Configurations2/images/Bitmaps/PKn!5E4ObjectReplacements/Object 1PKn!5} pr content.xmlPKn!5ejF styles.xmlPKn!5|*e M$Object 1/content.xmlPKn!53:L@.Object 1/styles.xmlPKn!5, /Object 1/meta.xmlPKn!5l~x,,1meta.xmlPKn!5hBqY5Thumbnails/thumbnail.pngPKn!5Q`c (=settings.xmlPKn!5ï AMETA-INF/manifest.xmlPKCSpreadsheet-ReadSXC-0.24/t/04-text-outside-xml.t0000644000175000017500000000134313547152473020622 0ustar corioncorion#!perl use strict; use Test::More tests => 3; use File::Basename 'dirname'; use Spreadsheet::ReadSXC; use Data::Dumper; my $d = dirname($0); my $xml_file = "$d/text-outside-cell.xml"; my $sheet; my $ok = eval { $sheet = Spreadsheet::ReadSXC::read_xml_file($xml_file); 1; }; my $err = $@; is $ok, 1, "We can parse this XML"; is $@, '', "No error was raised"; is_deeply $sheet, { "Sheet1" => [ ['A1','B1',undef,'D1'], ['A2','B2',undef,undef], ['A3',undef,'C3','D3'], ['A4','B4','C4',undef], ], "Second Sheet" => [ ['x',undef,'x',undef,'x'], [undef,'x',undef,'x',undef], ['x',undef,'x',undef,'x'], ], }, "The sheet looks as we want it" or diag Dumper $sheet; Spreadsheet-ReadSXC-0.24/t/text-outside-cell.xml0000644000175000017500000001063513547152473021061 0ustar corioncorion A1B1D1A2B2A3C3D3A4B4C4xxxNote 1xxxNote 2Note 2Note 2Note 3Note 3xx Spreadsheet-ReadSXC-0.24/t/t-date.ods0000644000175000017500000002211213547152473016642 0ustar corioncorionPK$COl9..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPK$CO!02\س,(dPV& J^}-M"I]cE8;tk\G{ѣG=~Us]zՍpY;&p&qX6â+hdTۯ=&?=x7誱i'$rӗ^$.o~Sb7wI:.km(b(K5!3T`D=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣG=zѣGl8 9!IENDB`PK$COConfigurations2/accelerator/PK$COConfigurations2/popupmenu/PK$COConfigurations2/toolpanel/PK$COConfigurations2/menubar/PK$COConfigurations2/images/Bitmaps/PK$COConfigurations2/toolbar/PK$COConfigurations2/floater/PK$COConfigurations2/statusbar/PK$COConfigurations2/progressbar/PK$CO content.xmlZ_o6ߧԢ؀вdIE`ݺd+#2JHʎ_ϱ=m0`(}EIdɕ(<ǻg!DHʣE"4 ~6O##{IH"<)@:#CۉFK*G)ocR2(eVZ)sYZ[UXVduw)sYxUXBPSUV240ƊP2݌RqEo1q8ᡓR ^'\F1=yCpW| )Jk":+ X]e*5:g|]Xݻ}ݻ?ryyy:}[}? f4jFe]2 U?̖Wm-|yyuAx.(fUDa#EzxǙ)g\&TyxtSTI%_0F֍l`6m$j[!>[hdP@"8ʭ RJ^@32@[ T$k9ǯ~.7$H( VJu(8z(SXCzaeIٹdy2PHkGא7@t1c_P_A0L66&& Vʨ I/ f_w{{o" Ɫ!f,DB6/C'bd$N'L|*c.>1 OM_.JBhc W p߶2ȅoeΙ`< lUYޑS~i!Nj/ ڳg /D޲G~9gF`i`8 ؘMbуY Յ`k^s]ƒBʅX [0yH#@̃ T=0j> è[P8}=Vvc vfYN* 99h:O=/Ps^v"qQ#7HswOt\2c %}mcrJ~Zװ#IbX媿4dYotW :dk/o1$_6>yɀլLYd؞b&u.Ft,J$HL­s&4S mHj@_6]'?RXg\}O"c{~жW%1GNt6o:e:wa.`wj\bMB<bs2Z*j{!̎ՠֱW'jh*32ĝ ><#Ed*X@=?ml+蒭 ɉzᚆ\mQ>_!wyvucHV]'nKgah:TZȭC,ëLNe͟^1PK }!PK$COmeta.xml[0+ X7VT[D qؑ1 DIyxΘx>T F( ) @s#nJks0pu[uR:[2]e3s=[5|Q9+hܑr:Sې(s3*;vVyJp -ˆP{si4ch.4%y+zkMHv5~pziQ5*|4[ȭ 0 0~z;o~E\5;ѧTo_jd0Տ{+[g` v|`rޓߟ p?T)N:Z,ܷ\!8 Q(3F0]e]. 36MW7P >E\A[S;[\ʣIx[N%h9(5kD,SD,H{Zzg1,~ K#7IPK;PK$CO styles.xmlZn6S P@eINn?[ Ae$ZJE.ͮv1`0`;$EZ$WmؖItC)ue7$K&_z6c-R 0gX/N g(XDc0R)JX_vr |!2Kl׬UU_fVg/EA"H +.(^NܹW`;` <%o\:)y8g\XCfDgh\{UMxB]-loIj˶+Pme_b-Oq0c,N_b$T+wY! J05ͺ*1/-9{dAﴲYwҒ$ƫЂ7#4X^)u |Mr8 07FTDR w]8J8M.YŤ(p8K5<8ʿ-le%"Dp\ =ΖF~g_),<;iʗ3m]ʵ9ޔl4oz͢Բ\wO) _L$B)jieD "|٦* OŒQh)w,!G-ҺŭՄE.Jou˺:At2=gȪWVjǫ !Ӗ۵!TeSWɝ,M'v'[ױKQn7V}q^"7oHĽ-YLW+i;Gz=:fgLcm%&RݵrCվceQ( Wnw$2Sa)%qdEb-@mI)Uֆ&.7 Sz5CGS-s !$&ɼ-f8HZn!Z7LZЉayl4v7nW\/eDw IGєpVOd%tj4M7Q| ̑Ws9Qtqn i|ny){|9jcԍ4ʳy^^S"# 3u" 9B)mPiɇ7nM ߓ9px,?vrpV6xg'P_ X }Ärbw@ `RLpH~~UiFwvr!EQ'cEͮ=ܴb^.k$|#yI eӱO/4[O&Qo5oM=-?/a#fH% z`k?D*T?jFRi py,G^&  vEKn7CbmeXզɘ_eP7hTeдPKw* 5!PK$CO manifest.rdf͓n0,zu#|i% VR$uJY(}sCg3KЃ\taWb8amJCJY;UCV}ٻ^YaDo~X]TzGyQT*Ťuաd槚~iRP<`LbLojШRmnvġjw9vV&KoQ~1dc/p&%.|Ǡ*G=W<q `t|1]aqd<j1_4=s D@~CF rah0 qA`kȏǐCnm(|x0 \@pp~>@Y.З瀯9-an2O zߌHK)fu92(%SFǠCmύ8^>|Q+@iۦC@Bx &tpŮbiԅslf)Ǝ\|4ZJԃ3C~B=&ՃzV5'u99' HVcu;Ōϲ_=1~Y1jbZ.u9i!(mG !pa$Lz݇#GkϦa}H=x!NęJ YCRea` ,i{2A&TzT]\ ? m-ΟK-Bl[Ig߽ٝ ?{! @̦_9A`5JK߆|v}b,b:38PNܓ,ta_`@4!8"I0X8b4ŷP}6 H2"C@jJ0X~_GkFnRiZHAJ*u\fo<*ȇj٥pAzZ}kbmUjʏ#m 5xib޸Ǝfi*뮩L_:P!X-],;^w5.}kګ⻺bSㆧ,mX2ٸ-E +Fu4Ӓ1N۠qG~mDhlM-Q565p}h_b`>_VX=<;PMiNecbC Q]wufM}[5?=%7}#En|(_Y!zk*bS3kȳ;<oS_ M3ZzkSkǿXMwx|{dR =2u>mRS/;_fyS?co9IfHSE^񇧷iϵվPKP{'PK$COMETA-INF/manifest.xmlAn E9Ŷ2YU(Nz  Qrb+N\Ub);`|fw]udž}wV^[}?n DrZT۶a9*$Q$@/GnfWi 2; use File::Basename 'dirname'; use Spreadsheet::ReadSXC; use XML::Parser; use Data::Dumper; my $d = dirname($0); my $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/NewAPI.ods"); my $expected = { '3D' => [], '2D' => [ [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Affected Charts' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Pie' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Bar' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Bar, Scatter' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Net' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Scatter' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Stock' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Instructions:', undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, "Click \x{201e}Create Charts\x{201c} to create 5 Charts", undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Then Click on any other Button to see some other things happen', undef, undef ] ], 'Data' => [ [ 'X', 'Y', undef, undef, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun' ], [ '0', '0.07', undef, 'Berlin', '2,001', '1,987', '500', '200', '1,000', '1,500' ], [ '0.16', '0.22', undef, 'Hamburg', '3,000', '2,000', '600', '160', '900', '1,600' ], [ '0.31', '0.41', undef, 'Bremen', '2,156', '1,800', '800', '180', '800', '1,000' ], [ '0.47', '0.51', undef, undef, undef, undef, undef, undef, undef, undef ], [ '0.63', '0.63', undef, undef, undef, undef, undef, undef, undef, undef ], [ '0.79', '0.72', undef, 'Date', 'Volume', 'Open', 'Low', 'High', 'Close', undef ], [ '0.94', '0.82', undef, 'Apr 7, 00', '15,000,000 K', '$88.75', '$88.88', '$94.50', '$92.69', undef ], [ '1.1', '0.95', undef, 'Apr 8, 00', '14,652,562 K', '$92.69', '$87.00', '$95.50', '$93.69', undef ], [ '1.26', '0.96', undef, 'Apr 9, 00', '15,070,589 K', '$93.69', '$91.00', '$96.50', '$92.00', undef ], [ '1.41', '1.01', undef, 'Apr 10, 00', '15,207,571 K', '$92.00', '$90.00', '$97.50', '$93.00', undef ], [ '1.57', '1.04', undef, 'Apr 11, 00', '14,869,716 K', '$93.00', '$90.00', '$98.50', '$93.60', undef ], [ '1.73', '1.05', undef, 'Apr 12, 00', '15,225,943 K', '$93.60', '$87.00', '$99.50', '$93.80', undef ], [ '1.88', '1.03', undef, 'Apr 13, 00', '15,243,446 K', '$93.80', '$88.00', '$100.50', '$94.00', undef ], [ '2.04', '0.96', undef, 'Apr 14, 00', '14,889,873 K', '$94.00', '$89.00', '$101.50', '$93.00', undef ], [ '2.2', '0.83', undef, 'Apr 15, 00', '14,781,243 K', '$93.00', '$90.00', '$100.00', '$92.00', undef ], [ '2.36', '0.76', undef, 'Apr 16, 00', '15,132,068 K', '$92.00', '$88.00', '$96.00', '$92.00', undef ], [ '2.51', '0.62', undef, 'Apr 17, 00', '15,253,822 K', '$92.00', '$89.00', '$95.00', '$93.00', undef ], [ '2.67', '0.51', undef, 'Apr 18, 00', '14,919,019 K', '$93.00', '$93.00', '$97.00', '$95.00', undef ], [ '2.83', '0.37', undef, 'Apr 19, 00', '14,972,533 K', '$95.00', '$84.00', '$95.00', '$91.00', undef ], [ '2.98', '0.19', undef, 'Apr 20, 00', '14,642,786 K', '$91.00', '$87.00', '$100.00', '$97.00', undef ], [ '3.14', '0.07', undef, 'Apr 21, 00', '14,804,376 K', '$97.00', '$91.00', '$98.00', '$96.00', undef ], [ '3.3', '-0.15', undef, 'Apr 22, 00', '14,907,452 K', '$96.00', '$92.00', '$102.00', '$95.00', undef ], [ '3.46', '-0.22', undef, 'Apr 23, 00', '15,319,376 K', '$95.00', '$91.00', '$96.00', '$93.00', undef ], [ '3.61', '-0.4', undef, 'Apr 24, 00', '15,066,118 K', '$93.00', '$94.00', '$99.00', '$98.00', undef ], [ '3.77', '-0.49', undef, undef, undef, undef, undef, undef, undef, undef ], [ '3.93', '-0.63', undef, undef, undef, undef, undef, undef, undef, undef ], [ '4.08', '-0.74', undef, 'CDU', 'SPD', "Gr\x{fc}ne", 'FDP', 'PDS', 'Andere', 'Summe' ], [ '4.24', '-0.8', undef, '34.29%', '35.43%', '11.43%', '8.57%', '4.57%', '5.71%', undef ], [ '4.4', '-0.92', undef, '1200', '1240', '400', '300', '160', '200', '3500' ], [ '4.56', '-0.94', undef, undef, undef, undef, undef, undef, undef, undef ], [ '4.71', '-0.92', undef, undef, undef, undef, undef, undef, undef, undef ], [ '4.87', '-0.99', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.03', '-0.93', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.18', '-0.81', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.34', '-0.75', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.5', '-0.64', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.65', '-0.5', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.81', '-0.39', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.97', '-0.29', undef, undef, undef, undef, undef, undef, undef, undef ], [ '6.13', '-0.06', undef, undef, undef, undef, undef, undef, undef, undef ], [ '6.28', '0.09', undef, undef, undef, undef, undef, undef, undef, undef ] ] }; is_deeply $workbook_ref, $expected, "NewAPI.ods gets parsed identically" or diag Dumper $workbook_ref; $workbook_ref = Spreadsheet::ReadSXC::read_sxc("$d/NewAPI.ods", { StandardCurrency => 1, StandardDate => 1, StandardTime => 1, }); my $expected = { '2D' => [ [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Affected Charts' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Pie' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Bar' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Bar, Scatter' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Net' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Scatter' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Stock' ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Instructions:', undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, "Click \x{201e}Create Charts\x{201c} to create 5 Charts", undef, undef ], [ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, 'Then Click on any other Button to see some other things happen', undef, undef ] ], '3D' => [], 'Data' => [ [ 'X', 'Y', undef, undef, '2000-01-01', '2000-02-01', '2000-03-01', '2000-04-01', '2000-05-01', '2000-06-01' ], [ '0', '0.0736160629771725', undef, 'Berlin', '2001', '1987', '500', '200', '1000', '1500' ], [ '0.15707963267949', '0.222381328196949', undef, 'Hamburg', '3000', '2000', '600', '160', '900', '1600' ], [ '0.314159265358979', '0.407497597612808', undef, 'Bremen', '2156', '1800', '800', '180', '800', '1000' ], [ '0.471238898038469', '0.509797219370413', undef, undef, undef, undef, undef, undef, undef, undef ], [ '0.628318530717959', '0.63395633752449', undef, undef, undef, undef, undef, undef, undef, undef ], [ '0.785398163397448', '0.719141179928584', undef, 'Date', 'Volume', 'Open', 'Low', 'High', 'Close', undef ], [ '0.942477796076938', '0.817336001844437', undef, '2000-04-07', '15000000', '88.75', '88.875', '94.5', '92.6875', undef ], [ '1.09955742875643', '0.949218778830976', undef, '2000-04-08', '14652562', '92.6875', '87', '95.5', '93.6875', undef ], [ '1.25663706143592', '0.959774132620732', undef, '2000-04-09', '15070589', '93.6875', '91', '96.5', '92', undef ], [ '1.41371669411541', '1.01017825625409', undef, '2000-04-10', '15207571', '92', '90', '97.5', '93', undef ], [ '1.5707963267949', '1.04025523422298', undef, '2000-04-11', '14869716', '93', '90', '98.5', '93.6', undef ], [ '1.72787595947439', '1.04753199951172', undef, '2000-04-12', '15225943', '93.6', '87', '99.5', '93.8', undef ], [ '1.88495559215388', '1.02786291206465', undef, '2000-04-13', '15243446', '93.8', '88', '100.5', '94', undef ], [ '2.04203522483337', '0.958401671062797', undef, '2000-04-14', '14889873', '94', '89', '101.5', '93', undef ], [ '2.19911485751286', '0.830847150364955', undef, '2000-04-15', '14781243', '93', '90', '100', '92', undef ], [ '2.35619449019234', '0.758422682219809', undef, '2000-04-16', '15132068', '92', '88', '96', '92', undef ], [ '2.51327412287183', '0.61750874736503', undef, '2000-04-17', '15253822', '92', '89', '95', '93', undef ], [ '2.67035375555132', '0.508331188555977', undef, '2000-04-18', '14919019', '93', '93', '97', '95', undef ], [ '2.82743338823081', '0.368970261437009', undef, '2000-04-19', '14972533', '95', '84', '95', '91', undef ], [ '2.9845130209103', '0.18734508575333', undef, '2000-04-20', '14642786', '91', '87', '100', '97', undef ], [ '3.14159265358979', '0.0695369149416393', undef, '2000-04-21', '14804376', '97', '91', '98', '96', undef ], [ '3.29867228626928', '-0.148695695469987', undef, '2000-04-22', '14907452', '96', '92', '102', '95', undef ], [ '3.45575191894877', '-0.217646287234005', undef, '2000-04-23', '15319376', '95', '91', '96', '93', undef ], [ '3.61283155162826', '-0.401230768200599', undef, '2000-04-24', '15066118', '93', '94', '99', '98', undef ], [ '3.76991118430775', '-0.489735482416856', undef, undef, undef, undef, undef, undef, undef, undef ], [ '3.92699081698724', '-0.633651622065608', undef, undef, undef, undef, undef, undef, undef, undef ], [ '4.08407044966673', '-0.74060350042111', undef, 'CDU', 'SPD', "Gr\x{fc}ne", 'FDP', 'PDS', 'Andere', 'Summe' ], [ '4.24115008234622', '-0.795288510043229', undef, '0.342857142857143', '0.354285714285714', '0.114285714285714', '0.0857142857142857', '0.0457142857142857', '0.0571428571428571', undef ], [ '4.39822971502571', '-0.922963238013719', undef, '1200', '1240', '400', '300', '160', '200', '3500' ], [ '4.5553093477052', '-0.936119316703055', undef, undef, undef, undef, undef, undef, undef, undef ], [ '4.71238898038469', '-0.921722266134677', undef, undef, undef, undef, undef, undef, undef, undef ], [ '4.86946861306418', '-0.985978999383097', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.02654824574367', '-0.933540629292918', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.18362787842316', '-0.814248187085184', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.34070751110265', '-0.751500933485474', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.49778714378214', '-0.643419808952295', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.65486677646163', '-0.498992516447254', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.81194640914112', '-0.388155431380584', undef, undef, undef, undef, undef, undef, undef, undef ], [ '5.96902604182061', '-0.287117767544654', undef, undef, undef, undef, undef, undef, undef, undef ], [ '6.1261056745001', '-0.0589241128694329', undef, undef, undef, undef, undef, undef, undef, undef ], [ '6.28318530717959', '0.0883249840644768', undef, undef, undef, undef, undef, undef, undef, undef ] ] }; is_deeply $workbook_ref, $expected, "NewAPI.ods gets parsed identically with standardized values" or diag Dumper $workbook_ref; Spreadsheet-ReadSXC-0.24/t/NewAPI.ods0000644000175000017500000006100313547152473016551 0ustar corioncorionPKnZ!5l9..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPKnZ!5Configurations2/statusbar/PKnZ!5'Configurations2/accelerator/current.xmlPKPKnZ!5Configurations2/floater/PKnZ!5Configurations2/popupmenu/PKnZ!5Configurations2/progressbar/PKnZ!5Configurations2/menubar/PKnZ!5Configurations2/toolbar/PKnZ!5Configurations2/images/Bitmaps/PKnZ!5 content.xml]ݒ۸ߧP)NjfӢ38;[J-HIs'ٻ}<!(R-~l=!8|88ɸяQ\uv;~0o{ǗMxw7p0AAv r( x_ď/e8[җi[YJ<~=-;?%۾z۷ο=m_eaP߅۾)BKF*>G/W݇$^#q辏R4wI`Yn:i}XG/Nۖ>]6OR0C%W[K#b7Ⱦkx*Fc2Hɓe_C%7<^8MFo˿D3+XnE8vW> Xn~ԛz~/{ڿfЫ\23z8ciNϼ6m:ܛvGv̸ۘu$B $hx,H{GA/gvݍ>-r#{KdyHw- CӕL%a\ț>"Tp yEnfm@ds<4ɥJtkK۫D?m1q__qG) FE L]^+d~s /~1|RZb_xr9hQWy՝?\]r55l Ul]J dH*\S]MGԒσFS _tpao֬%,쵄!n@ ڪrῃyK978CplzЍgy>k@\6j]jcjKr[-@-ȉ:vQ +Io ԿtD=j3r9 NBϰeގJ2؜_,"ߺm˟⟝kv Rv8n2wqV'/"(d|ݝ?" =̒S݂8hɎ&9b,ɵLCv~U# -\Ԕ_Yɬ ^44^e\1'(2,r$Q8&j /S0Ԣ:qYĉ9-<.o{?E)QNzCӜ̄4t}Kx 2+{%6}wތY;Oc\Z83GV~7GHjYC JfBv;K0ȫY:Mo22 uXzZ'[|}7x;řW9z&^& jFlN:̽1^<:pV8dڝ7e#VURkLR)0N% .b33O]pjfݎJֵa89 u2Ff|?˦_{h;o28d&{Z9=cSߏܞw(̄B茮',RSLot1,zº(0C^/;g=]}F(,g=aIXx34])?XOY\3~* EsS،?HI؊fOWh, :KvWR0%Yx.1>ȩn֘8ۗ,FcIyP_=^yzy\]/t ķ|e/Fߥy +wKٟҫS撟ҫ`%:9( 4!u8cx4=Elc/gjuޟVGYwCYID,R"8(?11}. ;oNYzKC55Xn4S.SQ] v3\ؠXVꕫg+}϶rٺ\r%+)*労+W,Wr8r+R[|FT5^vruѹ%,5f)`ۛTuMZ7 XlveG?9HEubXI]:U=~'oBRV~9Kd g"ZZ,Q[*H"R1xwʡYJQ?lژXRfiK|Q5ʨCf<?8w' ;,mE;vX~Ɇ) :^ ?dbzbğg$~yS?ǘS|9s9|Г%a>\ete7e͟i~x6^Rq&%:F:aҊ;| P,Jġ k@]Ul{0w*LñzF뫀 uZ:Itn8|#^f/S.V~ſxăV N m<[YJԋeFz.>ٳrZ5A6ֿ=! 3'rZ$Vv':,vK)l [R ^z^\^ݼ9߿O~eϜ w9VB Yw7&<7 W)owĄ+' EURo5aDKx@y{Yt_#ؓ=6'{zWzU}7+#"0gI% D!V6D9QkȣJ0%8ҕyn6lEzcgπd8)8Eـ'@+(;mOd EBj<<霣2>kb'r`}2a@xBm}^Zɉ491MNԂ}Ne:&ӑ&;w<'9)b)YgXw"9Vrfn޺I IĔPMZ;ICYOn7ڏDkg|,)g'=5Q` v +T?w]oOI".˱PyvlMOZ@Ozv}[s.nzFгhxDU̼1 . ut{M6v>NlH׆Cgwm"?FBc 1 l[::\2OqBnt6:tӔW ~h+Ŷ_4Vϊm ~0n# 3_؜)ַúĄX46g5(dІ`l~víVZ{o΋? g~4Ļ}qBd砝p6NO(g9ʙCI[r*(GuʿE#[^.&ڥz*i딿GIxζa@Q'#L4uRltQH4@8DsU킭I*ճ};hwb|ν1m[1`gLJw_e W_ﰒgȲ؜-i1*Bpav}lk!!Es]iÃ#qFT}dY4+|Ƙs!G+Tԑ\Q.a\N[ ӠO8B$SB*H9W%6G6buyUȆ"s9V2}v~f9H6U5:A^$bIFJNW6R!}.BHkXWɵM}) ,7.*&>i{x^K1*Z^%;gc5 -E wgI`AjD@m)Ɋu)GYśUm) qJfZV8=3 Q *XUé %Hf+rV#9+\ֱ jb5+ZE$rǿH4jwH$S+7 s+ZfC/ 0{;ޱgޱuWbbzP0ZFSVI z:FkDSиVyWJ׌VԡPzMZfTMo:*k OH8bɶT(Nڑ̷k ?PJ<3hS5砆ljIBY,Xh4?] P㱿7krXNp*2=Όdg?鋊/' z)C,X}X&ͯw`%8@򕉂)%1pd7@8IM+B(0"VI]a Ssn@\Ђ9߇ΞkC":HvĊяˎ@ u DS PJENJ oeJyUDUͱ2Y#<6ơVOxr%f&!?$T=' GqbM`.4ce9&p?Wmc8}# 1\y]Z/rP7n㔇׎-$B\%uY*&X2tH!IOWc-r4޻PKOte0PKnZ!5Basic/Standard/CreateCharts.xmlYmo8, ?,+εXu~KCi>-hyDI%^q=Dr<3*{B@,ۮWG$Y@Uu~oj6#$|N7"bA4ڿPmu!rI}2j!2C'%Vk4w Œuk)7twԯ]#F `VH=mBSY͒2njCDx&DNh&9xu/NF [H/8) yD>\Į] j-o/ GI^˴0vb7Vk @߰Qᮈo.՜MDc1I° kcG.q3k(NqҺ` ?G1XdIX;J1=qKkKw9NЅޫwh V|S_])(@5?=5vߴw~GOOZ%coKo ϖgʺԎϷ9nIHV'}&o_ʟb9++ _8)σԚge)j3gli? 4F ='&T:KƇX,j=X}-:%YDKʅT+ 3 NZ|bWS" SȞfU6+u^>n5tq+!<]Qrh$Eվ92ų\FdɗG*1K$("rOK"$ ptXC( d}9 [ aĕq]M60b:u%|H$(*5v+̆,Vq ~|ܨ . p A-GE!_ BX "F0ĈahEuF_k,x#E#[͞& nm6iK URUY.V9Q A@F%BoUXl RcKml' )%fèG krP ,l6W5Y sk+͌4)|*xn.m{TЫUtwUF +5ʫ0 ;spܒ XU`HEu9^>w }2rbT)M)e nɡwN z}^.-V)Zb 4œ/kΙBKeǑJِTެ̿c;=0O jaM-0LؒB1I+F57`Dha2ҺpAAX@M5 umf2kd@ԛJuOҴ(Q=i-E4-^?s;89hujϧK_'hx q FW $ dD0"ESfNd||"Mjo<Ӑ 3SZ~7Pݻ&jYqq!k{:^Nos|xfNem| Q9"+燄lwn*oY1HhQkY/ց&!L~1:cWOPK [ PKnZ!5Basic/Standard/Utils.xmlVmo0 /*ڄM"|ا$dK,v8o@N4$;?sgs'3Ͼtz6AvYO/>۷7x1Z[N(yLr3|}ẋEF>uxsz12u'sv' mL Dy^ʴy~".)1P,XT]Nv`_I |On'F 4Ц<,r Ȃ=|+rO7`-gn9<U;*HmLW4{bҬ"+ 9SQYJH~D(KX(6oXWRX = y΂xeAQz5AJt"kN%fB(悪`үcBfVyL=; ePPIMTye2=i| ׊ZhO29S*)j]sLtL #r8ȎTpN QF0j1-nd^# RrUI:A7 K<<;e^BS qԚĂV71LF ?f$TU5y` 1 }H͹'*DT5(M(I5*qyIUT!|;v me:dKf^((DlZF 3yOݾ01VHu<0"Ttz݆ F |a.cٮ ZCfyq ^ w"@5,x6XӯstW*YSk/PK CJ PKnZ!5Basic/Standard/SymbolSize.xmlVYSH~~ŬR8EFdS[I\fBo4'!󘰬yBc¸5`<,>!fh.R uj}T F$N@f*29yNx#W+5Hf6Xq;L4Y}_n,< qɳI&|-6SrK !տZ8WH)I6k*X3-)-g9,dK-,+,Z*δКSQA걷.gr^ƫ^6/gTTj8:8h 4&YQi-6[uYK0^a"fR;ѓxL  R~KD.46Vv]Bۀc;V` [x.vI) |ԝ=کĶޙBu) qb3C 1h>$ήц`]!/J孇"d,};C\X{jJ:]vA-d65'AT04eJT6#@|(J vՌĞ󔑏.ӽtRZMb'xx.M+ pJb}*~p2f=f@Њ>Z0ٵij/_;Ͳr-O2o7Y4=n2p7 qbޤ0Lh*v0@`N)byLa*8-0Gn09’&Wc8~Ot8$5Vhi;ܐ2[DpPNuPDVi*L6v.FP8{W'ˬ%?ގTǿc@8 zõ@-7WvU!ގ[[gzcI{U7$+N->!ժ-hjN_ G(߬l;aN3nY@VKT->h9RU] .XsQLsRE[ j}H޼8 }hl `&U2n[VT&+ 3 !J*by}`JS<̊KurTi=,W3&Zx^7uɜ*U:%C Q6M [O/)O` 'ŒE{rrorv5'}( Kp<念j3Zldm=OeW<)EAdʼny'ƈt=iΕYs^r;@[6(=7pt!dY'#)ŕɒ`ǡ~u]w鬻Hj#*<0V7sBqn·/PKc4PKnZ!5Basic/Standard/Pie.xmlVmoFl?*s /i^`{ϻN¿̮m D>̘yB8 LT*.^_&?)UZe/y U & D%UΥ>LhT&%r [Ib; +GL$[N~ٻ\E&a|Um װ2%cejg,5Ϳx\ Riͤ;^^\%&nc$*u%C5m12s%E[9FshuUS+Y~.^R̰52L\}Ⱥ?凢نg$E'0 %9JbRxǰfM@Ff8q5}ߛma ʲ^a{.= o<(º=#w|Ӕ5tx1cJⰄMM E(PlcJafuІXhׇ¦it.iWf!%K[ZIr$(P.TJݨeDͻFztNvQ/@*aLã3 ew/enqjH|kzJa؇C[m-9jaS̲ >Õ(p7=(?7&Tx'Lmt c000WO!uLxseATv$(tY>8O4ƪd[u:ѮD8{-! "5y`"ܶj@D3S`LCGp6Q޽`Is f SjC"HI=xkm P<j_CS=N޳\Lk0:eHt]+O!ϪFr*+VA#TJ/ϫ|і}/-ۚkvhR)<ٯIfu걟nNfc²Y-@D$%Фgk""z?0FҾ0PK"yPKnZ!5Basic/Standard/script-lb.xmlO0+j,2a&&>[=5h{zm1 B@qǸx,Obo4էͼ~8< 'y{B*CuڷEN3Fxe_˺TҜNBsP]df%#ĸT2ٲ2%S0LԘ٤; ]u9@a?:{ @5>|+OcD? Ֆ #4,7^t5hK׽'yus`T=LPKĵPPKnZ!5Basic/Standard/Statistics.xmlU[O0~N~N4MSK@4)R%hQS6h4Mws~*ͤHS܉,X$Ktytv7@Ple,-3LQtKl!da+* 8"yw\2𗘐]/K1nK@;N{h Môa*8 K^D" n~r(Ÿ00fBdJ(hI-(0^SŨ>V=Sz?YTUD pW-Dd>r7apDVRHEfI!Z"e9 E:wY=ZE%| 9d zH$!-/"$!y)ꖄom4>Qu[) &'hAM_;Vc~qP; sW張au9Sd,p*j6rn^1Z.ȩ7Arݸ&uk!+ R"j=\90A,!ʧk]*9LFB\w5cMAv\Sp3Mھ}[{C0Vpt?^{3gOD5ݟ߅ݭwr0{|_,, -y*puߺ-^}ۻ̏ l)4IRTRgؿgn PKhl7PKnZ!5Basic/script-lc.xmleAo0 e4ʤIhE8fM ҰUAplTl 8 BŹ1N'/$eJkmGשU {8 JӢй %YWX)v D$zƳuwFDS̍!u7ؽJiy(XT*ƕ,xl[VH>2=ePKlRPKnZ!5 styles.xml\kS` DOvcII`glHyr$$V[򌦑n`"Ū"K69/Q=C[^XG|?wdGK")L1ۤ[=xCClAن{Cڔ7jE):\ Gs‡iJ<ڧ8t/,4HjV<rAh`ڮ뚪0+℆JLB93m`}RlN'HC8hD=t@ %\ ޥ_~ɝ(:?@sI TE`7SxBHa&2ױ>G8%qWW N6Єm >KZT]FXSnZ,1?QB3FbɃ']le#w &Ǻ`ڪk%13?DsC!-塲 PR̴vEa'a4*" m I1vv+AtOϭ$a3Nhx0 \dT9H*E@tbR՝31 >4G br>b`Y`X 9TyXe۞lBч# TOz}!R۳TIrAX$E6\nW!|i] ɦK !T#d\FU/v>9, rEFöd4OAі ',2ȇ$a|(' 6[pI @TDyܖDfD l+aA؇rŭR"PE yXΈyτACT.5GB"%N?r l'K 9.'LP j niSqnj/T>ڦr/I(H,=T;h.%IȫzIǝV'溪eK}|,tZ:^ޕF*ghMщ@\$W53mA;aQ92Tۤq?/; AqKD$6V?&jv[/ ra!A=6 '^8_Ƌl[$qnlǁ$ftpŚpAdQ況l~c;0(3Nwfj2K] a?ލ-la|?zUUm|bTӇpfیᎣcmzX nVoy/oP@ )W-)U{! ϐC-hpE|L0Hn_0/!KE;g'=0d/| _ (ܹ¥Gs*^MЍ,-oC%r.%dXΩ(I̗#\bƷǐҸ[1G%=\mݫkyOUJ˪FյL;U_'pu=W#:lv-:?oo6 tIq7.)VLb"5btP&TǤ=Mb _Å# yk(kI-iZ1=[m^$0 Yp8D:okiz9uq"/קCOe !rD%ϱwHd/csG(ol'#MqB 8!D LL F*K :/eJ'B}vr~ocθ`fWAcЕD!W+S4ݬ%#13!8W<#[oz[8Ky$8db:7MYi"]H.۹:k?dE/{Sm|p.1Z ˽[FVe| K9[ͬd6t9D/&js흥~2nw!c}]nw% WK8`# bD5;#PUj\Rwr3_v0=<<ܛƬ%aP e3Q^ M1'8Բ'ivZ|R[Æ\W^Ƅ"[_S' eI{^ }*" *3UTfeqge%BP3}Ҳֆ嚖-/3+Zn^nlw㸅m8eAudeF3۳loMvPK OOo-dev/2.0$Linux OpenOffice.org_project/680m172$Build-9037$CWS-chart2mst31999-07-30T11:11:322006-09-01T13:19:29de-DE0PT0SPKnZ!5Thumbnails/thumbnail.png sb``p ҥ@ H}WV```ltq #'ρ'ps1ͼk{{Ʀ'(72hXZ'}! 0f<]\9%4PKzJ=kPKnZ!5 settings.xmlZ[s:~?p{k/u[ j!a _螖g:{싕d}ke%En~]|2#JJJ,@bSqIkʏ?7t4B6,:]H)B)D^KHx򢰋ԃd-V|;T=cD%e"WT ΂ec5뺺]O)񮪢oUQJ( "cʴ\\+'+#߹z囕 膾9Y=M+)Req`k&2]đ hSOY'J"w~MuA1yuu7=DF.Nq:qU0ؼSKybaĬcYl }/Uw88e ʑ){AWJ-#Lw]~ @3Do2Np|moiy L=q{O _p;%G!#=.qd#{|G{Q8Ǒ??Gu㗅PBU6[%VUJ)u'v㨫+cV[/S&4Tdu?m0LbGs^2')Nš2@QM)Ac^vYд ^\I{5PH9 l-\bނ`^60YܬtЄ\2-7·|A3|iNoo-=aWRf ;pҒ?ƍ,nd>T=|Cru%zyNG{;` 2U ܯφc/Wj^-mmj0߫5uͨ ;ߩb-WGͻZn2&,@"7?.755]]>zVf6i.=3̾~IAoĠH$tv)6OU@ӹUxu lm7CoutҹmV--or;7'TJy=/:eC6 VL%FsE ] >~)|0H<$._cDd~*o׻iRcy\[`C8n/bb+DfQ ]o[8C̛C rg Ll);8VjһgPKo&PKnZ!5META-INF/manifest.xmlMn09ĦB$ v@Q#vO_*5Hzp$Q{ז|ؖ/[yYYHqPO2Ex'-`C\Us}37Ix0 z-+:hKJR)^]_ꏈC@IdHTAԖ1ϫl*$%d䑗J 1; use File::Basename 'dirname'; use Spreadsheet::ReadSXC; my $d = dirname($0); my $sxc_file = "$d/t.sxc"; open my $fh, '<', $sxc_file or die "Couldn't read '$sxc_file': $!"; binmode $fh; my $workbook_ref_from_fh = Spreadsheet::ReadSXC::read_sxc_fh($fh); my $workbook_ref = Spreadsheet::ReadSXC::read_sxc($sxc_file); is_deeply $workbook_ref_from_fh, $workbook_ref, "Reading from FH is the same as reading from a file"; Spreadsheet-ReadSXC-0.24/META.yml0000644000175000017500000000153213547152475015770 0ustar corioncorion--- abstract: 'Extract OpenOffice 1.x spreadsheet data' author: - 'Max Maischein ' build_requires: Data::Dumper: '0' ExtUtils::MakeMaker: '0' File::Basename: '0' File::Temp: '0' Test::More: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150005' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Spreadsheet-ReadSXC no_index: directory: - t - inc requires: Archive::Zip: '1.34' Carp: '0' PerlIO::gzip: '0' XML::Parser: '0' perl: '5.008' resources: license: https://dev.perl.org/licenses/ repository: git://github.com/Corion/Spreadsheet-ReadSXC.git version: '0.24' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' x_static_install: 1 Spreadsheet-ReadSXC-0.24/lib/0000755000175000017500000000000013547152475015264 5ustar corioncorionSpreadsheet-ReadSXC-0.24/lib/Spreadsheet/0000755000175000017500000000000013547152475017533 5ustar corioncorionSpreadsheet-ReadSXC-0.24/lib/Spreadsheet/ReadSXC.pm0000644000175000017500000004750513547152473021333 0ustar corioncorionpackage Spreadsheet::ReadSXC; use 5.006; use strict; use warnings; use Exporter 'import'; our @EXPORT_OK = qw(read_sxc read_sxc_fh read_xml_file read_xml_string); our $VERSION = '0.24'; use Archive::Zip ':ERROR_CODES'; use XML::Parser; use Carp qw(croak); my %workbook = (); my @worksheets = (); my @sheet_order = (); my $table = ""; my $row = -1; my $col = -1; my $text_p = -1; my @cell = (); my $repeat_cells = 1; my $repeat_rows = 1; my $row_hidden = 0; my $date_value = ''; my $time_value = ''; my $currency_value = ''; my $max_datarow = -1; my $max_datacol = -1; my $col_count = -1; my @hidden_cols = (); my %options = (); sub zip_error_handler {} sub read_sxc ($;$) { my ($sxc_file, $options_ref) = @_; if( !$options_ref->{StrictErrors}) { -f $sxc_file && -s _ or return undef; }; open my $fh, '<', $sxc_file or croak "Couldn't open '$sxc_file': $!"; read_sxc_fh( $fh, $options_ref ); } sub read_sxc_fh { my ($fh, $options_ref) = @_; my $zip = Archive::Zip->new(); my $status = $zip->readFromFileHandle($fh); $status == AZ_OK or croak "Read error from zip"; my $content = $zip->memberNamed('content.xml'); $content->rewindData(); my $stream = $content->fh; binmode $stream => ':gzip(none)'; _parse_xml( {}, $stream, $options_ref ); } sub read_xml_file ($;$) { my ($xml_file, $options_ref) = @_; if( !$options_ref->{StrictErrors}) { -f $xml_file && -s _ or return undef; }; _parse_xml({ method => 'parsefile' }, $xml_file, $options_ref); } sub read_xml_string ($;$) { my ($xml_string, $options_ref) = @_; _parse_xml( {}, $xml_string, $options_ref ); } sub _parse_xml { my ($internal_options, $xml_thing, $options_ref) = @_; %workbook = (); @worksheets = (); if ( defined $options_ref ) { %options = %{$options_ref}}; my $parse = $internal_options->{ method } || 'parse'; my $p = XML::Parser->new(Handlers => {Start => \&handle_start, End => \&handle_end, Char => \&char_start}); $p->$parse($xml_thing); if ( $options{OrderBySheet} ) { return [@worksheets] } else { return {%workbook} } } sub handle_start { my ($expat, $element, %attributes) = @_; if ( $element eq "text:p" ) { # increase paragraph count if not part of an annotation if ( ! $expat->within_element('office:annotation') ) { $text_p++; } } elsif ( ( $element eq "table:table-cell" ) or ( $element eq "table:covered-table-cell" ) ) { # increase cell count $col++; # if number-columns-repeated is set, set $repeat_cells value accordingly for later use if ( exists $attributes{'table:number-columns-repeated'} ) { $repeat_cells = $attributes{'table:number-columns-repeated'}; } # save the currency value (if available) if (exists $attributes{'table:value'} or exists $attributes{'office:value'} ) { $currency_value = $attributes{'table:value'} || $attributes{'office:value'}; } # if cell contains date or time values, set boolean variable for later use elsif (exists $attributes{'table:date-value'} or exists $attributes{'office:date-value'}) { $date_value = $attributes{'table:date-value'} || $attributes{'office:date-value'}; } elsif (exists $attributes{'table:time-value'} or exists $attributes{'office:time-value'}) { $time_value = $attributes{'table:time-value'} || $attributes{'office:time-value'}; } } elsif ( $element eq "table:table-row" ) { # increase row count $row++; # if row is hidden, set $row_hidden for later use if ( exists $attributes{'table:visibility'} ) { $row_hidden = 1 } else { $row_hidden = 0 } # if number-rows-repeated is set, set $repeat_rows value accordingly for later use if ( exists $attributes{'table:number-rows-repeated'} ) { $repeat_rows = $attributes{'table:number-rows-repeated'}; } } elsif ( $element eq "table:table-column" ) { # increase column count $col_count++; # if columns is hidden, add column number to @hidden_cols array for later use if ( exists $attributes{'table:visibility'} ) { push @hidden_cols, $col_count; } # if number-columns-repeated is set and column is hidden, add affected columns to @hidden_cols if ( exists $attributes{'table:number-columns-repeated'} ) { $col_count++; if ( exists $attributes{'table:visibility'} ) { for (2..$attributes{'table:number-columns-repeated'}) { push @hidden_cols, $hidden_cols[$#hidden_cols] + 1; } } } } elsif ( $element eq "table:table" ) { # get name of current table $table = $attributes{'table:name'}; # Reset all the internal value-keepers $row = -1; $col = -1; $text_p = -1; @cell = (); $repeat_cells = 1; $repeat_rows = 1; $row_hidden = 0; $date_value = ''; $time_value = ''; $currency_value = ''; $max_datarow = -1; $max_datacol = -1; $col_count = -1; @hidden_cols = (); } } sub handle_end { my ($expat, $element) = @_; if ( $element eq "text:p" ) { if( ! $expat->within_element('office:annotation') ) { if( ! defined $cell[ $text_p ] ) { $cell[ $text_p ] = undef }; }; } elsif ( $element eq "table:table") { # decrease $max_datacol if hidden columns within range if ( ( ! $options{NoTruncate} ) and ( $options{DropHiddenColumns} ) ) { for ( 1..scalar grep { $_ <= $max_datacol } @hidden_cols ) { $max_datacol--; } } # truncate table to $max_datarow and $max_datacol if ( ! $options{NoTruncate} ) { $#{$workbook{$table}} = $max_datarow; foreach ( @{$workbook{$table}} ) { $#{$_} = $max_datacol; } } # set up alternative data structure if ( $options{OrderBySheet} ) { push @worksheets, ( { label => $table, data => \@{$workbook{$table}}, } ); } # reset table, column, and row values to default for next table $row = -1; $max_datarow = -1; $max_datacol = -1; $table = ""; $col_count = -1; @hidden_cols = (); } elsif ( $element eq "table:table-row" ) { # drop hidden columns from current row if ( $options{DropHiddenColumns} ) { foreach ( reverse @hidden_cols ) { splice @{$workbook{$table}[$row]}, $_, 1; } } # drop current row, if hidden if ( ( $options{DropHiddenRows} ) and ( $row_hidden == 1 ) ) { pop @{$workbook{$table}}; $row--; } # repeat current row, if necessary else { for (2..$repeat_rows) { $row++; $workbook{$table}[$row] = $workbook{$table}[$row - 1] # copy reference, not data } # set max_datarow, if row not empty if ( grep { defined $_ } @{$workbook{$table}[$row]} ) { $max_datarow = $row; } } # reset row and col values to default for next row $repeat_rows = 1; $col = -1; } elsif ( ( $element eq "table:table-cell" ) or ( $element eq "table:covered-table-cell" ) ) { # assign currency, date or time value to current workbook cell if requested if ( ( $options{StandardCurrency} ) and ( length( $currency_value ) ) ) { $workbook{$table}[$row][$col] = $currency_value; $currency_value = ''; } elsif ( ( $options{StandardDate} ) and ( $date_value ) ) { $workbook{$table}[$row][$col] = $date_value; $date_value = ''; } elsif ( ( $options{StandardTime} ) and ( $time_value ) ) { $workbook{$table}[$row][$col] = $time_value; $time_value = ''; } # join cell contents and assign to current workbook cell else { $workbook{$table}[$row][$col] = @cell ? join $options{ReplaceNewlineWith} || "", map { defined($_) ? $_ : '' } @cell : undef; } # repeat current cell, if necessary for (2..$repeat_cells) { $col++; $workbook{$table}[$row][$col] = $workbook{$table}[$row][$col - 1]; } # reset cell and paragraph values to default for next cell @cell = (); $repeat_cells = 1; $text_p = -1; } } sub char_start { my ($expat, $content) = @_; # don't include paragraph if part of an annotation if ( $expat->within_element('office:annotation') ) { return; } # don't include covered cells, if not requested if ( ( $expat->within_element('table:covered-table-cell') ) and ( ! $options{IncludeCoveredCells} ) ) { return; } # add paragraph or textspan to current @cell array if ( $table ) { $cell[$text_p] .= $content; # set $max_datarow and $max_datacol to current values $max_datarow = $row; if ( $col > $max_datacol ) { $max_datacol = $col } } } 1; __END__ =head1 NAME Spreadsheet::ReadSXC - Extract OpenOffice 1.x spreadsheet data =head1 SYNOPSIS use Spreadsheet::ReadSXC qw(read_sxc); my $workbook_ref = read_sxc("/path/to/file.sxc"); # Alternatively, unpack the .sxc file yourself and pass content.xml use Spreadsheet::ReadSXC qw(read_xml_file); my $workbook_ref = read_xml_file("/path/to/content.xml"); # Alternatively, pass the XML string directly use Spreadsheet::ReadSXC qw(read_xml_string); use Archive::Zip; my $zip = Archive::Zip->new("/path/to/file.sxc"); my $content = $zip->contents('content.xml'); my $workbook_ref = read_xml_string($content); # Control the output through a hash of options (below are the defaults): my %options = ( ReplaceNewlineWith => "", IncludeCoveredCells => 0, DropHiddenRows => 0, DropHiddenColumns => 0, NoTruncate => 0, StandardCurrency => 0, StandardDate => 0, StandardTime => 0, OrderBySheet => 0, StrictErrors => 0, ); my $workbook_ref = read_sxc("/path/to/file.sxc", \%options ); # Iterate over every worksheet, row, and cell: use Encode 'decode'; foreach ( sort keys %$workbook_ref ) { print "Worksheet ", $_, " contains ", $#{$$workbook_ref{$_}} + 1, " row(s):\n"; foreach ( @{$$workbook_ref{$_}} ) { foreach ( map { defined $_ ? $_ : '' } @{$_} ) { my $str = decode('UTF-8', $_); print " '$str'"; } print "\n"; } } # Cell D2 of worksheet "Sheet1" $cell = $$workbook_ref{"Sheet1"}[1][3]; # Row 1 of worksheet "Sheet1": @row = @{$$workbook_ref{"Sheet1"}[0]}; # Worksheet "Sheet1": @sheet = @{$$workbook_ref{"Sheet1"}}; =head1 DESCRIPTION Spreadsheet::ReadSXC extracts data from OpenOffice 1.x spreadsheet files (.sxc). It exports the function read_sxc() which takes a filename and an optional reference to a hash of options as arguments and returns a reference to a hash of references to two-dimensional arrays. The hash keys correspond to the names of worksheets in the OpenOffice workbook. The two-dimensional arrays correspond to rows and cells in the respective spreadsheets. If you don't like this because the order of sheets is not preserved in a hash, read on. The 'OrderBySheet' option provides an array of hashes instead. If you prefer to unpack the .sxc file yourself, you can use the function read_xml_file() instead and pass the path to content.xml as an argument. Or you can extract the XML string from content.xml and pass the string to the function read_xml_string(). Both functions also take a reference to a hash of options as an optional second argument. Spreadsheet::ReadSXC requires XML::Parser to parse the XML contained in .sxc files. Only the contents of text:p elements are returned, not the actual values of table:value attributes. For example, a cell might have a table:value-type attribute of "currency", a table:value attribute of "-1500.99" and a table:currency attribute of "USD". The text:p element would contain "-$1,500.99". This is the string which is returned by the read_sxc() function, not the value of -1500.99. Spreadsheet::ReadSXC was written with data import into an SQL database in mind. Therefore empty spreadsheet cells correspond to undef values in array rows. The example code above shows how to replace undef values with empty strings. If the .sxc file contains an empty spreadsheet its hash element will point to an empty array (unless you use the 'NoTruncate' option in which case it will point to an array of an array containing one undefined element). OpenOffice uses UTF-8 encoding. It depends on your environment how the data returned by the XML Parser is best handled: use Unicode::String qw(latin1 utf8); $unicode_string = utf8($$workbook_ref{"Sheet1"}[0][0])->as_string; # this will not work for characters outside ISO-8859-1: $latin1_string = utf8($$workbook_ref{"Sheet1"}[0][0])->latin1; Of course there are other modules than Unicode::String on CPAN that handle conversion between encodings. It's your choice. Table rows in .sxc files may have a "table:number-rows-repeated" attribute, which is often used for consecutive empty rows. When you format whole rows and/or columns in OpenOffice, it sets the numbers of rows in a worksheet to 32,000 and the number of columns to 256, even if only a few lower-numbered rows and cells actually contain data. Spreadsheet::ReadSXC truncates such sheets so that there are no empty rows after the last row containing data and no empty columns after the last column containing data (unless you use the 'NoTruncate' option). Still it is perfectly legal for an .sxc file to apply the "table:number-rows-repeated" attribute to rows that actually contain data (although I have only been able to produce such files manually, not through OpenOffice itself). To save on memory usage in these cases, Spreadsheet::ReadSXC does not copy rows by value, but by reference (remember that multi-dimensional arrays in Perl are really arrays of references to arrays). Therefore, if you change a value in one row, it is possible that you find the corresponding value in the next row changed, too: $$workbook_ref{"Sheet1"}[0][0] = 'new string'; print $$workbook_ref{"Sheet1"}[1][0]; As of version 0.20 the references returned by read_sxc() et al. remain valid after subsequent calls to the same function. In earlier versions, calling read_sxc() with a different file as the argument would change the data referenced by the original return value, so you had to derefence it before making another call. Thanks to H. Merijn Brand for fixing this. =head1 OPTIONS =over 4 =item StrictErrors Turn on error reporting by using C. Otherwise, functions silently return C when errors are encountered. =item ReplaceNewlineWith By default, newlines within cells are ignored and all lines in a cell are concatenated to a single string which does not contain a newline. To keep the newline characters, use the following key/value pair in your hash of options: ReplaceNewlineWith => "\n" However, you may replace newlines with any string you like. =item IncludeCoveredCells By default, the content of cells that are covered by other cells is ignored because you wouldn't see it in OpenOffice unless you unmerge the merged cells. To include covered cells in the data structure which is returned by parse_sxc(), use the following key/value pair in your hash of options: IncludeCoveredCells => 1 =item DropHiddenRows By default, hidden rows are included in the data structure returned by parse_sxc(). To drop those rows, use the following key/value pair in your hash of options: DropHiddenRows => 1 =item DropHiddenColumns By default, hidden columns are included in the data structure returned by parse_sxc(). To drop those rows, use the following key/value pair in your hash of options: DropHiddenColumns => 1 =item NoTruncate By default, the two-dimensional arrays that contain the data within each worksheet are truncated to get rid of empty rows below the last row containing data and empty columns beyond the last column containing data. If you prefer to keep those rows and columns, use the following key/value pair in your hash of options: NoTruncate => 1 =item StandardCurrency By default, cells are returned as formatted. If you prefer to obtain the value as contained in the table:value attribute, use the following key/value pair in your hash of options: StandardCurrency => 1 =item StandardDate By default, date cells are returned as formatted. If you prefer to obtain the date value as contained in the table:date-value attribute, use the following key/value pair in your hash of options: StandardDate => 1 =item StandardTime By default, time cells are returned as formatted. If you prefer to obtain the time value as contained in the table:time-value attribute, use the following key/value pair in your hash of options: StandardTime => 1 These options are a first step on the way to a different approach at reading data from .sxc files. There should be more options to read in values instead of the strings OpenOffice displays. It should give more flexibility in working with the data obtained from OpenOffice spreadsheets. 'float' and 'percentage' values could be next. 'currency' is less obvious, though, as we need to consider both its value and the 'table:currency' attribute. Formulas and array formulas are yet another issue. I probably won't deal with this until I've given this module an object-oriented interface. =item OrderBySheet The disadvantage of storing worksheets by name in a hash is that the order of sheets is lost. If you prefer not to obtain such a hash, but an array of worksheets insted, use the following key/value pair in your hash of options: OrderBySheet => 1 Thus the read_sxc function will return an array of hashes, each of which will have two keys, "label" and "data". The value of "label" is the name of the sheet. The value of data is a reference to a two-dimensional array containing rows and columns of the worksheet: my $worksheets_ref = read_sxc("/path/to/file.sxc"); my $name_of_first_sheet = $$worksheets_ref[0]{label}; my $first_cell_of_first_sheet = $$worksheets_ref[0]{data}[0][0]; =back =head1 FUNCTIONS =head2 read_sxc my $workbook_ref = read_sxc("/path/to/file.sxc"); Reads an SXC or ODS file given a filename and returns the worksheets as a data structure. =head2 read_sxc_fh open my $fh = 'example.ods'; my $sheet = read_sxc_fh( $fh ); Reads an SXC or ODS file given a filehandle and returns the worksheets as a data structure. =head2 read_xml_file my $workbook_ref = read_xml_file("/path/to/content.xml"); Reads an XML file from a SXC or ODS file returns the worksheets as a data structure. =head2 read_xml_string Parses an XML string and eturns the worksheets as a data structure. =head1 Reading an SXC file from an URL use HTTP::Tiny; use Spreadsheet::Read; # Fetch data and return a filehandle to that data sub fetch_url { my( $url ) = @_; my $ua = HTTP::Tiny->new; my $res = $ua->get( $url ); open my $fh, '<', \$res->{content}; return $fh } my $fh = fetch_url('http://example.com/example.ods'); my $sheet = read_sxc_fh( $fh ); =head1 SEE ALSO L has extensive documentation of the OpenOffice 1.x XML file format (soon to be replaced by the OASIS file format (ODS), see L). =head1 AUTHOR Christoph Terhechte, Eterhechte@cpan.orgE =head1 MAINTAINER Max Maischein, L =head1 COPYRIGHT AND LICENSE Copyright 2005-2019 by Christoph Terhechte Copyright 2019- by Max Maischein This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Spreadsheet-ReadSXC-0.24/META.json0000644000175000017500000000306213547152475016140 0ustar corioncorion{ "abstract" : "Extract OpenOffice 1.x spreadsheet data", "author" : [ "Max Maischein " ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150005", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Spreadsheet-ReadSXC", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Archive::Zip" : "1.34", "Carp" : "0", "PerlIO::gzip" : "0", "XML::Parser" : "0", "perl" : "5.008" } }, "test" : { "requires" : { "Data::Dumper" : "0", "File::Basename" : "0", "File::Temp" : "0", "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "license" : [ "https://dev.perl.org/licenses/" ], "repository" : { "type" : "git", "url" : "git://github.com/Corion/Spreadsheet-ReadSXC.git", "web" : "https://github.com/Corion/Spreadsheet-ReadSXC" } }, "version" : "0.24", "x_serialization_backend" : "JSON::PP version 2.27203", "x_static_install" : 1 } Spreadsheet-ReadSXC-0.24/xt/0000755000175000017500000000000013547152475015151 5ustar corioncorionSpreadsheet-ReadSXC-0.24/xt/99-todo.t0000755000175000017500000000174513547152473016552 0ustar corioncorionuse Test::More; use File::Spec; use File::Find; use strict; # Check that all files do not contain any # lines with "XXX" - such markers should # either have been converted into Todo-stuff # or have been resolved. # The test was provided by Andy Lester. my @files; my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib, 'bin')); plan tests => 2* @files; foreach my $file (@files) { source_file_ok($file); } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } sub source_file_ok { my $file = shift; open( my $fh, "<$file" ) or die "Can't open $file: $!"; my @lines = <$fh>; close $fh; my $n = 0; for ( @lines ) { ++$n; s/^/$file ($n): /; } my @x = grep /XXX/, @lines; if ( !is( scalar @x, 0, "Looking for XXXes in $file" ) ) { diag( $_ ) for @x; } @x = grep /<<<|>>>/, @lines; if ( !is( scalar @x, 0, "Looking for <<<<|>>>> in $file" ) ) { diag( $_ ) for @x; } } Spreadsheet-ReadSXC-0.24/xt/99-changes.t0000755000175000017500000000133713547152473017212 0ustar corioncorion#!perl -w use warnings; use strict; use File::Find; use Test::More tests => 2; =head1 PURPOSE This test ensures that the Changes file mentions the current version and that a release date is mentioned as well =cut require './Makefile.PL'; # Loaded from Makefile.PL our %module = get_module_info(); my $module = $module{NAME}; (my $file = $module) =~ s!::!/!g; require "$file.pm"; my $version = sprintf '%0.2f', $module->VERSION; my $changes = do { local $/; open my $fh, 'Changes' or die $!; <$fh> }; ok $changes =~ /^(.*$version.*)$/m, "We find version $version for $module"; my $changes_line = $1; ok $changes_line =~ /$version\s+20\d\d-[01]\d-[0123]\d\b/, "We find a release date on the same line" or diag $changes_line; Spreadsheet-ReadSXC-0.24/xt/99-manifest.t0000755000175000017500000000203113547152473017400 0ustar corioncorionuse strict; use Test::More; # Check that MANIFEST and MANIFEST.skip are sane : use File::Find; use File::Spec; my @files = qw( MANIFEST MANIFEST.SKIP ); plan tests => scalar @files * 4 +1 # MANIFEST existence check +1 # MYMETA.* non-existence check ; for my $file (@files) { ok(-f $file, "$file exists"); open F, "<$file" or die "Couldn't open $file : $!"; my @lines = ; is_deeply([grep(/^$/, @lines)],[], "No empty lines in $file"); is_deeply([grep(/^\s+$/, @lines)],[], "No whitespace-only lines in $file"); is_deeply([grep(/^\s*\S\s+$/, @lines)],[],"No trailing whitespace on lines in $file"); if ($file eq 'MANIFEST') { chomp @lines; is_deeply([grep { s/\s.*//; ! -f } @lines], [], "All files in $file exist") or do { diag "$_ is mentioned in $file but doesn't exist on disk" for grep { ! -f } @lines }; # Exclude some files from shipping is_deeply([grep(/^MYMETA\.(yml|json)$/, @lines)],[],"We don't try to ship MYMETA.* $file"); }; close F; }; Spreadsheet-ReadSXC-0.24/xt/99-unix-text.t0000755000175000017500000000147713547152473017554 0ustar corioncorionuse Test::More; # Check that all released module files are in # UNIX text format use File::Spec; use File::Find; use strict; my @files = ('Makefile.PL', 'MANIFEST', 'MANIFEST.SKIP', glob 't/*.t'); my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib, 'bin')); plan tests => scalar @files; foreach my $file (@files) { unix_file_ok($file); } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } sub unix_file_ok { my ($filename) = @_; local $/; open F, "< $filename" or die "Couldn't open '$filename' : $!\n"; binmode F; my $content = ; my $i; my @lines = grep { /\x0D\x0A$/sm } map { sprintf "%s: %s\x0A", $i++, $_ } split /\x0A/, $content; unless (is(scalar @lines, 0,"'$filename' contains no windows newlines")) { diag $_ for @lines; }; close F; }; Spreadsheet-ReadSXC-0.24/xt/99-test-prerequisites.t0000755000175000017500000000660013547152473021461 0ustar corioncorion#!perl -w use warnings; use strict; use Test::More; use Data::Dumper; use File::Find; =head1 DESCRIPTION This test checks whether all tests still pass when the optional test prerequisites for the test are not present. This is done by using L to rerun the test while excluding the optional prerequisite. =cut BEGIN { eval { require CPAN::Meta::Prereqs; require Parse::CPAN::Meta; require Perl::PrereqScanner::Lite; require Module::CoreList; require Test::Without::Module; require Capture::Tiny; Capture::Tiny->import('capture'); require Path::Class; Path::Class->import('dir'); }; if (my $err = $@) { warn "# $err"; plan skip_all => "Prerequisite needed for testing is missing"; exit 0; }; }; my @tests; if( @ARGV ) { @tests = @ARGV; } else { open my $manifest, '<', 'MANIFEST' or die "Couldn't read MANIFEST: $!"; @tests = grep { -f $_ } grep { m!^(t/.*\.t|scripts/.*\.pl)$! } map { s!\s*$!!; $_ } <$manifest> } plan tests => 0+@tests; my $meta = Parse::CPAN::Meta->load_file('META.json'); # Find what META.* declares my $explicit_test_prereqs = CPAN::Meta::Prereqs->new( $meta->{prereqs} )->merged_requirements->as_string_hash; my $minimum_perl = $meta->{prereqs}->{runtime}->{requires}->{perl} || 5.006; sub distributed_packages { my @modules; for( @_ ) { dir($_)->recurse( callback => sub { my( $child ) = @_; if( !$child->is_dir and $child =~ /\.pm$/) { push @modules, ((scalar $child->slurp()) =~ m/^\s*package\s+(?:#.*?\n\s+)*(\w+(?:::\w+)*)\b/msg); } }); }; map { $_ => $_ } @modules; } # Find what we distribute: my %distribution = distributed_packages('blib','t'); my $scanner = Perl::PrereqScanner::Lite->new; for my $test_file (@tests) { my $implicit_test_prereqs = $scanner->scan_file($test_file)->as_string_hash; my %missing = %{ $implicit_test_prereqs }; #warn Dumper \%missing; for my $p ( keys %missing ) { # remove core modules if( Module::CoreList::is_core( $p, undef, $minimum_perl)) { delete $missing{ $p }; #diag "$p is core for $minimum_perl"; } else { #diag "$p is not in core for $minimum_perl"; }; # remove explicit (test) prerequisites for my $k (keys %$explicit_test_prereqs) { delete $missing{ $k }; }; #warn Dumper $explicit_test_prereqs->as_string_hash; # Remove stuff from our distribution for my $k (keys %distribution) { delete $missing{ $k }; }; } # If we have no apparent missing prerequisites, we're good my @missing = sort keys %missing; # Rerun the test without these modules and see whether it crashes my @failed; for my $candidate (@missing) { diag "Checking that $candidate is not essential"; my @cmd = ($^X, "-MTest::Without::Module=$candidate", "-Mblib", '-w', $test_file); my $cmd = join " ", @cmd; my ($stdout, $stderr, $exit) = capture { system( @cmd ); }; if( $exit != 0 ) { push @failed, [ $candidate, [@cmd]]; } elsif( $? != 0 ) { push @failed, [ $candidate, [@cmd]]; }; }; is 0+@failed, 0, $test_file or diag Dumper \@failed; }; done_testing; Spreadsheet-ReadSXC-0.24/xt/99-pod.t0000755000175000017500000000123213547152473016356 0ustar corioncorionuse Test::More; # Check our Pod # The test was provided by Andy Lester, # who stole it from Brian D. Foy # Thanks to both ! use File::Spec; use File::Find; use strict; eval { require Test::Pod; Test::Pod->import; }; my @files; if ($@) { plan skip_all => "Test::Pod required for testing POD"; } elsif ($Test::Pod::VERSION < 0.95) { plan skip_all => "Test::Pod 0.95 required for testing POD"; } else { my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib, 'bin')); plan tests => scalar @files; foreach my $file (@files) { pod_file_ok($file); } } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/; } Spreadsheet-ReadSXC-0.24/xt/99-compile.t0000755000175000017500000000143713547152473017233 0ustar corioncorion#!perl use warnings; use strict; use File::Find; use Test::More; BEGIN { eval 'use Capture::Tiny ":all"; 1'; if ($@) { plan skip_all => "Capture::Tiny needed for testing"; exit 0; }; }; plan 'no_plan'; my $last_version = undef; sub check { return if (! m{(\.pm|\.pl) \z}xmsi); my ($stdout, $stderr, $exit) = capture(sub { system( $^X, '-Mblib', '-c', $_ ); }); s!\s*\z!! for ($stdout, $stderr); if( $exit ) { diag $stderr; diag "Exit code: ", $exit; fail($_); } elsif( $stderr ne "$_ syntax OK") { diag $stderr; fail($_); } else { pass($_); }; } find({wanted => \&check, no_chdir => 1}, grep { -d $_ } 'blib', 'scripts', 'examples', 'bin', 'lib' ); Spreadsheet-ReadSXC-0.24/xt/99-synopsis.t0000755000175000017500000000257113547152473017472 0ustar corioncorionuse strict; use Test::More; use File::Spec; use File::Find; use File::Temp 'tempfile'; my @files; my $blib = File::Spec->catfile(qw(blib lib)); find(\&wanted, grep { -d } ($blib, 'bin')); plan tests => scalar @files; foreach my $file (@files) { synopsis_file_ok($file); } sub wanted { push @files, $File::Find::name if /\.p(l|m|od)$/ and $_ !~ /\bDSL\.pm$/; # we skip that one as it initializes immediately } sub synopsis_file_ok { my( $file ) = @_; my $name = "SYNOPSIS in $file compiles"; open my $fh, '<', $file or die "Couldn't read '$file': $!"; my @synopsis = map { s!^\s\s!!; $_ } # outdent all code for here-docs grep { /^\s\s/ } # extract all verbatim (=code) stuff grep { /^=head1\s+SYNOPSIS$/.../^=/ } # extract Pod synopsis <$fh>; if( @synopsis ) { my($tmpfh,$tempname) = tempfile(); print {$tmpfh} join '', @synopsis; close $tmpfh; # flush it my $output = `$^X -Ilib -c $tempname 2>&1`; if( $output =~ /\ssyntax OK$/ ) { pass $name; } else { fail $name; diag $output; diag $_ for @synopsis; }; unlink $tempname or warn "Couldn't clean up $tempname: $!"; } else { SKIP: { skip "$file has no SYNOPSIS section", 1; }; }; } Spreadsheet-ReadSXC-0.24/xt/99-versions.t0000755000175000017500000000233713547152473017453 0ustar corioncorion#!perl -w # Stolen from ChrisDolan on use.perl.org # http://use.perl.org/comments.pl?sid=29264&cid=44309 use warnings; use strict; use File::Find; use Test::More; BEGIN { eval 'use File::Slurp; 1'; if ($@) { plan skip_all => "File::Slurp needed for testing"; exit 0; }; }; plan 'no_plan'; my $last_version = undef; sub check { return if (! m{blib/script/}xms && ! m{\.pm \z}xms); my $content = read_file($_); # only look at perl scripts, not sh scripts return if (m{blib/script/}xms && $content !~ m/\A \#![^\r\n]+?perl/xms); my @version_lines = $content =~ m/ ( [^\n]* \$VERSION \s* = [^=] [^\n]* ) /gxms; if (@version_lines == 0) { fail($_); } for my $line (@version_lines) { $line =~ s/^\s+//; $line =~ s/\s+$//; if (!defined $last_version) { $last_version = shift @version_lines; diag "Checking for $last_version"; pass($_); } else { is($line, $last_version, $_); } } } find({wanted => \&check, no_chdir => 1}, 'blib'); if (! defined $last_version) { fail('Failed to find any files with $VERSION'); } Spreadsheet-ReadSXC-0.24/xt/99-minimumversion.t0000755000175000017500000000047113547152473020661 0ustar corioncorion#!perl -w use strict; use Test::More; eval { #require Test::MinimumVersion::Fast; require Test::MinimumVersion; Test::MinimumVersion->import; }; my @files; if ($@) { plan skip_all => "Test::MinimumVersion required for testing minimum Perl version"; } else { all_minimum_version_from_metajson_ok(); } Spreadsheet-ReadSXC-0.24/xt/meta-lint.t0000755000175000017500000000216513547152473017235 0ustar corioncorion#!perl -w # Stolen from ChrisDolan on use.perl.org # http://use.perl.org/comments.pl?sid=29264&cid=44309 use warnings; use strict; use File::Find; use Test::More; eval { #require Test::MinimumVersion::Fast; require Parse::CPAN::Meta; Parse::CPAN::Meta->import(); require CPAN::Meta::Validator; CPAN::Meta::Validator->import(2.15); }; if ($@) { plan skip_all => "CPAN::Meta::Validator version 2.15 required for testing META files"; } else { plan tests => 4; } use lib '.'; use vars '%module'; require 'Makefile.PL'; # Loaded from Makefile.PL %module = get_module_info(); my $module = $module{NAME}; (my $file = $module) =~ s!::!/!g; require "$file.pm"; my $version = sprintf '%0.2f', $module->VERSION; for my $meta_file ('META.yml', 'META.json') { my $meta = Parse::CPAN::Meta->load_file($meta_file); my $cmv = CPAN::Meta::Validator->new( $meta ); if(! ok $cmv->is_valid, "$meta_file is valid" ) { diag $_ for $cmv->errors; }; # Also check that the declared version matches the version in META.* is $meta->{version}, $version, "$meta_file version matches module version ($version)"; }; Spreadsheet-ReadSXC-0.24/Changes0000644000175000017500000000536713547152473016022 0ustar corioncorionRevision history for Perl extension Spreadsheet::ReadSXC. 0.24 2019-10-08 - Fix parsing the text of cells that only contain "0" These were returned as undef, instead of 0 unless the StandardCurrency option was used. - Fix parsing multiple sheets. If the first cell of the new table was empty, the last defined value of the old table was used instead, if the real (instead of formatted) values were used. 0.23 2019-10-03 - Add a test and fix for files with text in annotations - these caused a crash 0.22 2019-10-03 - Lower Perl version requirement back to 5.8 There is no current reason to require more. - API Change: Formatting or read errors in the compressed input or the XML are now fatal errors. Before, they returned the half-parsed data structure. - read_sxc() and read_xml_file can now also return a fatal error when the file is not found or not readable instead of returning undef. Pass a true value to the the StrictError option. - Bugfix/behaviour change: Cells with leading/trailing newlines now return those leading/trailing newlines in their values 0.21 2019-10-02 - Add 'StandardCurrency' option to get at unformatted number values - Support unformatted values for ODS files - New maintainer Max Maischein (corion@corion.net) 0.20 2005-06-17 (Fri Jun 17 2005) - almost completely rewritten to use XML::Parser's event style (instead of its tree style), resulting in more readable and (hopefully) maintainable code - this version allows for multiple open files by returning an anonymous data structure instead of a data reference (thanks to H. Merijn Brand for fixing this) - new option to return an array of hashes instead of a hash of arrays - new option to return time cells as time value 0.12 2005-05-08 (Sun May 8 2005) - comments no longer appear in cells (have to find a cleaner way of returning annotations) 0.11 2005-05-08 (Sun May 8 2005) - eliminated a bug with 'DropHiddenColumns' which would populate an empty table - edited documentation 0.10 2005-05-06 (Fri May 6 2005) - almost completely rewritten to use XML::Parser instead of XML::Parser::Lite::Tree - new options to include/drop hidden rows/col, covered cells etc. - optionally truncate empty rows and columns - optionally replace newlines in cells with any string - new subroutines for reading unpacked XML data from SXC files - option to return date cells as standard date value 0.03 2005-05-03 (Tue May 3 2005) - more prerequisites in Makefile.PL - minor corrections in documentation 0.02 2005-05-01 (Mon May 1 2005) - fixed prerequisites in Makefile.PL 0.01 2005-04-30 (Sat Apr 30 2005) - original version; created by h2xs 1.22 with options -XAn Spreadsheet::ReadSXC Spreadsheet-ReadSXC-0.24/README0000644000175000017500000001143513547152473015400 0ustar corioncorionSpreadsheet::ReadSXC - Extract OpenOffice 1.x spreadsheet data DESCRIPTION Spreadsheet::ReadSXC extracts data from OpenOffice 1.x spreadsheet files (.sxc). It exports the function read_sxc() which takes a filename and an optional reference to a hash of options as arguments and returns a reference to a hash of references to two-dimensional arrays. The hash keys correspond to the names of worksheets in the OpenOffice workbook. The two-dimensional arrays correspond to rows and cells in the respective spreadsheets. If you don't like this because the order of sheets is not preserved in a hash, read on. The 'OrderBySheet' option provides an array of hashes instead. If you prefer to unpack the .sxc file yourself, you can use the function read_xml_file() instead and pass the path to content.xml as an argument. Or you can extract the XML string from content.xml and pass the string to the function read_xml_string(). Both functions also take a reference to a hash of options as an optional second argument. Spreadsheet::ReadSXC requires XML::Parser to parse the XML contained in .sxc files. Only the contents of text:p elements are returned, not the actual values of table:value attributes. For example, a cell might have a table:value-type attribute of "currency", a table:value attribute of "-1500.99" and a table:currency attribute of "USD". The text:p element would contain "-$1,500.99". This is the string which is returned by the read_sxc() function, not the value of -1500.99. Spreadsheet::ReadSXC was written with data import into an SQL database in mind. Therefore empty spreadsheet cells correspond to undef values in array rows. The example code above shows how to replace undef values with empty strings. If the .sxc file contains an empty spreadsheet its hash element will point to an empty array (unless you use the 'NoTruncate' option in which case it will point to an array of an array containing one undefined element). OpenOffice uses UTF-8 encoding. It depends on your environment how the data returned by the XML Parser is best handled: use Unicode::String qw(latin1 utf8); $unicode_string = utf8($$workbook_ref{"Sheet1"}[0][0])->as_string; # this will not work for characters outside ISO-8859-1: $latin1_string = utf8($$workbook_ref{"Sheet1"}[0][0])->latin1; Of course there are other modules than Unicode::String on CPAN that handle conversion between encodings. It's your choice. Table rows in .sxc files may have a "table:number-rows-repeated" attribute, which is often used for consecutive empty rows. When you format whole rows and/or columns in OpenOffice, it sets the numbers of rows in a worksheet to 32,000 and the number of columns to 256, even if only a few lower-numbered rows and cells actually contain data. Spreadsheet::ReadSXC truncates such sheets so that there are no empty rows after the last row containing data and no empty columns after the last column containing data (unless you use the 'NoTruncate' option). Still it is perfectly legal for an .sxc file to apply the "table:number-rows-repeated" attribute to rows that actually contain data (although I have only been able to produce such files manually, not through OpenOffice itself). To save on memory usage in these cases, Spreadsheet::ReadSXC does not copy rows by value, but by reference (remember that multi-dimensional arrays in Perl are really arrays of references to arrays). Therefore, if you change a value in one row, it is possible that you find the corresponding value in the next row changed, too: $$workbook_ref{"Sheet1"}[0][0] = 'new string'; print $$workbook_ref{"Sheet1"}[1][0]; As of version 0.20 the references returned by read_sxc() et al. remain valid after subsequent calls to the same function. In earlier versions, calling read_sxc() with a different file as the argument would change the data referenced by the original return value, so you had to derefence it before making another call. Thanks to H. Merijn Brand for fixing this. INSTALLATION This is a Perl module distribution. It should be installed with whichever tool you use to manage your installation of Perl, e.g. any of cpanm . cpan . cpanp -i . Consult https://www.cpan.org/modules/INSTALL.html for further instruction. Should you wish to install this module manually, the procedure is perl Makefile.PL make make test make install SEE ALSO L has extensive documentation of the OpenOffice 1.x XML file format (soon to be replaced by the OASIS file format (ODS), see L). AUTHOR Christoph Terhechte, Eterhechte@cpan.orgE COPYRIGHT AND LICENSE Copyright 2005-2019 by Christoph Terhechte Copyright 2019- by Max Maischein This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Spreadsheet-ReadSXC-0.24/MANIFEST0000644000175000017500000000110013547152473015635 0ustar corioncorionChanges lib/Spreadsheet/ReadSXC.pm LICENSE Makefile.PL MANIFEST MANIFEST.SKIP META.json META.yml Module meta-data (added by MakeMaker) README t/00-load.t t/02-read-fh.t t/02-read-xml-file.t t/03-error-handling.t t/04-text-outside-xml.t t/1.t t/NewAPI.ods t/regression-newapi.t t/regression-stockchart.t t/StockChart.ods t/t-date.ods t/t-multiline.ods t/t.sxc t/text-outside-cell.xml xt/99-changes.t xt/99-compile.t xt/99-manifest.t xt/99-minimumversion.t xt/99-pod.t xt/99-synopsis.t xt/99-test-prerequisites.t xt/99-todo.t xt/99-unix-text.t xt/99-versions.t xt/meta-lint.t Spreadsheet-ReadSXC-0.24/LICENSE0000755000175000017500000002127513547152473015533 0ustar corioncorion The Artistic License 2.0 Copyright (c) 2000-2006, The Perl Foundation. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software. You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement. Definitions "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package. "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures. "You" and "your" means any person who would like to copy, distribute, or modify the Package. "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version. "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization. "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees. "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder. "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder. "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future. "Source" form means the source code, documentation source, and configuration files for the Package. "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form. Permission for Use and Modification Without Distribution (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version. Permissions for Redistribution of the Standard Version (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package. (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License. Distribution of Modified Versions of the Package as Source (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following: (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version. (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version. (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under (i) the Original License or (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed. Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source (5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license. (6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version. Aggregating or Linking the Package (7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation. (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package. Items That are Not Considered Part of a Modified Version (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license. General Provisions (10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. (11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. (12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. (13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.